Skip to content

Latest commit

 

History

History
64 lines (50 loc) · 2.9 KB

File metadata and controls

64 lines (50 loc) · 2.9 KB

Kubernetes manifests

Not yet applied to a cluster. These manifests are written and reviewed but have never been run — treat them as a design artefact, not a verified deployment. The steps below are what to run to validate them; until someone does, assume there is a typo waiting.

You do not need a paid cluster to demonstrate this — you need manifests that apply cleanly and an explanation of why each choice was made.

k3d cluster create documind -p "8080:80@loadbalancer"

# Postgres with pgvector, for local testing only — production uses managed
# Postgres (Neon), not a pod.
kubectl create deployment postgres --image=pgvector/pgvector:pg16
kubectl set env deployment/postgres \
  POSTGRES_USER=documind POSTGRES_PASSWORD=documind POSTGRES_DB=documind
kubectl expose deployment postgres --port=5432

kubectl apply -f k8s/config.yaml      # edit the Secret first
kubectl apply -f k8s/deployment.yaml
kubectl apply -f k8s/service.yaml
kubectl apply -f k8s/hpa.yaml

kubectl rollout status deployment/documind-api
kubectl port-forward svc/documind-api 8000:80
curl localhost:8000/health

The decisions worth defending

Two probes, two endpoints. /live never touches Postgres; /health does. A database outage should pull pods out of the Service (readiness) without restarting them (liveness). Pointing both probes at a database-checking endpoint is the classic mistake that turns a recoverable dependency blip into a CrashLoopBackOff across every replica at once.

A long startup probe. Loading bge-small and bge-reranker-base takes tens of seconds on a cold node. The startup probe gives 120 seconds before the liveness probe takes over, so a slow boot is not mistaken for a hang.

readOnlyRootFilesystem: true, with explicit emptyDir mounts for /tmp and the Hugging Face cache. The cache is per-pod, so each replica downloads weights once on first start — a shared ReadWriteMany volume would avoid that and is the right change if pod churn is high.

HPA on CPU, scaling up fast and down slowly. CPU is a poor proxy for load here because the service spends most of its time waiting on the OpenAI API; the honest fix is a requests-per-second custom metric. Scale-down is deliberately sluggish because replacing a pod means re-downloading model weights and rebuilding a connection pool.

Migrations are not in this Deployment. alembic upgrade head belongs in a Job or an init container gated on a lock, not in the app's start command — otherwise replicas: 2 means two pods racing the same migration. It is left out rather than done wrong.

Not included, on purpose

  • Ingress / TLS — cluster-specific, and the demo is served from Vercel.
  • NetworkPolicy — worth adding for a real deployment; would be theatre here.
  • A Postgres StatefulSet — managed Postgres is the right answer, and a hand-rolled stateful database in a portfolio cluster teaches the wrong lesson.