445 lines
14 KiB
Markdown
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) |