Lab 11: Deployment Jobs und Environments¶
Hintergrund¶
In den bisherigen Labs haben wir Deployments mit normalen Jobs (job:)
simuliert. Das funktioniert, hat aber Nachteile: Azure DevOps weiß nicht, dass
es sich um ein Deployment handelt, es gibt keine Deployment-Historie, und
Mechanismen wie Approval Gates oder Deployment-Strategien stehen nicht zur
Verfügung.
Ein Deployment Job (deployment:) ist ein spezieller Job-Typ, der genau
für diesen Zweck konzipiert ist. Im Gegensatz zu normalen Jobs bietet er:
- Deployment-Strategien: Steuern, wie ein Deployment ausgerollt wird.
runOnceführt das Deployment einmalig aus (der einfachste Fall).rollingundcanaryermöglichen schrittweise Rollouts, bei denen nur ein Teil der Zielinfrastruktur gleichzeitig aktualisiert wird. - Lifecycle-Hooks: Strukturieren den Deployment-Prozess in definierte
Phasen. Statt alle Schritte als flache Liste zu definieren, kannst du sie
nach Funktion gruppieren: Vorbereitungen (
preDeploy), das eigentliche Deployment (deploy), Traffic-Umleitung (routeTraffic), Post-Deployment- Tests (postRouteTraffic) und Abschluss-Aktionen (on:success/on:failure). - Environment-Tracking: Jedes Deployment wird einem Environment zugeordnet. Azure DevOps führt eine Deployment-Historie pro Environment, in der du siehst, welche Version wann von wem deployt wurde und ob das Deployment erfolgreich war.
Environments repräsentieren Zielumgebungen wie Dev, Staging oder Production. Sie existieren als eigene Ressource in Azure DevOps (unter Pipelines > Environments) und bieten neben der Deployment-Historie auch Approval Gates und Checks, die wir in Lab 12 kennenlernen.
Ein weiterer praktischer Vorteil von Deployment Jobs: Artefakte, die in einer
vorherigen Stage publiziert wurden, werden automatisch heruntergeladen -
du brauchst keinen expliziten download-Step. Die Artefakte landen unter
$(Pipeline.Workspace)/<artefakt-name>/.
Aufgabenstellung¶
Schritt 1: Environments in Azure DevOps anlegen¶
Environments können auf zwei Wegen erstellt werden: manuell über die Web-Oberfläche oder automatisch beim ersten Pipeline-Lauf, der ein noch nicht existierendes Environment referenziert. Wir legen sie manuell an, damit wir in Lab 12 direkt Approval Gates konfigurieren können.
- Gehe zu Pipelines > Environments.
- Klicke auf "New environment".
- Erstelle die folgenden drei Environments:
| Name | Beschreibung |
|---|---|
dev |
Entwicklungsumgebung |
staging |
Staging/Test-Umgebung |
production |
Produktionsumgebung |
Für jedes Environment:
- Gib den Namen ein (exakt wie in der Tabelle, da die Pipeline diesen Namen referenziert).
- Resource: Wähle "None". Environments können optional mit Kubernetes-Clustern oder VM-Gruppen verknüpft werden - für unsere simulierten Deployments brauchen wir das nicht.
- Klicke auf "Create".
Nach dem Anlegen siehst du unter Pipelines > Environments alle drei Environments. Jedes hat zunächst eine leere Deployment-Historie, die sich füllt, sobald Deployments durchlaufen.
Schritt 2: Pipeline mit Deployment Jobs¶
Jetzt erstellen wir eine Pipeline mit vier Stages: Build, Deploy Dev, Deploy
Staging und Deploy Production. Die drei Deploy-Stages verwenden Deployment Jobs
mit der runOnce-Strategie. Die Staging-Stage demonstriert zusätzlich alle
verfügbaren Lifecycle-Hooks.
Ersetze den Inhalt von azure-pipelines.yml:
trigger:
branches:
include:
- master
variables:
appVersion: '1.0.$(Build.BuildId)'
stages:
# ===== Build Stage =====
- stage: Build
displayName: 'Build'
jobs:
- job: BuildApp
pool:
vmImage: 'ubuntu-latest'
steps:
- script: |
mkdir -p dist
cp src/*.js dist/
cp index.html dist/
echo '{"version": "$(appVersion)", "buildId": "$(Build.BuildId)"}' > dist/version.json
displayName: 'Build'
- publish: dist
artifact: app-package
displayName: 'Artefakte publizieren'
# ===== Deploy to Dev =====
- stage: DeployDev
displayName: 'Deploy Dev'
dependsOn: Build
jobs:
- deployment: DeployToDev
displayName: 'Deploy to Dev'
pool:
vmImage: 'ubuntu-latest'
environment: 'dev'
strategy:
runOnce:
deploy:
steps:
- script: |
echo "=== Deployment nach Dev ==="
echo "Version: $(appVersion)"
echo "Artefakte:"
ls -la $(Pipeline.Workspace)/app-package/
echo ""
echo "version.json:"
cat $(Pipeline.Workspace)/app-package/version.json
echo ""
echo "Simuliere Deployment nach Dev..."
sleep 2
echo "Dev Deployment erfolgreich!"
displayName: 'Deploy to Dev'
- script: |
echo "=== Smoke Test ==="
echo "Prüfe, ob die App erreichbar ist..."
echo "HTTP 200 OK (simuliert)"
echo "Smoke Test bestanden!"
displayName: 'Smoke Test'
# ===== Deploy to Staging =====
- stage: DeployStaging
displayName: 'Deploy Staging'
dependsOn: DeployDev
jobs:
- deployment: DeployToStaging
displayName: 'Deploy to Staging'
pool:
vmImage: 'ubuntu-latest'
environment: 'staging'
strategy:
runOnce:
preDeploy:
steps:
- script: |
echo "=== Pre-Deploy Check ==="
echo "Prüfe Voraussetzungen für Staging..."
echo "Datenbank-Migration: nicht erforderlich"
echo "Konfiguration: geprüft"
echo "Pre-Deploy Checks bestanden!"
displayName: 'Pre-Deploy Checks'
deploy:
steps:
- script: |
echo "=== Deployment nach Staging ==="
echo "Version: $(appVersion)"
echo "Artefakte:"
ls -la $(Pipeline.Workspace)/app-package/
echo ""
echo "Simuliere Deployment nach Staging..."
sleep 3
echo "Staging Deployment erfolgreich!"
displayName: 'Deploy to Staging'
postRouteTraffic:
steps:
- script: |
echo "=== Integration Tests ==="
echo "Führe Integration Tests gegen Staging aus..."
echo " Test 1: API Health Check - PASS"
echo " Test 2: Login Flow - PASS"
echo " Test 3: Datenbank-Verbindung - PASS"
echo "Alle Integration Tests bestanden!"
displayName: 'Integration Tests'
on:
success:
steps:
- script: |
echo "Staging Deployment erfolgreich abgeschlossen!"
echo "Version $(appVersion) ist auf Staging live."
displayName: 'Erfolgs-Benachrichtigung'
failure:
steps:
- script: |
echo "FEHLER: Staging Deployment fehlgeschlagen!"
echo "Automatischer Rollback wird eingeleitet..."
displayName: 'Fehler-Behandlung'
# ===== Deploy to Production =====
- stage: DeployProduction
displayName: 'Deploy Production'
dependsOn: DeployStaging
condition: and(succeeded(), eq(variables['Build.SourceBranchName'], 'master'))
jobs:
- deployment: DeployToProd
displayName: 'Deploy to Production'
pool:
vmImage: 'ubuntu-latest'
environment: 'production'
strategy:
runOnce:
preDeploy:
steps:
- script: |
echo "=== Production Pre-Deploy ==="
echo "Version: $(appVersion)"
echo "Letzte Prüfungen vor Production..."
echo "Alle Checks bestanden."
displayName: 'Final Checks'
deploy:
steps:
- script: |
echo "=== Production Deployment ==="
echo "Deploye Version $(appVersion) nach Production..."
sleep 5
echo "Production Deployment erfolgreich!"
echo ""
echo "Die App ist live unter: https://app.example.com"
displayName: 'Deploy to Production'
on:
success:
steps:
- script: |
echo "Version $(appVersion) erfolgreich in Production deployt!"
displayName: 'Production Live'
Gehe die Pipeline Abschnitt für Abschnitt durch:
- Build-Stage: Ein normaler Job (kein Deployment Job), der die Anwendung
baut und als Artefakt
app-packagepubliziert. Dieversion.json-Datei enthält die Version und Build-ID, sodass in späteren Stages nachvollziehbar ist, welcher Build deployt wurde. - DeployDev-Stage: Der erste Deployment Job. Beachte die Syntax-Unterschiede
zu einem normalen Job: Statt
job:steht hierdeployment:, und statt einer flachensteps:-Liste gibt es einestrategy:mitrunOnce:und einemdeploy:-Hook. Das Keywordenvironment: 'dev'verknüpft diesen Job mit dem gleichnamigen Environment - jeder Lauf erscheint in dessen Deployment-Historie. Beachte auch, dass keindownload-Step nötig ist: Der Deployment Job lädt die Artefakte automatisch herunter. - DeployStaging-Stage: Demonstriert alle Lifecycle-Hooks der
runOnce-Strategie:preDeploy: Läuft vor dem eigentlichen Deployment. Hier können Voraussetzungen geprüft werden (z. B. ob eine Datenbank-Migration nötig ist oder ob die Konfiguration stimmt).deploy: Das eigentliche Deployment. Hier würde man in der Praxis Befehle wieaz webapp deployoderkubectl applyausführen.postRouteTraffic: Läuft nach dem Deployment (und nach einer optionalen Traffic-Umleitung). Typischerweise werden hier Integration Tests gegen die frisch deployte Umgebung ausgeführt.on.success/on.failure: Abschluss-Hooks, die je nach Ergebnis ausgeführt werden. In der Praxis könntest du hier Benachrichtigungen senden (z. B. Slack/Teams) oder bei einem Fehler einen automatischen Rollback einleiten.
- DeployProduction-Stage: Kombiniert Deployment Job und
condition. Die Stage wird nur auf demmaster-Branch ausgeführt - auf Feature-Branches wird sie übersprungen. Hier werden nurpreDeploy,deployundon.successverwendet; nicht jeder Hook muss in jeder Stage genutzt werden.
Schritt 3: Committen und Pipeline starten¶
git add azure-pipelines.yml
git commit -m "Add deployment jobs with environments"
git push origin master
Schritt 4: Deployment-Historie prüfen¶
Nachdem die Pipeline durchgelaufen ist, hat jedes Environment ein Deployment in seiner Historie. Prüfe das im Browser:
- Gehe zu Pipelines > Environments.
- Klicke auf das Environment "staging".
- Du siehst die Deployment-Historie - eine chronologische Liste aller
Deployments mit:
- Deployment-Nummer: Fortlaufend pro Environment.
- Version/Commit: Der Git-Commit, auf dem der Build basiert.
- Status: Succeeded, Failed oder In Progress.
- Zeitpunkt: Wann das Deployment stattfand.
- Klicke auf ein einzelnes Deployment, um zum zugehörigen Pipeline-Run zu springen.
- Prüfe auch die Environments "dev" und "production" - auch dort sollte jeweils ein erfolgreiches Deployment angezeigt werden.
Die Deployment-Historie ist einer der Hauptvorteile von Environments: Du kannst jederzeit nachvollziehen, welche Version in welcher Umgebung läuft und wer das Deployment ausgelöst hat.
Aufräumen¶
Environments können bestehen bleiben - sie verursachen keine Kosten und werden in Lab 12 (Approval Gates) weiterverwendet.