⎈ Helm Lektion 9/12 ~8 Min. Experte

Helm Hooks

Pre/Post Install/Upgrade/Delete Jobs.

📝 Meine Notizen

Lernziele

Nach dieser Lektion kannst du Helm Hooks einsetzen, um Jobs an Lifecycle-Punkten wie pre-upgrade oder post-install auszuführen — etwa für Datenbank-Migrationen. Du beherrschst die Steuerung über hook-weight und hook-delete-policy, weißt, dass Hook-Ressourcen nicht Teil des Release-Managements sind, und kannst begründen, wann ein Hook die falsche Lösung ist.

Das Problem: „Erst die Migration, dann das Deployment”

Ein Update deiner Anwendung bringt ein neues Datenbankschema mit. Die neue App-Version darf erst starten, wenn die Migration gelaufen ist — und die Migration darf nicht von fünf Replicas gleichzeitig ausgeführt werden. Mit reinem helm upgrade hast du keinen Platz für diesen Schritt: Helm legt Ressourcen an, wartet aber nicht auf fachliche Vorarbeiten.

Hooks schließen diese Lücke: Sie markieren Ressourcen im Chart so, dass Helm sie nicht als Teil des normalen Manifests behandelt, sondern an einem definierten Punkt im Lifecycle ausführt — und bei Jobs und Pods auf deren Abschluss wartet, bevor es weitergeht.

Der Mechanismus: Annotations statt neuer Syntax

Ein Hook ist eine ganz normale Kubernetes-Ressource mit einer speziellen Annotation:

apiVersion: batch/v1
kind: Job
metadata:
  name: "{{ .Release.Name }}-db-migrate"
  annotations:
    "helm.sh/hook": pre-upgrade,pre-install
    "helm.sh/hook-weight": "0"
    "helm.sh/hook-delete-policy": before-hook-creation,hook-succeeded
spec:
  backoffLimit: 1
  template:
    spec:
      restartPolicy: Never
      containers:
        - name: migrate
          image: registry.example.com/myapp-migrations:1.4.0
          command: ["./migrate", "up"]

Die verfügbaren Lifecycle-Punkte: pre-install, post-install, pre-upgrade, post-upgrade, pre-delete, post-delete, pre-rollback, post-rollback und test. Eine Ressource kann mehrere davon tragen — das Beispiel oben läuft vor jedem Upgrade und bei der Erstinstallation, das übliche Muster für Migrationen.

Der Ablauf bei helm upgrade: Helm rendert alles, sortiert Hook-Ressourcen aus, legt zuerst die pre-upgrade-Hooks an und blockiert, bis sie abgeschlossen sind (bei einem Job: bis er erfolgreich durchläuft). Erst dann werden die regulären Manifeste angewendet, danach die post-upgrade-Hooks. Schlägt ein Hook fehl, bricht Helm ab und markiert das Release als failed — die neuen Manifeste wurden dann nie angewendet. Genau dieses Blockieren ist Feature und Risiko zugleich.

Reihenfolge und Aufräumen: hook-weight und hook-delete-policy

Mehrere Hooks am selben Lifecycle-Punkt sortiert hook-weight: aufsteigend, niedrige Gewichte zuerst, negative Werte erlaubt. Der Wert ist eine Annotation und damit ein String"helm.sh/hook-weight": "-5" mit Anführungszeichen, sonst scheitert das Rendern. So stellst du sicher, dass der Backup-Job (Gewicht -5) vor der Migration (Gewicht 0) läuft.

hook-delete-policy regelt, wann Helm die Hook-Ressource wieder löscht:

  • before-hook-creation (Default): vor der nächsten Ausführung wird die alte Instanz gelöscht. Nötig, weil ein abgeschlossener Job nicht erneut gestartet werden kann — ohne diese Policy schlüge das zweite Upgrade fehl, weil der Job-Name schon existiert.
  • hook-succeeded: nach Erfolg sofort löschen — hält den Namespace sauber.
  • hook-failed: nach Fehlschlag löschen — meist nicht gewollt, denn die Logs des gescheiterten Jobs sind dein wichtigstes Diagnosematerial.

Die Kombination before-hook-creation,hook-succeeded ist ein bewährter Standard: Erfolgreiche Jobs verschwinden, gescheiterte bleiben zur Analyse liegen und werden beim nächsten Versuch ersetzt.

Die unterschätzte Eigenschaft: Hooks gehören nicht zum Release

Hook-Ressourcen sind vom Release-Management ausgenommen: helm uninstall löscht sie nicht (nur die delete-Policies tun das), ein helm rollback führt keine Hooks rückwärts aus und stellt sie nicht wieder her, und helm get manifest listet sie nicht auf. Helm erzeugt sie, wartet auf sie — und vergisst sie dann weitgehend.

Daraus folgen zwei Konsequenzen für die Praxis. Erstens: Aufräumen ist deine Verantwortung, über delete-Policies oder von Hand. Zweitens — und im Interview der wichtigere Punkt: Ein pre-upgrade-Migrationsjob läuft beim Rollback nicht rückwärts. Wenn Revision 5 das Schema migriert hat und du auf Revision 4 zurückrollst, läuft die alte App gegen das neue Schema. Hooks lösen das Sequenzierungsproblem, nicht das Kompatibilitätsproblem — abwärtskompatible Migrationen bleiben Pflicht (das knüpft direkt an die Rollback-Grenzen aus Lektion 3 an).

