Zum Inhalt springen
Projekt besprechen

2. Oktober 2026

Zwei grüne Deploys – und live läuft die alte Version

DevOpsAWSCI/CD

Das Fehlerbild

Ein konkreter Fall aus GitHub Actions: Zwei Merges lagen etwa zehn Sekunden auseinander. Beide Actions-Runs waren grün. Danach lief in Produktion trotzdem der ältere Commit.

Das wirkt zunächst widersprüchlich. Ein erfolgreicher Deploy sagt aber nur, dass der jeweilige Workflow technisch erfolgreich durchgelaufen ist. Er garantiert nicht, dass genau dieser Stand am Ende aktiv bleibt.

Die Kombination aus parallelen Pipelines und einem veränderbaren Docker latest Tag erzeugt eine klassische Deploy Race Condition.

Wie das Race entsteht

Angenommen, Commit A wird gemergt und startet Pipeline A. Kurz darauf startet Commit B Pipeline B.

Beide Pipelines bauen ein Container-Image und pushen es unter denselben Tag, etwa latest. Dieser Tag ist keine feste Referenz auf einen bestimmten Build. Er zeigt immer auf das Image, das zuletzt unter diesem Namen gepusht wurde.

Ein typischer Ablauf kann so aussehen:

ZeitpunktPipeline APipeline B
StartBuild für Commit A startet
Kurz danachBuild für Commit B startet
PushImage A wird als latest gepusht
PushImage B wird als latest gepusht
DeployPipeline B deployt latest
DeployPipeline A deployt ebenfalls latest

Je nach Timing kann dabei mehr als ein Problem entstehen:

  • Der Tag latest zeigt beim Deploy auf ein anderes Image als beim vorherigen Schritt.
  • Ein älterer Workflow beendet seinen Deploy nach einem neueren Workflow.
  • Ein Deploy lädt einen Tag, dessen Inhalt inzwischen überschrieben wurde.

Welcher Build zuletzt pusht und welcher Workflow zuletzt deployt, entscheidet dann nicht die Commit-Reihenfolge, sondern das Timing. Beide Läufe können grün sein, obwohl das Ergebnis falsch ist.

Mein wichtigster Punkt: Ein grüner Pipeline-Status ist kein Beweis für eine korrekte Reihenfolge in Produktion.

Fix 1: Deploys pro Umgebung serialisieren

Für GitHub Actions concurrency sollte es pro Zielumgebung genau eine Deploy-Ausführung geben. Wenn ein neuer Commit kommt, soll der ältere, noch laufende Deploy nicht weiterarbeiten.

Ein mögliches Muster im Workflow:

concurrency:
  group: production-deploy
  cancel-in-progress: true

Damit erhält die Umgebung eine gemeinsame Sperre. Startet ein neuer Workflow für dieselbe Gruppe, wird ein laufender älterer Lauf abgebrochen.

Die Gruppen sollten zur Umgebung passen. Beispielsweise braucht Staging eine andere Gruppe als Produktion:

concurrency:
  group: deploy-production
  cancel-in-progress: true

Das ist keine allgemeine Build-Sperre. Builds dürfen häufig parallel laufen. Entscheidend ist, dass der Abschnitt mit Wirkung auf dieselbe Umgebung nicht konkurriert.

Fix 2: Unveränderliche Tags deployen

Serialisierung verhindert viele Timing-Probleme. Sie ersetzt aber keine eindeutige Artefakt-Referenz.

Statt latest sollte der Deploy einen unveränderlichen Tag verwenden, beispielsweise den Commit-SHA. Dann referenziert jeder Deploy exakt das Image, das zu diesem Commit gebaut wurde.

- name: Build image
  run: docker build -t app:${{ github.sha }} .
 
- name: Push image
  run: docker push app:${{ github.sha }}
 
- name: Deploy
  run: deploy --image app:${{ github.sha }}

Der konkrete Deploy-Befehl hängt von der Zielplattform ab. Das Prinzip bleibt gleich: Build und Deploy verwenden dieselbe unveränderliche Kennung.

Ein zusätzlicher beweglicher Tag kann für lokale Entwicklung oder manuelle Tests sinnvoll sein. Er sollte aber nicht die alleinige Referenz für produktive Deploys sein.

Übertragung auf ECR und ECS

Dieses Muster ist nicht auf ein bestimmtes Zielsystem beschränkt. Bei Container-Deploys auf AWS gilt es ebenso: Ein Image in ECR kann unter einem Tag referenziert werden, und ein AWS ECS Deployment kann diesen Tag als Image-Referenz verwenden.

Wenn zwei Pipelines denselben veränderbaren Tag verwenden und parallel arbeiten, bleibt das Grundproblem bestehen. ECR und ECS ändern nichts daran, dass ein Tag veränderbar ist und dass die zeitliche Reihenfolge konkurrierender Deploys relevant bleibt.

Für ein belastbares AWS ECS Deployment gilt daher dieselbe Regel:

  1. Image mit einer unveränderlichen Kennung bauen, etwa dem Commit-SHA.
  2. Genau diese Kennung nach ECR pushen.
  3. Genau diese Image-Referenz im ECS-Deploy verwenden.
  4. Deploys pro Umgebung mit GitHub Actions concurrency serialisieren.

So wird aus einem schwer nachvollziehbaren Timing-Problem ein überprüfbarer Zustand: Aus dem laufenden Image-Tag lässt sich direkt ableiten, welcher Commit ausgerollt wurde.

Welchen Tag deployt eure Pipeline – und können zwei Deploys gleichzeitig laufen?