# Kubernetes Deployment Deploy FlaskPaste to a Kubernetes cluster using images from Harbor registry. --- ## Quick Deploy (k1s) Single-node k1s cluster deployment: ```bash # Restart deployment to pull latest image ssh k1s "kubectl -n flaskpaste rollout restart deployment/flaskpaste" # Watch rollout progress ssh k1s "kubectl -n flaskpaste rollout status deployment/flaskpaste" # Verify deployment ssh k1s "kubectl -n flaskpaste get pods" ssh k1s "curl -s http://\$(kubectl -n flaskpaste get pod -o jsonpath='{.items[0].status.podIP}'):5000/health" ``` | Property | Value | |----------|-------| | Host | k1s (192.168.122.241) | | Namespace | flaskpaste | | Image | harbor.mymx.me/library/flaskpaste:latest | | Pull Policy | Always | --- ## Architecture ``` ┌─────────────────────────────────────────────────────────────────────────────┐ │ Traffic Flow │ ├─────────────────────────────────────────────────────────────────────────────┤ │ │ │ Internet → odin (public IP) → WireGuard → mymx HAProxy │ │ │ │ │ ↓ │ │ K3s NodePort :30500 │ │ │ │ │ ┌──────────┼──────────┐ │ │ ↓ ↓ ↓ │ │ k3s-master k3s-worker-1 k3s-worker-2 │ │ │ │ │ │ │ └──────────┼──────────┘ │ │ ↓ │ │ flaskpaste pod │ │ │ └─────────────────────────────────────────────────────────────────────────────┘ ``` --- ## Prerequisites - Kubernetes cluster with kubectl access - Harbor registry with flaskpaste image (see [harbor-registry.md](harbor-registry.md)) - `local-path` StorageClass (or equivalent) - Network access from HAProxy to K8s nodes --- ## Deployment ### 1. Create Namespace and Secrets ```bash # Create namespace kubectl create namespace flaskpaste # Create Harbor pull secret kubectl create secret docker-registry harbor-creds \ --namespace flaskpaste \ --docker-server=192.168.122.154:30443 \ --docker-username= \ --docker-password= \ --docker-email= ``` ### 2. Apply Manifest ```bash kubectl apply -f kubernetes.yaml ``` ### 3. Verify Deployment ```bash # Check pod status kubectl get pods -n flaskpaste # Check service kubectl get svc -n flaskpaste # View logs kubectl logs -n flaskpaste -l app=flaskpaste # Test API kubectl run test --rm -it --image=alpine --restart=Never -- \ wget -qO- http://flaskpaste.flaskpaste.svc:80/ ``` --- ## Manifest Components ### Namespace ```yaml apiVersion: v1 kind: Namespace metadata: name: flaskpaste ``` ### ConfigMap Environment configuration: ```yaml apiVersion: v1 kind: ConfigMap metadata: name: flaskpaste-config namespace: flaskpaste data: FLASK_ENV: "production" FLASKPASTE_URL_PREFIX: "/paste" FLASKPASTE_EXPIRY_ANON: "432000" FLASKPASTE_MAX_ANON: "3145728" FLASKPASTE_MAX_AUTH: "52428800" ``` ### PersistentVolumeClaim ```yaml apiVersion: v1 kind: PersistentVolumeClaim metadata: name: flaskpaste-data namespace: flaskpaste spec: storageClassName: local-path accessModes: - ReadWriteOnce resources: requests: storage: 1Gi ``` ### Deployment ```yaml apiVersion: apps/v1 kind: Deployment metadata: name: flaskpaste namespace: flaskpaste spec: replicas: 1 selector: matchLabels: app: flaskpaste template: metadata: labels: app: flaskpaste spec: securityContext: runAsUser: 999 runAsGroup: 999 fsGroup: 999 imagePullSecrets: - name: harbor-creds containers: - name: flaskpaste image: harbor.mymx.me/library/flaskpaste:latest ports: - containerPort: 5000 envFrom: - configMapRef: name: flaskpaste-config volumeMounts: - name: data mountPath: /app/data resources: limits: memory: "256Mi" cpu: "1000m" requests: memory: "64Mi" cpu: "250m" livenessProbe: httpGet: path: /health port: 5000 initialDelaySeconds: 10 periodSeconds: 30 readinessProbe: httpGet: path: /health port: 5000 initialDelaySeconds: 5 periodSeconds: 10 volumes: - name: data persistentVolumeClaim: claimName: flaskpaste-data ``` ### Service (NodePort) ```yaml apiVersion: v1 kind: Service metadata: name: flaskpaste namespace: flaskpaste spec: selector: app: flaskpaste ports: - protocol: TCP port: 80 targetPort: 5000 nodePort: 30500 type: NodePort ``` --- ## HAProxy Configuration Route `/paste` path to K8s NodePort with failover: ```haproxy frontend https bind *:443 ssl crt /etc/haproxy/certs/ acl is_paste path_beg /paste use_backend backend-flaskpaste if is_paste { req.hdr(Host) -i mymx.me } backend backend-flaskpaste mode http timeout connect 5000 timeout server 30000 # Strip /paste prefix before forwarding http-request replace-path /paste(.*) \1 http-request set-path / if { path -m len 0 } # Request tracing http-request set-header X-Request-ID %[uuid()] unless { req.hdr(X-Request-ID) -m found } http-request set-var(txn.request_id) req.hdr(X-Request-ID) http-response set-header X-Request-ID %[var(txn.request_id)] # Proxy trust secret http-request set-header X-Proxy-Secret # K3s NodePort backends with failover server k3s-master 192.168.122.186:30500 check server k3s-worker-1 192.168.122.97:30500 check backup server k3s-worker-2 192.168.122.221:30500 check backup ``` ### Verify HAProxy Backend Status ```bash echo "show stat" | nc -U /run/haproxy/admin.sock | grep flaskpaste ``` --- ## containerd Registry Configuration For self-signed Harbor certificates, configure containerd on all K8s nodes: ```toml # /etc/containerd/config.toml [plugins."io.containerd.grpc.v1.cri".registry] [plugins."io.containerd.grpc.v1.cri".registry.configs] [plugins."io.containerd.grpc.v1.cri".registry.configs."192.168.122.154:30443"] [plugins."io.containerd.grpc.v1.cri".registry.configs."192.168.122.154:30443".tls] insecure_skip_verify = true ``` Restart containerd after changes: ```bash sudo systemctl restart containerd ``` --- ## Operations ### Scale Deployment ```bash kubectl scale deployment flaskpaste -n flaskpaste --replicas=3 ``` ### Rolling Update ```bash # Update image kubectl set image deployment/flaskpaste \ -n flaskpaste \ flaskpaste=harbor.mymx.me/library/flaskpaste:v2.0.0 # Watch rollout kubectl rollout status deployment/flaskpaste -n flaskpaste # Rollback if needed kubectl rollout undo deployment/flaskpaste -n flaskpaste ``` ### View Events ```bash kubectl get events -n flaskpaste --sort-by='.lastTimestamp' ``` ### Shell Access ```bash kubectl exec -it -n flaskpaste deploy/flaskpaste -- /bin/sh ``` ### Database Backup ```bash # Copy database from pod kubectl cp flaskpaste/$(kubectl get pod -n flaskpaste -l app=flaskpaste -o jsonpath='{.items[0].metadata.name}'):/app/data/pastes.db ./backup-pastes.db ``` --- ## Troubleshooting ### Image Pull Errors ```bash # Check pod events kubectl describe pod -n flaskpaste -l app=flaskpaste | grep -A10 Events # Common issues: # - x509 certificate error → configure containerd insecure registry # - ImagePullBackOff → check harbor-creds secret exists # - ErrImagePull → verify image exists in Harbor ``` ### Pod Not Starting ```bash # Check logs kubectl logs -n flaskpaste -l app=flaskpaste --previous # Check PVC binding kubectl get pvc -n flaskpaste # Check resource constraints kubectl describe pod -n flaskpaste -l app=flaskpaste ``` ### Health Probe Failures ```bash # Test endpoint directly kubectl exec -n flaskpaste deploy/flaskpaste -- wget -qO- http://localhost:5000/ # Check probe configuration kubectl get deployment flaskpaste -n flaskpaste -o yaml | grep -A5 Probe ``` ### HAProxy Connection Issues ```bash # Test from HAProxy host to NodePort curl -s http://192.168.122.186:30500/health # Check HAProxy backend status echo "show stat" | nc -U /run/haproxy/admin.sock | grep -E "flaskpaste.*(UP|DOWN)" # Check HAProxy logs journalctl -u haproxy -f ``` --- ## Resource Summary | Resource | Name | Namespace | |----------|------|-----------| | Namespace | flaskpaste | - | | ConfigMap | flaskpaste-config | flaskpaste | | PVC | flaskpaste-data | flaskpaste | | Deployment | flaskpaste | flaskpaste | | Service | flaskpaste (NodePort 30500) | flaskpaste | | Secret | harbor-creds | flaskpaste | --- ## Related Documentation - [Harbor Registry](harbor-registry.md) - Building and pushing images - [API Reference](api.md) - FlaskPaste API documentation - [Deployment Guide](deployment.md) - Non-K8s deployment options