Eine Einordnung gehört dazu: GitOps-Tools behandeln Hooks unterschiedlich. Argo CD etwa bildet Helm-Hooks auf seine eigenen Sync-Phasen ab — das funktioniert für die gängigen Fälle, aber nicht 1:1 für jede Policy-Kombination. Wer Charts für GitOps-Umgebungen baut, sollte Hook-Logik einfach halten.

Praxis: einen Migrations-Hook beobachten

helm create hookdemo
mkdir -p hookdemo/templates
cat > hookdemo/templates/migrate-job.yaml <<'EOF'
apiVersion: batch/v1
kind: Job
metadata:
  name: "{{ .Release.Name }}-migrate"
  annotations:
    "helm.sh/hook": pre-install,pre-upgrade
    "helm.sh/hook-weight": "0"
    "helm.sh/hook-delete-policy": before-hook-creation,hook-succeeded
spec:
  backoffLimit: 1
  template:
    spec:
      restartPolicy: Never
      containers:
        - name: migrate
          image: busybox:1.36
          command: ["sh", "-c", "echo 'migrating...'; sleep 10; echo done"]
EOF

# Installieren — beobachte, dass helm ~10s blockiert
helm install demo ./hookdemo

# In einem zweiten Terminal währenddessen:
kubectl get jobs,pods

# Nach Erfolg ist der Job weg (hook-succeeded):
kubectl get jobs

# Fehlschlag provozieren: command auf "exit 1" ändern, dann
helm upgrade demo ./hookdemo
helm history demo        # Upgrade-Revision steht auf failed
kubectl get jobs         # der gescheiterte Job liegt zur Analyse da
kubectl logs job/demo-migrate

helm uninstall demo
kubectl get jobs   # Achtung: gescheiterte Hook-Jobs überleben das uninstall

Die letzten beiden Befehle zeigen die zwei Eigenschaften, die man am leichtesten vergisst: Der fehlgeschlagene Hook blockiert das Upgrade (Release failed), und seine Ressourcen räumt helm uninstall nicht weg.

Typische Stolperfallen

Hook blockiert das Upgrade bis zum Timeout: Ein hängender Migrations-Job hält helm upgrade fest — in der CI heißt das: Pipeline steht, bis --timeout zuschlägt. Jobs brauchen ein backoffLimit und activeDeadlineSeconds, sonst wartet Helm auf einen Job, der nie fertig wird.

hook-failed in der delete-policy: Räumt genau das Beweismaterial weg, das du zur Fehleranalyse brauchst. Gescheiterte Hooks liegen lassen.

Unveränderlicher Job-Name ohne before-hook-creation: Abgeschlossene Jobs sind immutable; existiert der Job vom letzten Lauf noch, scheitert die Neuanlage. Entweder Default-Policy nutzen oder den Job-Namen pro Revision variieren (z. B. {{ .Release.Revision }} im Namen).

Rollback-Illusion: Hooks laufen beim Rollback nicht „rückwärts”. Wer Schema-Migrationen per Hook deployt, braucht trotzdem abwärtskompatible Migrationen oder einen separaten Down-Migration-Plan.

Interview-Vorbereitung

Die Standardfrage „Was sind Helm Hooks und wofür nutzt man sie?” beantwortest du über den Mechanismus: normale Ressourcen mit helm.sh/hook-Annotation, die Helm an Lifecycle-Punkten (pre-/post-install/upgrade/delete/rollback) ausführt und auf die es bei Jobs wartet; Reihenfolge per hook-weight, Aufräumen per hook-delete-policy. Dann der Use Case Datenbank-Migration als pre-upgrade-Job — und als Differenzierer die Grenze: Hooks sind nicht Teil des Release-Managements und laufen beim Rollback nicht rückwärts.

Follow-ups:

  • „Was passiert, wenn ein pre-upgrade-Hook fehlschlägt?” — Upgrade bricht ab, Release wird failed, die neuen Manifeste wurden nicht angewendet.
  • „Werden Hook-Ressourcen bei helm uninstall gelöscht?” — nein, sie gehören nicht zum Release; nur delete-Policies räumen sie ab.
  • „Wie steuerst du die Reihenfolge mehrerer Hooks?” — hook-weight, aufsteigend sortiert, als String-Annotation.
  • „Warum ist before-hook-creation der Default?” — abgeschlossene Jobs sind immutable; die alte Instanz muss vor dem nächsten Lauf weg.

Zusammenfassung

Hooks geben dem Release-Lifecycle Einschubpunkte: Ressourcen mit helm.sh/hook-Annotation laufen vor oder nach Install, Upgrade, Delete und Rollback; Helm wartet auf ihren Abschluss und bricht bei Fehlern ab. hook-weight ordnet, hook-delete-policy räumt auf — gescheiterte Hooks lässt man zur Analyse liegen. Die wichtigste Grenze: Hooks sind nicht Teil des Release-States, überleben uninstall und machen Rollbacks nicht fachlich rückgängig.

In der nächsten Lektion verlassen wir den Cluster und schauen auf die Verteilseite: Charts als OCI-Artefakte in Container-Registries — der moderne Nachfolger des klassischen Chart-Repositories aus Lektion 4.