Files
flaskpaste/documentation/kubernetes-deployment.md
Username 60652e96b4
Some checks failed
CI / Lint & Format (push) Successful in 22s
CI / Security Scan (push) Successful in 20s
CI / Advanced Security Tests (push) Successful in 17s
CI / Memory Leak Check (push) Successful in 19s
CI / Security Tests (push) Successful in 26s
CI / Unit Tests (push) Successful in 33s
CI / Build & Push Image (push) Failing after 2s
CI / Harbor Vulnerability Scan (push) Has been skipped
CI / SBOM Generation (push) Successful in 19s
CI / Fuzz Testing (push) Successful in 26s
containerfile: consolidate to single alpine image
2026-01-21 12:17:47 +01:00

413 lines
10 KiB
Markdown

# 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=<harbor-username> \
--docker-password=<harbor-password> \
--docker-email=<your-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 <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
```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