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

445 lines
14 KiB
Markdown

# 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)
```bash
# 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:**
```bash
# 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:
```bash
# 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:**
```bash
# 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:**
```yaml
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:**
```yaml
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
```nginx
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
```bash
kubectl get pods -l app=nginx-nodeport
kubectl describe pod -l app=nginx-nodeport
```
### Check service configuration
```bash
kubectl get svc nginx-nodeport-service -o yaml
kubectl get endpoints nginx-nodeport-service
```
### Test nginx configuration
```bash
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
```bash
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
```bash
# 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
```bash
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
```bash
# 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
```bash
# 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
```bash
# 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
```bash
# 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
```bash
# 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
```bash
# 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
```bash
# 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
```bash
# 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
```yaml
# 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)