14 KiB
Mycelium Cloud - Nginx NodePort Secure Website Hosting
A complete, production-ready example for deploying a secure, globally accessible website on Mycelium Cloud using Kubernetes NodePort services with enhanced network isolation. This demonstrates security-first IPv6 web hosting with standard Kubernetes patterns.
📁 What This Contains
This directory contains everything you need to deploy a secure website with global IPv6 accessibility:
- nginx-nodeport.md - This comprehensive guide
- nginx-nodeport-deployment.yaml - Secure deployment configuration
- nginx-nodeport-service.yaml - NodePort service configuration
- nginx-nodeport-configmaps.yaml - Website content and nginx configuration
- test-nodeport-ipv6.sh - IPv6 testing and verification script
- update-content.sh - Dynamic content update with IPv6 discovery
- compare-approaches.md - Security and architecture comparison
🚀 Quick Start (3 minutes)
# 1. Deploy the ConfigMaps (content and nginx config)
kubectl apply -f nginx-nodeport-configmaps.yaml
# 2. Deploy the nginx application
kubectl apply -f nginx-nodeport-deployment.yaml
# 3. Create the NodePort service
kubectl apply -f nginx-nodeport-service.yaml
# 4. Wait for deployment to be ready
kubectl wait --for=condition=ready pod -l app=nginx-nodeport --timeout=60s
# 5. Get IPv6 address of the ACTUAL node where your pod is running
POD_NODE=$(kubectl get pods -l app=nginx-nodeport -o jsonpath='{.items[0].spec.nodeName}')
NODE_IPV6=$(kubectl get node $POD_NODE -o jsonpath='{range .status.addresses[?(@.type=="InternalIP")]}{.address}{"\n"}{end}' | grep -E '^[0-9a-f]+:[0-9a-f]+:[0-9a-f]+:[0-9a-f]+:[0-9a-f]+:[0-9a-f]+:[0-9a-f]+:[0-9a-f]+$' | head -1)
echo "Your website is accessible at: http://[$NODE_IPV6]:30091"
echo "NOTE: Pod is running on node: $POD_NODE"
# 6. Test the website
curl -6 "http://[$NODE_IPV6]:30091/"
Alternative: Use the automated script for IPv6 discovery:
# Automatically discover IPv6 addresses and update content
./update-content.sh
⚠️ Important: Worker Node vs Master Node Access Unlike hostNetwork (which can be accessed from any node), NodePort services are limited to the nodes where your pods are actually running. Always check:
# Check which node your pod is running on
kubectl get pods -l app=nginx-nodeport -o wide
# Get the IPv6 of the correct WORKER node (not master)
NODE_IPV6=$(kubectl get nodes -l kubernetes.io/role!=master -o jsonpath='{range .items[0].status.addresses[?(@.type=="InternalIP")]}{.address}{"\n"}{end}' | grep -E '^[0-9a-f]+:[0-9a-f]+:[0-9a-f]+:[0-9a-f]+:[0-9a-f]+:[0-9a-f]+:[0-9a-f]+:[0-9a-f]+$' | head -1)
Expected Result: You'll see a professional website with NodePort security branding and IPv6 address detection.
📋 What You'll Learn
- ✅ Security-First IPv6 Web Hosting - Enhanced network isolation
- ✅ Standard Kubernetes Service Patterns - Industry best practices
- ✅ NodePort with External Traffic Policy - Preserves IPv6 source IP
- ✅ ConfigMap-based Content Management - Dynamic updates
- ✅ Production nginx Configuration - Dual-stack (IPv4/IPv6) support
- ✅ Resource Management - CPU/memory limits and health checks
- ✅ Security Comparison - Understanding hostNetwork vs standard networking
🏗️ Architecture
This example uses separate configuration files for better organization and security:
Component Overview
┌─────────────────┐ ┌──────────────────┐ ┌─────────────────┐
│ Mycelium │ │ NodePort │ │ nginx Pod │
│ IPv6 Network │───▶│ Service │───▶│ (Isolated) │
│ :30090 │ │ :8080 │ │ :8080 │
└─────────────────┘ └──────────────────┘ └─────────────────┘
│
▼
┌──────────────────┐
│ ConfigMaps │
│ • HTML Content │
│ • nginx Config │
└──────────────────┘
Network Flow
IPv6 Client → NodePort:30091 → Service:8080 → Pod:8080 → nginx → HTML Content
🚨 Critical: Worker Node vs Master Node Access
Unlike hostNetwork (accessible from any node), NodePort services are ONLY accessible from the specific worker node where your pod is running.
- hostNetwork: Pod has direct host access → accessible from ANY node's IPv6
- NodePort: Pod isolated network → accessible ONLY from pod's host node
Always check pod location first:
# Check where your pod is running
kubectl get pods -l app=nginx-nodeport -o wide
# Look for the NODE column - this is where you can access your service
Key Security Improvements
- Pod Isolation: No
hostNetworkaccess, pods run in isolated network namespace - Standard Service Patterns: Uses typical Kubernetes service discovery
- External Traffic Policy:
Localpreserves IPv6 source IP for logging - Resource Limits: Prevents resource exhaustion attacks
- Health Checks: Liveness and readiness probes for reliability
📄 Configuration Files
1. nginx-nodeport-deployment.yaml
Secure pod deployment without hostNetwork:
spec:
hostNetwork: false # Key security improvement
containers:
- name: nginx
image: nginx:alpine
ports:
- containerPort: 8080 # No hostPort needed
resources:
requests:
memory: "64Mi"
cpu: "100m"
limits:
memory: "128Mi"
cpu: "200m"
livenessProbe:
httpGet:
path: /health
port: 8080
readinessProbe:
httpGet:
path: /health
port: 8080
What it does:
- Creates 1 pod with nginx and custom website content
- Uses standard pod networking (no direct host access)
- Includes resource limits and health checks
- Uses ConfigMaps for dynamic content management
- Dual-stack nginx (IPv4 + IPv6) configuration
2. nginx-nodeport-service.yaml
NodePort service with IPv6 source IP preservation:
spec:
type: NodePort
externalTrafficPolicy: Local # Preserves IPv6 source IP
ports:
- port: 8080
targetPort: 8080
nodePort: 30091 # External port (avoiding conflict with nginx-mycelium)
protocol: TCP
What it does:
- Exposes nginx on NodePort 30091 (external, avoiding conflicts)
- Preserves IPv6 source IP for logging and security
- Uses standard Kubernetes service patterns
- Provides load balancing across pod replicas (when scaled)
3. nginx-nodeport-configmaps.yaml
Content and nginx configuration:
HTML Content: Professional website with NodePort security branding nginx Configuration: Dual-stack (IPv4/IPv6) with health endpoint
server {
listen 8080;
listen [::]:8080 ipv6only=on; # IPv6 support
server_name _;
location / {
root /usr/share/nginx/html;
index index.html;
}
location /health {
access_log off;
return 200 "healthy\n";
}
}
🔍 Verification and Testing
Check pod status
kubectl get pods -l app=nginx-nodeport
kubectl describe pod -l app=nginx-nodeport
Check service configuration
kubectl get svc nginx-nodeport-service -o yaml
kubectl get endpoints nginx-nodeport-service
Test nginx configuration
POD_NAME=$(kubectl get pods -l app=nginx-nodeport -o name | head -1)
kubectl exec $POD_NAME -- nginx -t
kubectl exec $POD_NAME -- nginx -s reload
Test health endpoint
POD_NAME=$(kubectl get pods -l app=nginx-nodeport -o name | head -1)
kubectl exec $POD_NAME -- curl -s http://localhost:8080/health
Test website content
# Get node IPv6 address
NODE_IPV6=$(kubectl get nodes -o jsonpath='{.items[0].status.addresses[?(@.type=="InternalIP")].address}')
# Test from outside the cluster
curl -6 "http://[$NODE_IPV6]:30091/"
curl -6 "http://[$NODE_IPV6]:30091/health"
Verify ConfigMaps are mounted
POD_NAME=$(kubectl get pods -l app=nginx-nodeport -o name | head -1)
kubectl exec $POD_NAME -- ls -la /usr/share/nginx/html/
kubectl exec $POD_NAME -- cat /usr/share/nginx/html/index.html | head -10
🔄 Updating Content
Update website content
# Create new HTML file
cat > new-index.html << 'EOF'
<!DOCTYPE html>
<html>
<head><title>Updated Site</title></head>
<body>
<h1>Content Updated!</h1>
<p>New content deployed at $(date)</p>
</body>
</html>
EOF
# Update ConfigMap
kubectl create configmap nginx-nodeport-content \
--from-file=index.html=new-index.html \
--dry-run=client -o yaml | kubectl apply -f -
# Reload nginx to pick up changes
POD_NAME=$(kubectl get pods -l app=nginx-nodeport -o name | head -1)
kubectl exec $POD_NAME -- nginx -s reload
echo "✅ Content updated! Access at: http://[$NODE_IPV6]:30091"
Update nginx configuration
# Create custom nginx config
cat > custom-nginx.conf << 'EOF'
server {
listen 8080;
listen [::]:8080 ipv6only=on;
server_name my-site.com;
# Custom logging
access_log /var/log/nginx/access.log;
error_log /var/log/nginx/error.log;
location / {
root /usr/share/nginx/html;
index index.html;
try_files $uri $uri/ =404;
}
location /health {
access_log off;
return 200 "healthy\n";
}
}
EOF
# Update ConfigMap
kubectl create configmap nginx-nodeport-nginx-config \
--from-file=default.conf=custom-nginx.conf \
--dry-run=client -o yaml | kubectl apply -f -
# Reload nginx
POD_NAME=$(kubectl get pods -l app=nginx-nodeport -o name | head -1)
kubectl exec $POD_NAME -- nginx -s reload
🔍 Troubleshooting
Pod won't start
# Check pod status and events
kubectl describe pod -l app=nginx-nodeport
# Check pod logs
kubectl logs -f deployment/nginx-nodeport
# Check resource availability
kubectl top nodes
kubectl describe nodes
Service not accessible
# Verify service exists and has endpoints
kubectl get svc nginx-nodeport-service
kubectl get endpoints nginx-nodeport-service
# Check if pod is ready
kubectl get pods -l app=nginx-nodeport -o wide
# Verify NodePort is open
netstat -tulpn | grep 30091
IPv6 connectivity issues
# Test IPv6 on the node
ping6 -c 3 $NODE_IPV6
# Check nginx is listening on IPv6
POD_NAME=$(kubectl get pods -l app=nginx-nodeport -o name | head -1)
kubectl exec $POD_NAME -- netstat -tuln | grep 8080
# Verify nginx configuration
kubectl exec $POD_NAME -- cat /etc/nginx/conf.d/default.conf
Performance issues
# Check resource usage
kubectl top pods -l app=nginx-nodeport
# Check nginx status and connections
POD_NAME=$(kubectl get pods -l app=nginx-nodeport -o name | head -1)
kubectl exec $POD_NAME -- ps aux | grep nginx
kubectl exec $POD_NAME -- netstat -an | grep :8080 | wc -l
🗑️ Cleanup
# Remove all resources
kubectl delete -f nginx-nodeport-deployment.yaml -f nginx-nodeport-service.yaml
# Remove ConfigMaps
kubectl delete configmap nginx-nodeport-content nginx-nodeport-nginx-config
# Verify cleanup
kubectl get all -l app=nginx-nodeport # Should return nothing
📊 Key Differences from hostNetwork Approach
| Feature | hostNetwork (nginx-mycelium) | NodePort (nginx-nodeport) |
|---|---|---|
| Security | ⚠️ Direct host access | ✅ Isolated pod network |
| Network Isolation | ❌ Uses host interfaces | ✅ Pod namespace isolation |
| Port Conflicts | ❌ Limited by host ports | ✅ No port conflicts |
| Debugging | 🔄 Host-level tools | ✅ Standard K8s patterns |
| Monitoring | 🔄 Host monitoring | ✅ Pod-level monitoring |
| Scalability | ❌ Single instance | ✅ Multiple replicas |
| Production Ready | ⚠️ Demo/POC | ✅ Production patterns |
📈 Scaling and High Availability
Scale to multiple replicas
# Scale deployment
kubectl scale deployment nginx-nodeport --replicas=3
# Verify scaling
kubectl get pods -l app=nginx-nodeport
# Check load balancing
NODE_IPV6=$(kubectl get nodes -o jsonpath='{.items[0].status.addresses[?(@.type=="InternalIP")].address}')
for i in {1..5}; do
curl -6 "http://[$NODE_IPV6]:30091/" | grep -o "nginx-nodeport-[a-z0-9]*-[a-z0-9]*" | head -1
done
Add resource limits for better performance
# Update deployment with enhanced resources
resources:
requests:
memory: "128Mi"
cpu: "200m"
limits:
memory: "256Mi"
cpu: "500m"
🎯 Best Practices
- Security First: Always prefer standard pod networking over hostNetwork
- Resource Management: Set appropriate requests and limits
- Health Checks: Use liveness and readiness probes
- Monitoring: Implement proper logging and metrics collection
- Updates: Use rolling updates for zero-downtime deployments
- Configuration: Store configuration in ConfigMaps for flexibility
📚 Learning Outcomes
After completing this example, you understand:
- NodePort Services - Kubernetes service exposure patterns
- Network Security - Pod isolation vs hostNetwork trade-offs
- Production Patterns - Resource management and health checks
- IPv6 Networking - Dual-stack nginx configuration
- ConfigMap Management - Dynamic content and configuration updates
- Service Discovery - Kubernetes service networking
- Load Balancing - Service-level load distribution
🚀 Next Steps
- Multi-Replica Deployment: Scale to 3+ replicas for high availability
- LoadBalancer Service: Move to cloud LoadBalancer for production
- Ingress Controller: Implement advanced routing and SSL termination
- Monitoring: Add Prometheus metrics and Grafana dashboards
- SSL/TLS: Implement HTTPS with Let's Encrypt certificates
Success Criteria: You'll know everything is working when:
- ✅
kubectl get podsshows nginx-nodeport pod in "Running" status - ✅
kubectl get svcshows nginx-nodeport-service with NodePort 30091 - ✅
curl -6 "http://[$NODE_IPV6]:30091"returns your secure website - ✅ Website displays "NODEPORT SECURE" and "ENHANCED SECURITY" badges
- ✅
kubectl logs deployment/nginx-nodeportshows nginx access logs
Access URL: http://[NODE-IPV6]:30091 (replace NODE-IPV6 with your node's IPv6 address)