feat: Add nginx-nodeport example with comprehensive documentation and security comparison
This commit is contained in:
445
examples/nginx-nodeport/nginx-nodeport.md
Normal file
445
examples/nginx-nodeport/nginx-nodeport.md
Normal file
@@ -0,0 +1,445 @@
|
||||
# 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)
|
||||
Reference in New Issue
Block a user