PHP, SQL, Docker und Git
Kein einzelnes Tool beherrscht alle Aspekte eines modernen Deployment-Stacks. Bash als Glue-Code verbindet PHP-Skripte, SQL-Dump-Workflows, Docker-Build-Ketten und Git-Hooks zu einem kohärenten Automatisierungssystem – mit robustem Exit-Code-Handling, gezielter Datenweiterleitung und klarer Fehler-Eskalation, die Probleme früh sichtbar macht.
Inhaltsverzeichnis
- 1. Was Glue-Code leistet und wo er versagt
- 2. Exit-Code-Handling: die Sprache der Tools
- 3. Bash und PHP: CLI-Skripte orchestrieren
- 4. SQL-Dumps, Imports und Datenbankmigrationen
- 5. Docker-Workflows in Bash orchestrieren
- 6. Git-Hooks und automatisierte Git-Workflows
- 7. Datenweiterleitung zwischen Prozessen
- 8. Fehler-Eskalation: von Bash nach Monitoring
- 9. Glue-Code-Ansätze im Vergleich
- 10. Zusammenfassung
- 11. FAQ
1. Was Glue-Code leistet und wo er versagt
Bash als Glue-Code ist keine Programmiersprache für komplexe Logik, sondern ein Orchestrierungswerkzeug für die Verbindung von Programmen, die jeweils ihren eigenen Domäne beherrschen. PHP ist gut für Anwendungslogik und Datenbankzugriffe, MySQL für relationale Operationen, Docker für Containerverwaltung, Git für Versionskontrolle. Bash als Glue-Code verbindet diese Werkzeuge, leitet Daten zwischen ihnen weiter, wertet ihre Ergebnisse aus und trifft einfache Steuerungsentscheidungen basierend auf Exit-Codes.
Wo Bash als Glue-Code versagt: wenn die Orchestrierungslogik selbst komplex wird. Komplexe Bedingungslogik, tiefe Datenstruktur-Manipulation und ausgedehnte Fehlerbehandlung über viele Werkzeuge hinweg sind Zeichen, dass das Skript in eine höhere Sprache migriert werden sollte – Python, Go oder ein dediziertes Build-Tool. Das Versagen von Bash als Glue-Code ist selten technisch bedingt, sondern fast immer ein Signal, dass das Abstraktionsniveau nicht mehr stimmt. Die Grenze ist subjektiv, aber praktisch: wenn das Skript länger als 300 Zeilen wird und dabei hauptsächlich Logik statt Orchestrierung enthält, lohnt sich die Diskussion.
Das wichtigste Prinzip für Bash als Glue-Code: Exit-Codes sind die einzige zuverlässige Kommunikation zwischen Prozessen. Ein Werkzeug, das mit Exit-Code 0 beendet, hat Erfolg gemeldet. Alles andere ist Fehler. Bash muss diese Codes abfangen, auswerten und eskalieren. Ohne set -euo pipefail und explizites Exit-Code-Handling ist Bash als Glue-Code taub gegenüber den Fehlermeldungen seiner Kinder.
2. Exit-Code-Handling: die Sprache der Tools
Exit-Codes sind das Protokoll zwischen Bash-Glue-Code und den aufgerufenen Tools. Code 0 bedeutet Erfolg, alles andere Fehler – wobei manche Tools differenzierte Codes verwenden: grep gibt 0 zurück wenn Treffer, 1 wenn keine Treffer, 2 bei einem Fehler. Das Muster grep -q "pattern" file || { echo "Pattern not found"; exit 1; } unterscheidet nicht zwischen "kein Treffer" und "Datei nicht lesbar". Das korrekte Glue-Code-Muster prüft den Exit-Code und wertet ihn gegen die bekannte Semantik aus: exit_code=$?; if (( exit_code == 1 )); then echo "no match"; elif (( exit_code == 2 )); then echo "error"; fi.
Bei Pipes verliert Bash standardmäßig alle Exit-Codes außer dem des letzten Befehls. Das Array PIPESTATUS enthält nach einer Pipe die Exit-Codes aller Befehle in der Reihenfolge ihrer Ausführung. Mit set -o pipefail beendet Bash die Ausführung, wenn irgendein Befehl in einer Pipe fehlschlägt – aber PIPESTATUS ist trotzdem nützlich, um zu erkennen, welcher Befehl in der Kette fehlgeschlagen ist. Für Bash-Glue-Code zwischen mehreren Tools ist das die entscheidende Diagnostik.
#!/usr/bin/env bash
# glue_exit_codes.sh — correct exit-code handling in Bash glue code
set -euo pipefail
# Capture exit code explicitly without triggering set -e
run_with_status() {
local cmd=("$@")
local exit_code=0
"${cmd[@]}" || exit_code=$?
echo "$exit_code"
}
# PHP CLI with exit-code check
php_exit=$(run_with_status php bin/magento setup:upgrade 2>&1 | tee /tmp/magento.log; echo "${PIPESTATUS[0]}")
if [[ "$php_exit" != "0" ]]; then
echo "[ERROR] Magento setup:upgrade failed (exit $php_exit)" >&2
exit "$php_exit"
fi
# Check PIPESTATUS after a pipeline — which step failed?
mysqldump --single-transaction magento 2>/dev/null | gzip -9 > /backup/magento.sql.gz
dump_status="${PIPESTATUS[0]}"
gzip_status="${PIPESTATUS[1]}"
if (( dump_status != 0 )); then
echo "[ERROR] mysqldump failed with code $dump_status" >&2; exit 1
fi
if (( gzip_status != 0 )); then
echo "[ERROR] gzip compression failed with code $gzip_status" >&2; exit 1
fi
echo "[OK] Database backup completed successfully"
3. Bash und PHP: CLI-Skripte orchestrieren
PHP-Anwendungen wie Magento 2 liefern über ihre CLI-Befehle (bin/magento) mächtige Werkzeuge für Deployment-Aufgaben, die von Bash als Glue-Code orchestriert werden. Das Muster: Bash ruft PHP-Befehle in der richtigen Reihenfolge auf, prüft jeden Exit-Code, sammelt Ausgaben im Log und entscheidet basierend auf dem Ergebnis über den nächsten Schritt. Kritisch ist dabei, dass PHP über Exceptions und Fehlerausgaben kommuniziert, die in Bash-Logs landen müssen – ohne die PHP-Ausgaben in Bash-Logik zu integrieren oder zu parsen, soweit möglich.
Ein häufiger Fehler beim Bash-Glue-Code für PHP: das Parsen von PHP-Ausgaben mit grep und awk, um zu bestimmen, ob ein Befehl erfolgreich war. Das ist fragil, weil Ausgabeformate sich ändern und lokalisiert sein können. Zuverlässiger ist immer der Exit-Code. Wenn ein PHP-Skript keinen zuverlässigen Exit-Code liefert, ist das ein Bug im PHP-Skript, der dort behoben werden sollte – nicht im Bash-Glue-Code durch Ausgabe-Parsing umgangen. Diese Grenze zwischen den Schichten zu respektieren ist das Fundament von wartbarem Bash-Glue-Code.
4. SQL-Dumps, Imports und Datenbankmigrationen
Datenbankoperationen sind ein klassischer Anwendungsfall für Bash als Glue-Code: Dump erstellen, komprimieren, an einen anderen Ort übertragen, importieren und Migrationen ausführen – jede dieser Operationen ist ein eigenständiges Tool, das Bash verbindet. Das grundlegende Muster für einen sicheren Dump: mysqldump --single-transaction --routines --triggers dbname | gzip -9 > dump.sql.gz mit anschließender PIPESTATUS-Prüfung. --single-transaction ist bei InnoDB-Tabellen entscheidend, um konsistente Snapshots ohne exklusive Sperren zu erzeugen.
Beim Import in Bash-Glue-Code: immer zuerst die Zieldatenbank prüfen, dann den Import mit explizitem Exit-Code abfangen. Das Muster mysql -e "SELECT 1" dbname >/dev/null 2>&1 || { echo "DB not accessible"; exit 1; } prüft Erreichbarkeit und Zugriffsrechte, bevor ein stundenlanger Import beginnt. Nach dem Import die Zeilenanzahl in kritischen Tabellen gegen erwartete Mindest-Werte prüfen – ein strukturell korrekter Import mit 0 Zeilen in der Produkt-Tabelle ist ein Fehler, den Exit-Code 0 nicht erkennt.
#!/usr/bin/env bash
# db_glue.sh — Bash glue code for database operations
set -euo pipefail
DB_HOST="${DB_HOST:-localhost}"
DB_USER="${DB_USER:?DB_USER not set}"
DB_PASS="${DB_PASS:?DB_PASS not set}"
DB_NAME="${DB_NAME:?DB_NAME not set}"
BACKUP_DIR="${BACKUP_DIR:-/backups/db}"
TIMESTAMP=$(date +%Y%m%d-%H%M%S)
# MySQL connection test before any operation
mysql_args=(-h"$DB_HOST" -u"$DB_USER" -p"$DB_PASS" --batch --skip-column-names)
mysql "${mysql_args[@]}" -e "SELECT 1" "$DB_NAME" >/dev/null 2>&1 \
|| { echo "[ERROR] Cannot connect to database $DB_NAME" >&2; exit 1; }
# Dump with pipeline exit-code validation
DUMP_FILE="$BACKUP_DIR/${DB_NAME}-${TIMESTAMP}.sql.gz"
mysqldump "${mysql_args[@]}" --single-transaction --routines \
"$DB_NAME" 2>/tmp/dump.err | gzip -9 > "$DUMP_FILE"
# Check both steps in the pipeline
if (( PIPESTATUS[0] != 0 )); then
echo "[ERROR] mysqldump failed: $(cat /tmp/dump.err)" >&2; exit 1
fi
# Verify dump integrity
actual_size=$(stat --format="%s" "$DUMP_FILE")
(( actual_size < 1024 )) && { echo "[ERROR] Dump suspiciously small: $actual_size bytes" >&2; exit 1; }
echo "[OK] Dump written: $DUMP_FILE (${actual_size} bytes)"
# Row count verification after import
check_table_count() {
local table="$1" min_rows="$2"
local count
count=$(mysql "${mysql_args[@]}" -e "SELECT COUNT(*) FROM $table" "$DB_NAME")
(( count < min_rows )) && { echo "[ERROR] Table $table: $count rows < $min_rows expected" >&2; return 1; }
echo "[OK] Table $table: $count rows"
}
check_table_count "catalog_product_entity" 1
check_table_count "customer_entity" 0
5. Docker-Workflows in Bash orchestrieren
Docker-Befehle in Bash als Glue-Code zu orchestrieren ist ein häufiger Anwendungsfall in CI/CD-Pipelines und Deployment-Skripten. Das grundlegende Muster: Container-Status prüfen, Build ausführen, Container ersetzen, Health-Check abwarten. Jeder dieser Schritte liefert Exit-Codes und ggf. Ausgaben, die ausgewertet werden müssen. Besonders das Warten auf Container-Readiness ist ein häufiger Stolperstein: docker run gibt Exit-Code 0, sobald der Container gestartet ist – nicht wenn er vollständig hochgefahren ist. Ein Bash-Glue-Code muss mit einer Retry-Schleife auf echte Readiness warten.
Das Muster für Container-Readiness in Bash-Glue-Code: eine Schleife mit konfigurierbarer Anzahl von Versuchen und Wartezeit, die eine spezifische Health-Prüfung ausführt – nicht nur docker ps, sondern einen echten Verbindungstest über docker exec oder einen HTTP-Request. Bei Magento könnte das ein docker exec app php bin/magento list sein, der sicherstellt, dass die PHP-Anwendung antwortfähig ist. Nach dem Erreichen der Readiness erst mit dem nächsten Schritt des Deployments fortfahren.
#!/usr/bin/env bash
# docker_glue.sh — Bash glue code for Docker orchestration
set -euo pipefail
IMAGE_NAME="${1:?Usage: $0 IMAGE_NAME}"
CONTAINER_NAME="app"
HEALTH_TIMEOUT=60
HEALTH_INTERVAL=3
# Build with streaming output, capture exit code
echo "[INFO] Building image: $IMAGE_NAME"
if ! docker build -t "$IMAGE_NAME" .; then
echo "[ERROR] Docker build failed" >&2; exit 1
fi
# Stop and remove existing container if running
if docker ps -q --filter "name=$CONTAINER_NAME" | grep -q .; then
echo "[INFO] Stopping existing container: $CONTAINER_NAME"
docker stop "$CONTAINER_NAME" >/dev/null
docker rm "$CONTAINER_NAME" >/dev/null
fi
# Start new container
docker run -d \
--name "$CONTAINER_NAME" \
--restart unless-stopped \
-e "APP_ENV=${APP_ENV:-production}" \
"$IMAGE_NAME"
# Wait for container readiness — not just running, but healthy
echo "[INFO] Waiting for container readiness (max ${HEALTH_TIMEOUT}s)..."
elapsed=0
until docker exec "$CONTAINER_NAME" php -r "echo 'ok';" 2>/dev/null | grep -q "ok"; do
sleep "$HEALTH_INTERVAL"
elapsed=$((elapsed + HEALTH_INTERVAL))
if (( elapsed >= HEALTH_TIMEOUT )); then
echo "[ERROR] Container $CONTAINER_NAME not ready after ${HEALTH_TIMEOUT}s" >&2
docker logs --tail 50 "$CONTAINER_NAME" >&2
exit 1
fi
echo "[INFO] Still waiting... (${elapsed}s)"
done
echo "[OK] Container $CONTAINER_NAME is ready after ${elapsed}s"
6. Git-Hooks und automatisierte Git-Workflows
Git-Hooks sind Shell-Skripte, die Git zu bestimmten Zeitpunkten im Workflow aufruft – und damit eine natürliche Heimat für Bash als Glue-Code. Der pre-commit-Hook läuft vor jedem Commit und kann PHP-Syntax-Prüfungen, PHPStan-Analysen oder PHPCS-Prüfungen ausführen. Der pre-push-Hook kann Tests ausführen bevor Code in das Remote-Repository gelangt. Der post-receive-Hook auf dem Server kann Deployments auslösen. In allen Fällen ist der Exit-Code entscheidend: Code 0 lässt Git fortfahren, alles andere bricht die Operation ab.
Eine wichtige Einschränkung beim Bash-Glue-Code in Git-Hooks: Hooks werden nicht automatisch versioniert oder geteilt, wenn sie im .git/hooks/-Verzeichnis liegen. Das Standardmuster für geteilte Hooks in Teams: Hooks in einem versionierten Verzeichnis im Repository ablegen (.githooks/) und mit git config core.hooksPath .githooks aktivieren. So landen Hook-Skripte im Repository, werden mit git pull aktualisiert und können in Code-Reviews begutachtet werden – wie jeder andere Glue-Code auch.
7. Datenweiterleitung zwischen Prozessen
Datenweiterleitung ist die Kernkompetenz von Bash als Glue-Code. Pipes, Umleitungen und Process Substitution bestimmen, wie die Ausgabe eines Tools zur Eingabe des nächsten wird. Das einfachste Muster – cmd1 | cmd2 – hat den bekannten Nachteil des Exit-Code-Verlustes ohne pipefail. Komplexere Weiterleitungen mit tee erlauben, eine Ausgabe gleichzeitig in eine Datei und in einen weiteren Prozess zu schicken: mysqldump dbname | tee >(gzip > backup.gz) | md5sum > backup.md5. Process Substitution macht den Dump gleichzeitig komprimiert speicherbar und prüfsummen-berechenbar – ohne Zwischendatei.
Ein weiteres mächtiges Werkzeug im Bash-Glue-Code für Datenweiterleitung: coproc. Ein Koprozess läuft als Hintergrundprozess und stellt bidirektionale Kommunikation über Dateideskriptoren bereit. Das ermöglicht, einen langlebigen Prozess zu starten und wiederholt Daten hin und her zu schicken, ohne für jede Anfrage einen neuen Kindprozess zu forken. Für Datenbankverbindungen, bei denen jede neue Verbindung Overhead erzeugt, kann ein coproc mysql die Verbindung über mehrere Queries hinweg offenhalten.
8. Fehler-Eskalation: von Bash nach Monitoring
Fehler in Bash-Glue-Code müssen mehr als nur lokale Logging-Einträge produzieren. In einem produktiven Stack erwartet man, dass kritische Fehler Alerts in Monitoring-Systemen auslösen, Slack-Nachrichten senden oder Tickets erstellen. Das Standardmuster: eine notify_failure-Funktion in der trap cleanup EXIT-Routine, die bei einem Nicht-Null-Exit-Code aktiv wird. In ihr kann ein HTTP-Webhook aufgerufen werden (curl -X POST -d "…" https://hooks.slack.com/…), eine E-Mail versendet oder ein Monitoring-System per API informiert werden.
Das Muster für strukturierte Fehler-Eskalation in Bash-Glue-Skripten: Fehlerkontext sammeln (welche Phase fehlschlug, welcher Exit-Code, welche letzten Logzeilen) und in einem strukturierten Format weiterleiten. Eine JSON-Payload mit printf '{"status":"error","phase":"%s","exit_code":%d,"log":"%s"}\n' "$PHASE" "$EXIT_CODE" "$LOG_EXCERPT" ermöglicht Monitoring-Systemen, die Fehler maschinell auszuwerten. Der Unterschied zwischen "Deployment fehlgeschlagen" und "Deployment fehlgeschlagen in Phase 'setup:upgrade', Exit-Code 127, letzte Zeile: Class X not found" ist in der Produktion erheblich.
9. Glue-Code-Ansätze im Vergleich
Für die Orchestrierung von PHP, SQL, Docker und Git gibt es mehrere Ansätze – Bash als Glue-Code ist dabei nicht immer die beste Wahl, aber oft die einfachste.
| Ansatz | Stärken | Schwächen | Empfehlung |
|---|---|---|---|
| Bash Glue-Code | Überall vorhanden, keine Deps | Komplex bei Logik, Debug schwer | Bis ~200 Zeilen Orchestrierung |
| Makefile | Dependency-Graph, parallel | Keine Schleifen, Quoting-Fallen | Build-Targets, nicht Flows |
| Python-Skript | Volle Sprache, testbar | Python-Version, venv nötig | Komplexe Logik, API-Calls |
| Ansible Playbook | Idempotent, deklarativ | Overhead, YAML-Komplexität | Infrastruktur-Konfiguration |
| CI/CD YAML | Integriert, visuell | Nur in Pipeline ausführbar | Für Pipeline-spezifische Steps |
Die Praxis zeigt: die beste Architektur für komplexe Deployments kombiniert Bash-Glue-Code für einfache Orchestrierung (Befehlsreihenfolge, Exit-Code-Prüfung, Log-Sammlung) mit einem höheren Tool für komplexere Logik. Ein Bash-Wrapper um ein Python-Deployment-Skript, der die Umgebungsvariablen setzt, Logs sammelt und Fehler eskaliert, ist oft wartbarer als ein monolithisches Bash-Skript, das alles selbst macht.
Mironsoft
Deployment-Infrastruktur, Bash-Automatisierung und DevOps-Tooling
Deployment-Skripte, die PHP, Docker und Git zuverlässig verbinden?
Wir bauen Bash-Glue-Code, der PHP-Anwendungen, Datenbankoperationen, Docker-Workflows und Git-Hooks zu einem kohärenten Deployment-System verbindet – mit vollständigem Exit-Code-Handling, strukturiertem Logging und automatischer Fehler-Eskalation.
Deployment-Skripte
Magento/PHP-Deployments mit robustem Glue-Code automatisieren
DB-Orchestrierung
Dump, Kompression, Import und Migrations-Workflows in Bash
Git-Hook-Integration
Pre-commit und Pre-push Hooks für PHP-Qualitätssicherung
10. Zusammenfassung
Bash als Glue-Code zwischen PHP, SQL, Docker und Git ist am effektivsten, wenn er sich auf seine Kernaufgabe konzentriert: Befehlsreihenfolge steuern, Exit-Codes auswerten und Daten zwischen Prozessen weiterleiten. set -euo pipefail und PIPESTATUS sind die technischen Grundlagen für zuverlässiges Exit-Code-Handling. Process Substitution mit >(cmd) ermöglicht gleichzeitige Datenweiterleitung ohne Zwischendateien. Readiness-Schleifen für Docker-Container ersetzen blindes Warten nach docker run. Git-Hooks in versionierten Verzeichnissen machen Qualitätssicherung zum automatischen Teil des Workflows.
Die Grenze von Bash als Glue-Code ist die Komplexität der Orchestrierungslogik. Wenn ein Skript anfängt, komplexe Bedingungen über viele Zustände hinweg zu verwalten oder tief in die Ausgaben der aufgerufenen Tools zu parsen, ist der Zeitpunkt für eine Neubewertung des Werkzeugs erreicht. Die Kombination aus Bash für einfache Orchestrierung und einem spezialisierten Tool für komplexe Logik – ein Ansible-Playbook, ein Python-Skript oder ein CI/CD-System – ist oft die wartbarste Architektur.
Bash als Glue-Code — Das Wichtigste auf einen Blick
Exit-Codes
PIPESTATUS[@] nach Pipes. set -o pipefail als Basis. Exit-Code-Semantik pro Tool verstehen – grep 1 ≠ Fehler.
PHP & SQL
Ausgaben nicht parsen – Exit-Codes auswerten. DB-Verbindung vor dem Dump testen. Zeilenanzahl nach Import verifizieren.
Docker
docker run liefert Exit-Code 0 bei Start, nicht bei Readiness. Retry-Schleife mit echtem Health-Check ist Pflicht.
Git-Hooks
Hooks in .githooks/ versionieren, git config core.hooksPath setzen. Exit ≠ 0 bricht git commit/push ab.