Dies ist Teil 2 der Serie KI-Agenten als Pipeline-Engineers. Im ersten Teil ging es um Pipeline Generation — wie ein Agent aus einem Prompt heraus eine lauffähige Pipeline baut. Am Ende stand ein fertiges Programm, das lokal sauber durchläuft. Dieser Artikel zeigt den Schritt dazwischen, bevor die Pipeline im dritten Teil operiert wird: Deployment — vom lokalen Programm zum nächtlich laufenden CronJob im Cluster.
Auch dieser Schritt ist agentengetrieben. Claude schreibt nicht nur den DAG, sondern auch das Dockerfile, baut das Image, pinnt es im Helm-Chart und löst den Rollout aus — über einen Pull Request, nicht über kubectl apply von Hand.
Die Grundlage bleibt unsere DAG-Engine Reveal — und das Prinzip lässt sich auf andere Pipeline-Frameworks übertragen, solange Code, Container und Cluster sauber zusammenhängen.
Vom Programm zum Image
Eine Reveal-Pipeline ist ein ganz normales .NET-Programm mit einer Main, die den DAG aufruft (top.RunSemDag(...) aus Teil 1). Damit es im Cluster läuft, braucht es ein Container-Image. Das Dockerfile ist ein multi-stage Build: eine fette SDK-Stage kompiliert, eine schlanke Runtime-Stage läuft am Ende — das Produktions-Image trägt keinen Compiler mit sich herum.
# Build-Stage: .NET-9-SDK, restore + publish
FROM mcr.microsoft.com/dotnet/sdk:9.0 AS build
WORKDIR /src
COPY . .
WORKDIR /src/dags/SalesEtlMain
# NuGet-Login zu GitHub Packages über ein BuildKit-Secret —
# das Token landet NICHT in einem Image-Layer.
RUN --mount=type=secret,id=GITHUB_NUGET_TOKEN \
export GITHUB_TOKEN="$(cat /run/secrets/GITHUB_NUGET_TOKEN)" && \
dotnet nuget add source "https://nuget.pkg.github.com/yourorg/index.json" \
--name github --username yourorg --password "$GITHUB_TOKEN" \
--store-password-in-clear-text && \
dotnet publish ./SalesEtlMain.csproj -c Release -o /app/publish
# Runtime-Stage: schlankes Runtime-Image, non-root, nur das Publish-Ergebnis
FROM mcr.microsoft.com/dotnet/runtime:9.0 AS final
USER app
WORKDIR /app
LABEL org.opencontainers.image.source="https://github.com/yourorg/pipelines"
ARG IMAGE_TAG=unknown
ENV CONTAINER_IMAGE_TAG=${IMAGE_TAG}
COPY --from=build /app/publish .
ENTRYPOINT ["dotnet", "SalesEtlMain.dll"]
Zwei Details, die kein Zufall sind:
- Secrets gehören ins BuildKit-Secret, nicht in den Layer. Der NuGet-Token kommt über
--mount=type=secretrein und ist im fertigen Image nicht mehr vorhanden. Ein Token imRUN-Befehl wäre für jeden, der das Image zieht, auslesbar. - Das Image kennt seinen eigenen Tag.
ARG IMAGE_TAG→ENV CONTAINER_IMAGE_TAGzieht den Build-Tag in den Container. Zusammen mit dem OCI-Labelimage.sourceweiß ein laufender Pod jederzeit, aus welchem Commit er gebaut wurde — wichtig für den Audit-Trail in Teil 3.
Gebaut und nach ghcr.io (GitHub Container Registry) gepusht wird in CI — ein GitHub-Actions-Workflow, den der Agent ebenfalls anlegt oder anpasst.
Vom Image zum Cluster: GitOps
Jetzt kommt der entscheidende Architektur-Schritt. Das Image wird nicht von Hand in den Cluster geschoben. Stattdessen beschreibt ein Helm-Chart den gewünschten Zustand, und ArgoCD sorgt dafür, dass der Cluster diesem Zustand folgt — GitOps: Der Git-Stand ist die Wahrheit, der Cluster zieht nach.
Im Chart-values.yaml steht, welches Image in welchem Takt laufen soll:
cronjob:
schedule: "30 23 * * *" # jede Nacht um 23:30
image:
repository: ghcr.io/yourorg/sales-etl
digest: sha256:c080058f59ec0b77… # per Digest gepinnt, nicht per :latest
pipeline: "SALES"
Das digest-Pinning statt :latest ist Absicht: Jeder Run läuft gegen exakt das Image, das im Git-Commit referenziert ist. Kein „latest hat sich heute Nacht heimlich geändert” — der Deploy ist reproduzierbar und nachvollziehbar, genau wie der Pipeline-Run selbst.
Die Verbindung Git → Cluster macht eine ArgoCD-Application:
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
name: sales-pipeline
spec:
source:
repoURL: git@github.com:yourorg/pipeline-charts.git
path: pipelines/sales
targetRevision: HEAD
helm:
valueFiles: [values.yaml]
destination:
namespace: sales
server: https://kubernetes.default.svc
syncPolicy:
automated:
prune: true # entfernte Ressourcen werden auch im Cluster entfernt
selfHeal: true # manuelle Drift im Cluster wird zurückgesetzt
syncOptions:
- CreateNamespace=true
automated + selfHeal bedeutet: Sobald ein Commit im Chart-Repo landet, gleicht ArgoCD den Cluster an — ohne dass jemand kubectl apply tippt. Und weicht der Cluster vom Git-Stand ab, zieht ArgoCD ihn zurück. Der Cluster kann gar nicht dauerhaft anders aussehen als das, was im Git steht.
Der nächtliche Run
Deployt wird kein Dauer-Service, sondern ein CronJob — die Pipeline läuft, verarbeitet die neuen Daten, schreibt Dashboards und beendet sich. Das Helm-Template rendert aus den values den Job:
containers:
- name: sales-etl
# Digest aus values.yaml — reproduzierbar gepinnt
image: "{{ .Values.cronjob.image.repository }}@{{ .Values.cronjob.image.digest }}"
env:
- name: PIPELINE
value: "{{ .Values.cronjob.pipeline }}"
- name: PIPELINEROOT
value: /pipelineroot # genau der Pfad aus PipelineContext.Create(...)
volumeMounts:
- name: pipeline-volume
mountPath: /pipelineroot # persistente Daten + Dashboards überleben den Run
Hier schließt sich der Kreis zu Teil 1: Die Env-Variable PIPELINEROOT ist exakt der Pfad, den PipelineContext.Create("/pipelineroot") im Code erwartet. Das gemountete Volume hält Eingangsdaten, Delta-Buckets und die generierten Dashboards — sie überleben das Ende des Pods und sind die Grundlage für das Monitoring in Teil 3.
Was der Agent hier wirklich macht
Deployment klingt nach DevOps-Handarbeit — ist es aber nicht mehr, wenn die Artefakte zusammenhängen. Und was den Agenten überhaupt in diese Kette bringt, ist derselbe Bootstrapper wie im Betrieb:
Auch hier ist der Skills-Knoten der Bootstrapper. Dieselben wiederverwendbaren Agent-Skills, die in der Operation den laufenden Betrieb aktivieren, bootstrappen auch das Deployment: das Wissen um das multi-stage-Dockerfile-Muster, die Digest-Pinning-Konvention, den GitOps-PR-Flow. Nicht ein Infra-Artefakt macht den Rollout agentengetrieben — sondern die Skills, die der Agent mitbringt.
Damit übernimmt Claude die ganze Kette:
- Dockerfile schreiben — multi-stage, non-root, Secrets sauber über BuildKit. Bei einer neuen Pipeline kopiert er das Muster einer bestehenden und passt nur Projektpfad und Entrypoint an.
- Image bauen lassen — den CI-Workflow anstoßen bzw. anpassen; das Image landet mit Digest in
ghcr.io. - Digest pinnen — den neuen
sha256-Digest invalues.yamleintragen. - PR öffnen — Änderung am Chart-Repo als Pull Request. Mensch reviewt, merged.
Den Rest macht ArgoCD von allein: Merge → Sync → der CronJob läuft ab dem nächsten Schedule mit dem neuen Image. Kein kubectl, kein manuelles Rollout.
Rollback ist ein git revert. Weil der Cluster-Zustand vollständig im Git steht — Image-Digest, Schedule, Env — bedeutet „zurück auf gestern” einfach: den Commit zurückdrehen. ArgoCD synct den alten Digest zurück. Kein Stress-Deployment um Mitternacht.
Das ist der Grund, warum Deployment in diesem Setup kein eigenes Team braucht: Die Infrastruktur ist deklarativ und im selben Git wie der Code — also genau das, worin ein Agent navigieren kann. Was für die Pipeline-Generierung gilt (lesbarer Code, klare Konventionen), gilt hier für die Infrastruktur (Helm-Werte, ArgoCD-Manifeste).
Im dritten Teil zeige ich, was beim Operating der laufenden Pipeline möglich ist — Monitoring, Debug, Extend, Run, alles agentengetrieben. Und falls ihr Build und Betrieb nicht selbst stemmen wollt: Unser Agent Ops Team übernimmt den agentengetriebenen Betrieb eurer Pipelines als Managed Service.
Weiter zu Pipeline Operation →
Reveal kennenlernen →