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

22 KiB
Raw Blame History

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.

🔑 Key Concept: NodePort Access Pattern

Important: With NodePort, you access the service via:

  • Worker Node's Mycelium IPv6 address + NodePort 30091
  • Example: http://[worker-node-mycelium-ipv6]:30091/

This is different from hostNetwork where pods get direct Mycelium IPs:

  • hostNetwork: Pod has Mycelium IP → Access: http://[pod-mycelium-ip]:8080
  • NodePort: Pod isolated network → Access: http://[node-mycelium-ip]:30091

The traffic flow is: User → Node:30091 → Service:8080 → Pod:8080

📁 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 (NodePort Pattern)

User with Mycelium
        ↓
http://[worker-node-mycelium-ipv6]:30091
        ↓
Worker Node (kube-proxy forwards)
        ↓
NodePort 30091 → Service port 8080
        ↓
Kubernetes Service (load balances)
        ↓
Pod container port 8080
        ↓
nginx → HTML Content

🚨 Critical: NodePort Access Pattern

With NodePort:

  • Pods run in isolated network (no hostNetwork)
  • Pods do NOT have direct Mycelium IPv6 addresses
  • Access via worker node's Mycelium IPv6 + NodePort
  • Service is accessible on all worker nodes at port 30091
  • kube-proxy forwards traffic: node:30091 → service:8080 → pod:8080

Comparison:

  • hostNetwork (nginx-mycelium): Pod gets host's Mycelium IP → Access: http://[pod-ip]:8080
  • NodePort (nginx-nodeport): Pod isolated → Access: http://[node-ip]:30091

Getting the right IPv6:

# Get worker node Mycelium IPv6 addresses
kubectl get nodes -o wide

# OR use the fetch-ip.sh script
../../scripts/fetch-ip.sh

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  # ← Service type is NodePort
  externalTrafficPolicy: Local  # Preserves IPv6 source IP
  ports:
  - port: 8080
    targetPort: 8080
    nodePort: 30091  # ← Explicitly set to 30091 (avoiding conflict with nginx-mycelium's 30090)
    protocol: TCP

What it does:

  • Exposes nginx on NodePort 30091 on all worker nodes
  • Preserves IPv6 source IP for logging and security
  • Uses standard Kubernetes service patterns (not LoadBalancer)
  • Provides load balancing across pod replicas (when scaled)
  • Access via: http://[worker-node-mycelium-ipv6]:30091/

NodePort Range: Kubernetes NodePorts use range 30000-32767 by default

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

📊 Nginx Deployment Variants Comparison

Complete Overview: 4 Ways to Deploy Nginx on Mycelium Cloud

Feature hostNetwork NodePort LoadBalancer Ingress
Example nginx-mycelium nginx-nodeport (Future) (Future)
Pod Network Host Isolated Isolated Isolated
Security ⚠️ Low Medium Medium High
Access Pattern [pod-ip]:8080 [node-ip]:30091 [lb-ip]:80 domain.com
Port Range Any 30000-32767 80, 443 80, 443
Mycelium Access Direct pod Via node Via LB Via ingress
Use Case Demo/POC Testing/Dev Production Web apps
Scalability Limited Good Excellent Excellent
Load Balancing Manual K8s Service Cloud LB Ingress controller
SSL/TLS Manual Manual Possible Native
DNS Support No No Possible Yes
Production Ready ⚠️ No Yes Yes Yes

Key Differences: hostNetwork vs NodePort

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 per node Multiple replicas
Production Ready ⚠️ Demo/POC only Production patterns
Access URL http://[pod-mycelium-ip]:8080 http://[node-mycelium-ip]:30091
Port Used 8080 (+ NodePort 30090) NodePort 30091

📈 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

🔄 Complete NodePort Flow Explained

Understanding the Full Request Path

┌─────────────────────────────────────────────────────────────────┐
│ Step 1: User with Mycelium Network                             │
│ • Has Mycelium client running                                   │
│ • Can reach Mycelium IPv6 addresses globally                    │
│ • Accesses: http://[worker-node-mycelium-ipv6]:30091          │
└─────────────────────────────────────────────────────────────────┘
                            ↓
┌─────────────────────────────────────────────────────────────────┐
│ Step 2: Mycelium Network Routes to Worker Node                 │
│ • Peer-to-peer IPv6 routing                                     │
│ • No traditional DNS needed                                     │
│ • Direct encrypted connection                                   │
└─────────────────────────────────────────────────────────────────┘
                            ↓
┌─────────────────────────────────────────────────────────────────┐
│ Step 3: Worker Node Receives Request on Port 30091             │
│ • kube-proxy listens on NodePort 30091                          │
│ • Forwards to Service cluster IP                                │
│ • Preserves source IPv6 (externalTrafficPolicy: Local)          │
└─────────────────────────────────────────────────────────────────┘
                            ↓
┌─────────────────────────────────────────────────────────────────┐
│ Step 4: Kubernetes Service Load Balances                       │
│ • Service receives on port 8080                                 │
│ • Selects backend pod (app=nginx-nodeport)                      │
│ • Routes to targetPort 8080                                     │
└─────────────────────────────────────────────────────────────────┘
                            ↓
┌─────────────────────────────────────────────────────────────────┐
│ Step 5: nginx Pod Processes Request                            │
│ • Pod in isolated network namespace                             │
│ • nginx listens on container port 8080                          │
│ • Serves HTML from ConfigMap                                    │
│ • Returns response                                              │
└─────────────────────────────────────────────────────────────────┘
                            ↓
┌─────────────────────────────────────────────────────────────────┐
│ Step 6: Response Returns to User                               │
│ • Same path in reverse                                          │
│ • User sees website in browser                                  │
│ • Access logs show original IPv6 (source IP preserved)          │
└─────────────────────────────────────────────────────────────────┘

Why NodePort is Better for Production

Security Benefits:

  1. Pods cannot access host network interfaces
  2. Network policies can restrict pod-to-pod traffic
  3. Resource limits prevent resource exhaustion
  4. Health checks ensure reliability

Operational Benefits:

  1. Standard Kubernetes debugging tools work
  2. Can scale horizontally (multiple replicas)
  3. Service provides automatic load balancing
  4. Compatible with monitoring stacks (Prometheus, etc.)
  5. Can evolve to LoadBalancer or Ingress later

Mycelium Integration:

  • Worker nodes have Mycelium IPv6 addresses
  • NodePort makes service accessible via these IPs
  • No need for traditional cloud load balancers
  • Perfect for decentralized web hosting

<EFBFBD> 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)