Lab: Deployment & Infrastructure Scripts (Artikel 297)
Entwicklung von Deployment-Skripten für moderne Web-Applikationen. Erfahren Sie alles über atomare Verzeichniswechsel via Symlinks und das automatisierte Ausrollen von Code.
# Deployment Lab: Automatisches Ausrollen ohne Downtime
TL;DR / Management Summary Wer Code händisch via FTP auf einen Server lädt, lebt im letzten Jahrzehnt. Ein Senior Admin nutzt Deployment-Skripte, die den Code aus Git ziehen, Abhängigkeiten installieren und Dienste neu starten. In diesem Lab bauen wir ein Blue-Green-Deployment Skript: Wir laden den neuen Code in ein separates Verzeichnis und schalten erst bei Erfolg mittels eines Symlinks blitzschnell um. Das Ergebnis: Null-Sekunden Downtime und ein sofortiges Rollback-Szenario.
# 1. Das Konzept: Der Symlink-Switch
Atomarer Austausch.
Anstatt Dateien im laufenden Verzeichnis (/var/www/html) zu überschreiben, arbeiten wir mit Versionen.
graph TD
A[Git Push / Trigger] --> B[Clone into /apps/releases/v2]
B --> C[Run Tests / Install Deps]
C -->|Success| D[Atomic Switch: ln -sfn v2 current]
C -->|Failure| E[Abort & Keep v1 active]
D --> F[Reload Nginx/PHP-FPM]
subgraph "Filesystem"
G[current link] --> H[v1]
G -.-> I[v2]
end
# 2. Das Skript: deploy_app.sh
Sicherer Rollout.
#!/bin/bash
set -euo pipefail
# --- 1. CONFIGURATION ---
readonly APP_NAME="my-web-app"
readonly BASE_DIR="/opt/apps/${APP_NAME}"
readonly RELEASES_DIR="${BASE_DIR}/releases"
readonly CURRENT_LINK="${BASE_DIR}/current"
readonly TIMESTAMP=$(date +%Y%m%d_%H%M%S)
readonly REPO_URL="https://github.com/company/myapp.git"
# --- 2. PREPARATION ---
log() { echo "[$(date +'%F %T')] $*"; }
mkdir -p "$RELEASES_DIR"
NEW_RELEASE="${RELEASES_DIR}/${TIMESTAMP}"
# --- 3. THE WORKFLOW ---
log "INFO: Cloning latest code..."
git clone --depth 1 "$REPO_URL" "$NEW_RELEASE"
log "INFO: Installing dependencies (npm/pip)..."
# cd "$NEW_RELEASE" && npm install --production
log "INFO: Performing Atomic Switch..."
# -s: symbolic, -f: force, -n: treat link as file (no nesting)
ln -sfn "$NEW_RELEASE" "$CURRENT_LINK"
log "INFO: Reloading services..."
sudo systemctl reload nginx
# --- 4. CLEANUP ---
log "INFO: Keeping only last 5 releases..."
cd "$RELEASES_DIR" && ls -1t | tail -n +6 | xargs -d '\n' rm -rf --
log "SUCCESS: Deployment finished."
# 3. Integration in Nginx
Den Link nutzen.
Konfigurieren Sie Ihren Webserver so, dass er auf das current Verzeichnis zeigt:
server {
listen 80;
root /opt/apps/my-web-app/current/public;
# ...
}
# 4. Day-2 Operations: Rollback
Zurück zur letzten stabilen Version.
Wenn die neue Version Bugs enthält, ist der Weg zurück extrem einfach:
# Zeige alle Versionen
ls -lh /opt/apps/my-web-app/releases/
# Schalte manuell zurück
ln -sfn /opt/apps/my-web-app/releases/20231025_120000 /opt/apps/my-web-app/current
sudo systemctl reload nginx
# 5. Troubleshooting & “War Stories”
Wenn der Link ins Leere zeigt.
# Story 1: “Der hängende Root-Service”
Symptom: Das Skript bricht beim Symlink-Wechsel ab, weil der Admin-User keine Schreibrechte im /opt/apps Verzeichnis hat.
Ursache: Fehlende Sudoers-Konfiguration (Artikel 288).
Lösung: Erlauben Sie dem Deployment-User (deploy-bot) das Ausführen von ln und systemctl reload ohne Passwort.
# Story 2: “Das Cache-Dilemma”
Symptom: Der Symlink wurde gewechselt, aber die Webseite zeigt noch den alten Inhalt.
Ursache: Der PHP-OpCache (Artikel 031) hat die alten Dateien noch im RAM und bemerkt den Symlink-Wechsel nicht sofort.
Lösung: Fügen Sie sudo systemctl reload php8.1-fpm zum Skript hinzu oder nutzen Sie ein Tool wie cachetool, um den OpCache gezielt zu flushen.
# 6. Fazit & Empfehlung
- Atomarität: Nutzen Sie
ln -sfn. Es ist ein atomarer System-Call auf Kernel-Ebene. Es gibt keinen Moment, in dem der Link nicht existiert. - Wartung: Begrenzen Sie die Anzahl der Releases (Cleanup), um die Disk nicht zu füllen.
- Wahl: Für sehr komplexe Stacks (Kubernetes, hunderte Microservices) nutzen Sie GitOps Tools wie ArgoCD. Für klassische VMs ist dieses Skript unschlagbar.
# Anhang: Cheatsheet
| Aufgabe | Befehl |
|---|---|
| Atomarer Link-Wechsel | ln -sfn <target> <link> |
| Repo ohne Historie | git clone --depth 1 |
| Verzeichnisse sortieren | ls -1t (Neu nach alt) |
| Letzte X behalten | `tail -n +6 |
| Link Ziel prüfen | readlink -f <link> |
| Permissions fixen | chown -R www-data:www-data ... |