forked from username/flaskpaste
10 KiB
10 KiB
Kubernetes Deployment
Deploy FlaskPaste to a Kubernetes cluster using images from Harbor registry.
Quick Deploy (k1s)
Single-node k1s cluster deployment:
# 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)
local-pathStorageClass (or equivalent)- Network access from HAProxy to K8s nodes
Deployment
1. Create Namespace and Secrets
# 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=<harbor-username> \
--docker-password=<harbor-password> \
--docker-email=<your-email>
2. Apply Manifest
kubectl apply -f kubernetes.yaml
3. Verify Deployment
# 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
apiVersion: v1
kind: Namespace
metadata:
name: flaskpaste
ConfigMap
Environment configuration:
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
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: flaskpaste-data
namespace: flaskpaste
spec:
storageClassName: local-path
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 1Gi
Deployment
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)
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:
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 <your-secret-here>
# 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
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:
# /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:
sudo systemctl restart containerd
Operations
Scale Deployment
kubectl scale deployment flaskpaste -n flaskpaste --replicas=3
Rolling Update
# 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
kubectl get events -n flaskpaste --sort-by='.lastTimestamp'
Shell Access
kubectl exec -it -n flaskpaste deploy/flaskpaste -- /bin/sh
Database Backup
# 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
# 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
# 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
# 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
# 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 - Building and pushing images
- API Reference - FlaskPaste API documentation
- Deployment Guide - Non-K8s deployment options