Files
myceliumcloud-examples/examples/nginx-nodeport/nginx-nodeport.md

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 hostNetwork access, pods run in isolated network namespace
  • Standard Service Patterns: Uses typical Kubernetes service discovery
  • External Traffic Policy: Local preserves 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

  1. Security First: Always prefer standard pod networking over hostNetwork
  2. Resource Management: Set appropriate requests and limits
  3. Health Checks: Use liveness and readiness probes
  4. Monitoring: Implement proper logging and metrics collection
  5. Updates: Use rolling updates for zero-downtime deployments
  6. 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 pods shows nginx-nodeport pod in "Running" status
  • kubectl get svc shows 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-nodeport shows nginx access logs

Access URL: http://[NODE-IPV6]:30091 (replace NODE-IPV6 with your node's IPv6 address)