Zum Inhalt springen

Ein Django-SaaS deployen

Hello-World-Deployments verstecken alles, was Produktion interessant macht. Dieser Guide deployt UpCheck – ein echtes, quelloffenes Django-SaaS (ein Uptime-Monitor mit Registrierung, Hintergrund-Checks und öffentlichen Statusseiten) – von Anfang bis Ende: Datenbank, Hintergrund-Worker, geplante Tasks, Migrationen, HTTPS. Es ist dasselbe Setup, mit dem die Live-Instanz auf upcheck.paasbox.com läuft, und die Form lässt sich auf fast jede Django- (oder Rails- oder Laravel-)Anwendung übertragen.

Ein Container-Image (ghcr.io/mitja/upcheck) übernimmt per Command-Override vier Rollen:

RolleWas sie ausführtKubernetes-Form
webgunicorn + WhiteNoiseDeployment + Service
workerCelery-Worker (die eigentlichen Uptime-Checks)Deployment
beatCelery Beat (plant die Checks)Deployment, eine Replica
migratemanage.py migrate + Task-Bootstrapeinmaliger Job, pro Release

Dazu PostgreSQL über den CloudNativePG-Operator und ein Redis mit einer Instanz als Celery-Broker. Die Manifeste liegen im Repo unter deploy/ als kustomize-Base mit Overlays (kind für lokal, paasbox für Produktion).

  • Einen Cluster (Quickstart – ein cpx32-Worker reicht für UpCheck locker), kubectl, helm und eine Domain, die dir gehört.
  • Das Repo: git clone https://github.com/mitja/upcheck && cd upcheck

Drei Add-ons, die ohnehin jeder Produktions-Cluster haben will – der Ingress-und-TLS-Guide erklärt jedes im Detail:

Terminal-Fenster
# CloudNativePG operator
kubectl apply --server-side -f \
https://raw.githubusercontent.com/cloudnative-pg/cloudnative-pg/release-1.30/releases/cnpg-1.30.0.yaml
# ingress-nginx — provisions a Hetzner load balancer (on your Hetzner bill)
helm upgrade --install ingress-nginx ingress-nginx \
--repo https://kubernetes.github.io/ingress-nginx \
--namespace ingress-nginx --create-namespace \
--set controller.service.annotations."load-balancer\.hetzner\.cloud/location"=nbg1
# cert-manager + a Let's Encrypt issuer (edit the email in the file first)
helm upgrade --install cert-manager cert-manager \
--repo https://charts.jetstack.io \
--namespace cert-manager --create-namespace \
--set crds.enabled=true
kubectl apply -f deploy/cluster-issuer.yaml
Terminal-Fenster
kubectl -n ingress-nginx get svc ingress-nginx-controller \
-o jsonpath='{.status.loadBalancer.ingress[0].ip}'

Lege einen A-Eintrag für deinen Host an, der auf diese IP zeigt, und ändere den Hostnamen in deploy/overlays/paasbox/ von upcheck.paasbox.com auf deine eigene Domain.

3. Secrets anlegen (außerhalb des Repos, nie committet)

Abschnitt betitelt „3. Secrets anlegen (außerhalb des Repos, nie committet)“
Terminal-Fenster
kubectl create namespace upcheck
kubectl -n upcheck create secret generic upcheck-secrets \
--from-literal=SECRET_KEY="$(python3 -c 'import secrets; print(secrets.token_urlsafe(50))')"

Das ist das Minimum – UpCheck wickelt seine Abrechnung über Polar ab, und ohne die optionalen POLAR_*-Keys laufen einfach alle Konten im Free-Plan, was für einen Walkthrough genau richtig ist. Beachte, was hier nicht steht: kein Datenbank-Passwort. CNPG erzeugt das Secret pg-app selbst, und die App verwendet dessen uri-Key als DATABASE_URL.

4. Deployen – und Migrationen ehrlich ausführen

Abschnitt betitelt „4. Deployen – und Migrationen ehrlich ausführen“
Terminal-Fenster
kubectl apply -k deploy/overlays/paasbox
# wait for Postgres, then migrate
kubectl -n upcheck wait --for=condition=Ready cluster/pg --timeout=420s
kubectl -n upcheck delete job upcheck-migrate --ignore-not-found
kubectl apply -f deploy/overlays/paasbox/migrate-job.yaml
kubectl -n upcheck wait --for=condition=complete job/upcheck-migrate --timeout=300s

Der Migrate-Job ist bewusst nicht Teil der Kustomization: Kubernetes-Jobs sind unveränderlich, ein „einfach alles anwenden“ kann sie also nicht erneut ausführen. Löschen-dann-anwenden pro Release ist das langweilige, zuverlässige Muster – der Job führt manage.py migrate aus und registriert den Beat-Schedule, und die Deployments für web/worker/beat legen los, sobald das Schema existiert.

Terminal-Fenster
kubectl -n upcheck get pods
curl -I https://upcheck.example.com # your domain: valid Let's Encrypt cert + 200

Öffne die Seite, registriere dich, lege einen Monitor für example.com an und sieh zu, wie der Worker ihn nach Zeitplan prüft – ein kompletter SaaS-Kreislauf: Web-Schicht, asynchrone Worker, Scheduler, Datenbank, TLS.

Die CI veröffentlicht bei jedem Commit ghcr.io/mitja/upcheck:latest und :sha-<commit>. Für reproduzierbare Deploys pinnst du newTag in overlays/paasbox/kustomization.yaml (und das Image in dessen migrate-job.yaml) auf einen sha-Tag, dann:

Terminal-Fenster
kubectl apply -k deploy/overlays/paasbox
kubectl -n upcheck delete job upcheck-migrate --ignore-not-found
kubectl apply -f deploy/overlays/paasbox/migrate-job.yaml
kubectl -n upcheck rollout restart deploy/web deploy/worker deploy/beat

Wie du das aus der CI statt vom Laptop ausführst, zeigt der Guide CI/CD mit GitHub Actions – dieselben Befehle, angetrieben von einer kurzlebigen kubeconfig.

Die UpCheck-Manifeste sind eine Vorlage, die man guten Gewissens klauen darf: ein Image mit Command-Overrides pro Rolle, ein CNPG-Cluster mit dem generierten Secret pg-app, Secrets außerhalb des Repos angelegt, ein Migrationsschritt als unveränderlicher Job und ein Ingress mit der Annotation cert-manager.io/cluster-issuer. Tausche Image, Hostname und Umgebungsvariablen aus, und derselbe deploy/-Baum betreibt die meisten Web-Anwendungen.

Zwei ehrliche Fußnoten: Setze Resource-Requests auf jeder Rolle (die Manifeste im Repo tun das – nur so kann der Scheduler einen kleinen Cluster effizient packen), und wenn dein Walkthrough-Cluster nichts zu tun hat, versetze ihn in den Ruhezustand, statt ihn weiter abrechnen zu lassen.

Terminal-Fenster
kubectl delete namespace upcheck
helm -n cert-manager uninstall cert-manager
helm -n ingress-nginx uninstall ingress-nginx # deletes the Hetzner load balancer