- Complete k8s manifests with Kustomize support - Production and staging overlays - ConfigMap/Secret management - Ingress with TLS (Traefik/NGINX) - Persistent storage for SQLite - Comprehensive k8s README with operations guide - Updated main README with k8s deployment instructions - Gitignore for k8s secrets Usage: kubectl apply -k k8s/overlays/production
366 lines
8.8 KiB
Markdown
366 lines
8.8 KiB
Markdown
# Kubernetes Deployment
|
|
|
|
This directory contains Kubernetes manifests for deploying the DWS Dynamic DNS service on K3s, Kubernetes, or any K8s-compatible platform.
|
|
|
|
## Directory Structure
|
|
|
|
```
|
|
k8s/
|
|
├── base/ # Base manifests (don't edit directly)
|
|
│ ├── deployment.yaml # Deployment, Service, PVC
|
|
│ ├── configmap.yaml # Non-sensitive configuration
|
|
│ ├── secrets.yaml # Sensitive configuration (placeholders)
|
|
│ ├── ingress.yaml # Ingress with TLS
|
|
│ └── kustomization.yaml # Base kustomization
|
|
│
|
|
├── overlays/
|
|
│ ├── production/ # Production environment
|
|
│ │ ├── kustomization.yaml # Production-specific settings
|
|
│ │ ├── deployment-patch.yaml # Resource adjustments
|
|
│ │ ├── namespace.yaml # Production namespace
|
|
│ │ ├── secrets.yaml # Production secrets (gitignored)
|
|
│ │ └── secrets.example.yaml # Example secrets template
|
|
│ │
|
|
│ └── staging/ # Staging environment
|
|
│ ├── kustomization.yaml # Staging-specific settings
|
|
│ ├── deployment-patch.yaml # Single replica, lower resources
|
|
│ ├── namespace.yaml # Staging namespace
|
|
│ ├── secrets.yaml # Staging secrets (gitignored)
|
|
│ └── secrets.example.yaml # Example secrets template
|
|
│
|
|
└── README.md # This file
|
|
```
|
|
|
|
## Quick Start
|
|
|
|
### Prerequisites
|
|
|
|
- Kubernetes 1.21+ or K3s cluster
|
|
- kubectl configured with cluster access
|
|
- cert-manager installed (for TLS certificates)
|
|
- Ingress controller (Traefik, NGINX, etc.)
|
|
- Storage class for persistent volumes
|
|
|
|
### Production Deployment
|
|
|
|
```bash
|
|
# 1. Clone and enter directory
|
|
git clone https://git.dws.rip/DWS/dyn.git
|
|
cd dyn/k8s
|
|
|
|
# 2. Create production secrets
|
|
cp overlays/production/secrets.example.yaml overlays/production/secrets.yaml
|
|
|
|
# 3. Edit secrets with your Technitium credentials
|
|
# Replace 'your-production-api-token-here' with actual token
|
|
nano overlays/production/secrets.yaml
|
|
|
|
# 4. Deploy to production
|
|
kubectl apply -k overlays/production
|
|
|
|
# 5. Verify deployment
|
|
kubectl get pods -n dyn-ddns
|
|
kubectl get svc -n dyn-ddns
|
|
kubectl get ingress -n dyn-ddns
|
|
|
|
# 6. Check logs
|
|
kubectl logs -n dyn-ddns -l app.kubernetes.io/name=dyn-ddns -f
|
|
```
|
|
|
|
### Staging Deployment
|
|
|
|
```bash
|
|
# Similar process for staging
|
|
cp overlays/staging/secrets.example.yaml overlays/staging/secrets.yaml
|
|
# Edit with staging credentials
|
|
kubectl apply -k overlays/staging
|
|
```
|
|
|
|
## Configuration
|
|
|
|
### Secrets (Required)
|
|
|
|
Create `overlays/production/secrets.yaml`:
|
|
|
|
```yaml
|
|
apiVersion: v1
|
|
kind: Secret
|
|
metadata:
|
|
name: dyn-ddns-secrets
|
|
type: Opaque
|
|
stringData:
|
|
# Choose ONE authentication method:
|
|
|
|
# Method 1: API Token (recommended)
|
|
TECHNITIUM_TOKEN: "your-actual-api-token-here"
|
|
|
|
# Method 2: Username/Password
|
|
# TECHNITIUM_USERNAME: "admin"
|
|
# TECHNITIUM_PASSWORD: "your-password"
|
|
|
|
# Optional: Trusted proxies
|
|
# TRUSTED_PROXIES: "10.0.0.0/8,172.16.0.0/12"
|
|
```
|
|
|
|
**Important:** Never commit secrets.yaml to git. It's already in .gitignore.
|
|
|
|
### ConfigMap (Optional Overrides)
|
|
|
|
Edit `overlays/production/kustomization.yaml`:
|
|
|
|
```yaml
|
|
configMapGenerator:
|
|
- name: dyn-ddns-config
|
|
behavior: merge
|
|
literals:
|
|
- TECHNITIUM_URL=https://dns.dws.rip
|
|
- BASE_DOMAIN=dws.rip
|
|
- SPACE_SUBDOMAIN=space
|
|
- RATE_LIMIT_PER_IP=10
|
|
- RATE_LIMIT_PER_TOKEN=1
|
|
```
|
|
|
|
### Ingress Customization
|
|
|
|
By default, the ingress is configured for **Traefik** with cert-manager:
|
|
|
|
```yaml
|
|
annotations:
|
|
traefik.ingress.kubernetes.io/router.entrypoints: websecure
|
|
traefik.ingress.kubernetes.io/router.tls: "true"
|
|
cert-manager.io/cluster-issuer: "letsencrypt-prod"
|
|
```
|
|
|
|
For **NGINX** ingress, change annotations in `base/ingress.yaml`:
|
|
|
|
```yaml
|
|
annotations:
|
|
kubernetes.io/ingress.class: nginx
|
|
cert-manager.io/cluster-issuer: "letsencrypt-prod"
|
|
nginx.ingress.kubernetes.io/ssl-redirect: "true"
|
|
```
|
|
|
|
## Architecture
|
|
|
|
### Components
|
|
|
|
1. **Deployment** - Runs the DDNS bridge container
|
|
- Replicas: 2 (production), 1 (staging)
|
|
- Resource limits configurable per overlay
|
|
- Health checks (liveness & readiness probes)
|
|
|
|
2. **Service** - ClusterIP exposing port 80
|
|
|
|
3. **Ingress** - TLS termination at edge
|
|
- Host: dyn.dws.rip (customize as needed)
|
|
- Automatic certificate via cert-manager
|
|
|
|
4. **PersistentVolumeClaim** - SQLite database storage
|
|
- Size: 1Gi (adjustable)
|
|
- AccessMode: ReadWriteOnce
|
|
|
|
### Resource Requirements
|
|
|
|
**Production:**
|
|
- CPU: 200m request / 1000m limit
|
|
- Memory: 128Mi request / 512Mi limit
|
|
- Replicas: 2
|
|
|
|
**Staging:**
|
|
- CPU: 100m request / 500m limit
|
|
- Memory: 64Mi request / 256Mi limit
|
|
- Replicas: 1
|
|
|
|
## Operations
|
|
|
|
### View Logs
|
|
|
|
```bash
|
|
# All pods
|
|
kubectl logs -n dyn-ddns -l app.kubernetes.io/name=dyn-ddns
|
|
|
|
# Specific pod
|
|
kubectl logs -n dyn-ddns -f deployment/prod-dyn-ddns
|
|
|
|
# Previous container logs (after restart)
|
|
kubectl logs -n dyn-ddns --previous deployment/prod-dyn-ddns
|
|
```
|
|
|
|
### Scale Deployment
|
|
|
|
```bash
|
|
# Scale to 3 replicas
|
|
kubectl scale deployment -n dyn-ddns prod-dyn-ddns --replicas=3
|
|
|
|
# Edit deployment directly
|
|
kubectl edit deployment -n dyn-ddns prod-dyn-ddns
|
|
```
|
|
|
|
### Database Backup
|
|
|
|
The SQLite database is stored in the persistent volume at `/data/dyn.db`:
|
|
|
|
```bash
|
|
# Find the pod
|
|
POD=$(kubectl get pod -n dyn-ddns -l app.kubernetes.io/name=dyn-ddns -o jsonpath='{.items[0].metadata.name}')
|
|
|
|
# Copy database locally
|
|
kubectl cp dyn-ddns/$POD:/data/dyn.db ./dyn-backup-$(date +%Y%m%d).db
|
|
|
|
# Or exec into pod
|
|
kubectl exec -it -n dyn-ddns $POD -- sh
|
|
# Then: sqlite3 /data/dyn.db "SELECT * FROM spaces;"
|
|
```
|
|
|
|
### Update Deployment
|
|
|
|
```bash
|
|
# Update to latest image
|
|
kubectl rollout restart deployment -n dyn-ddns prod-dyn-ddns
|
|
|
|
# Or set specific image tag
|
|
kubectl set image deployment -n dyn-ddns prod-dyn-ddns dyn-ddns=git.dws.rip/DWS/dyn:v1.0.0
|
|
|
|
# Monitor rollout
|
|
kubectl rollout status deployment -n dyn-ddns prod-dyn-ddns
|
|
```
|
|
|
|
### Troubleshooting
|
|
|
|
**Pod stuck in Pending:**
|
|
```bash
|
|
kubectl describe pod -n dyn-ddns <pod-name>
|
|
# Check: Storage class available? PV provisioned?
|
|
```
|
|
|
|
**Pod crash looping:**
|
|
```bash
|
|
kubectl logs -n dyn-ddns --previous <pod-name>
|
|
# Check: Secrets configured? Technitium URL reachable?
|
|
```
|
|
|
|
**Ingress not working:**
|
|
```bash
|
|
kubectl describe ingress -n dyn-ddns prod-dyn-ddns
|
|
kubectl get certificate -n dyn-ddns
|
|
# Check: DNS pointing to ingress controller? Cert-manager working?
|
|
```
|
|
|
|
## Advanced Usage
|
|
|
|
### Multi-Environment Setup
|
|
|
|
Deploy to multiple environments:
|
|
|
|
```bash
|
|
# Staging
|
|
kubectl apply -k overlays/staging
|
|
|
|
# Production
|
|
kubectl apply -k overlays/production
|
|
|
|
# Verify both
|
|
kubectl get pods --all-namespaces -l app.kubernetes.io/name=dyn-ddns
|
|
```
|
|
|
|
### Custom Overlay
|
|
|
|
Create your own overlay for specific needs:
|
|
|
|
```bash
|
|
mkdir overlays/custom
|
|
cat > overlays/custom/kustomization.yaml << 'EOF'
|
|
apiVersion: kustomize.config.k8s.io/v1beta1
|
|
kind: Kustomization
|
|
|
|
resources:
|
|
- ../../base
|
|
|
|
namespace: my-namespace
|
|
|
|
configMapGenerator:
|
|
- name: dyn-ddns-config
|
|
behavior: merge
|
|
literals:
|
|
- TECHNITIUM_URL=https://my-dns-server.example.com
|
|
- RATE_LIMIT_PER_IP=5
|
|
EOF
|
|
|
|
kubectl apply -k overlays/custom
|
|
```
|
|
|
|
### Monitoring
|
|
|
|
The deployment exposes standard metrics. Add Prometheus scraping:
|
|
|
|
```yaml
|
|
# Add to deployment-patch.yaml
|
|
metadata:
|
|
annotations:
|
|
prometheus.io/scrape: "true"
|
|
prometheus.io/port: "8080"
|
|
```
|
|
|
|
## Security Considerations
|
|
|
|
1. **Secrets Management**
|
|
- Never commit secrets to git
|
|
- Consider using external secrets operator (Vault, Sealed Secrets)
|
|
- Rotate Technitium API tokens regularly
|
|
|
|
2. **Network Policies**
|
|
```yaml
|
|
# Example network policy
|
|
apiVersion: networking.k8s.io/v1
|
|
kind: NetworkPolicy
|
|
metadata:
|
|
name: dyn-ddns-netpol
|
|
namespace: dyn-ddns
|
|
spec:
|
|
podSelector:
|
|
matchLabels:
|
|
app.kubernetes.io/name: dyn-ddns
|
|
policyTypes:
|
|
- Ingress
|
|
- Egress
|
|
ingress:
|
|
- from:
|
|
- namespaceSelector:
|
|
matchLabels:
|
|
name: ingress-nginx
|
|
ports:
|
|
- protocol: TCP
|
|
port: 8080
|
|
egress:
|
|
- to: [] # Allow all egress (for Technitium API)
|
|
```
|
|
|
|
3. **Pod Security**
|
|
- Container runs as non-root (in Dockerfile)
|
|
- Read-only root filesystem recommended
|
|
- Drop all capabilities
|
|
|
|
## Maintenance
|
|
|
|
### Regular Tasks
|
|
|
|
1. **Backup database weekly**
|
|
2. **Monitor rate limit metrics**
|
|
3. **Review access logs**
|
|
4. **Update base image for security patches**
|
|
|
|
### Version Upgrades
|
|
|
|
1. Update image tag in overlay kustomization
|
|
2. Apply changes: `kubectl apply -k overlays/production`
|
|
3. Verify rollout: `kubectl rollout status ...`
|
|
4. Monitor for errors in logs
|
|
|
|
## Support
|
|
|
|
For issues specific to Kubernetes deployment:
|
|
1. Check pod logs: `kubectl logs ...`
|
|
2. Describe resources: `kubectl describe ...`
|
|
3. Check ingress controller logs
|
|
4. Verify cert-manager is issuing certificates
|