LW IT Solutions logo LW IT Solutions logo LW IT Solutions
« Blog-Übersicht /Raspberry Pi/Tutorials / Tutorial: Eine Docker-Compose-Datei für den unbeaufsichtigten Betrieb...
Diesen Artikel in anderen Sprachen lesen:

Tutorial: Eine Docker-Compose-Datei für den unbeaufsichtigten Betrieb auf dem Raspberry Pi härten

Tutorial: Eine Docker-Compose-Datei für den unbeaufsichtigten Betrieb auf dem Raspberry Pi härten

Management Summary

Docker Compose beschreibt, wie Anwendungen gemeinsam auf einem Server laufen. Bei einem unbeaufsichtigt betriebenen Raspberry Pi können diese Einstellungen Speicherverbrauch, wachsende Protokolle und Zugriffsrechte begrenzen. So soll ein einzelner Dienst nicht das ganze Gerät überlasten oder unnötig weitreichenden Zugriff erhalten. Festgelegte Softwarestände erleichtern kontrollierte Updates; Neustartregeln helfen bei manchen Unterbrechungen. Sicherungen und Wiederherstellung bleiben nötig, denn eine Zustandswarnung repariert noch keinen Fehler.

Inhalt
  1. Ein Ausgangspunkt für eine dauerhaft laufende Anwendung
  2. Neustart und Zustandsmeldung
  3. Begrenzte Protokolle und Ressourcen
  4. Image-Identität und Berechtigungen
  5. Konfigurationsprüfung mit ausdrücklicher Regel
  6. Wiederherstellungstests und nützliche Signale
  7. Quellen

Eine Docker-Compose-Datei kann typische Betriebsrisiken verringern; wenige Einstellungen garantieren aber keinen unbeaufsichtigten Jahresbetrieb. Neustartverhalten, Zustandsprüfung, Protokollaufbewahrung, Ressourcenlimits und Image-Aktualisierung lösen unterschiedliche Probleme. Sicherungen, Überwachung und geprüfte Wiederherstellung bleiben eigene Anforderungen.

Vier ergänzende Prüfbereiche im Compose-Betrieb: Neustartverhalten, Zustandsmeldung, Ressourcenbudgets und Image-Wiederherstellung. Konfigurationsprüfung allein weist keine Wiederherstellung nach.
Vier ergänzende Prüfbereiche im Compose-Betrieb: Neustartverhalten, Zustandsmeldung, Ressourcenbudgets und Image-Wiederherstellung. Konfigurationsprüfung allein weist keine Wiederherstellung nach.

Ein Ausgangspunkt für eine dauerhaft laufende Anwendung

Das folgende Beispiel gilt für eine Linux-Docker-Engine mit aktuellem Compose. Das Anwendungsimage muss UID/GID 1000, ein schreibgeschütztes Wurzeldateisystem und den angegebenen Health-Endpunkt unterstützen sowie curl enthalten. UID und GID sind die numerischen Benutzer- und Gruppenkennungen für Dateirechte. Die Image-Referenz stammt aus APP_IMAGE und muss einen überprüften Digest enthalten. Die Ressourcenwerte sind Beispiele, keine Dimensionierungsempfehlung für jeden Raspberry Pi.

YAML
services:
  app:
    image: "${APP_IMAGE:?verified image reference required}"
    restart: unless-stopped
    healthcheck:
      test: ["CMD", "curl", "-fsS", "--max-time", "4", "http://localhost:8080/health"]
      interval: 30s
      timeout: 5s
      retries: 3
      start_period: 60s
    logging:
      driver: json-file
      options:
        max-size: "10m"
        max-file: "3"
    mem_limit: 512m
    cpus: 1.5
    pids_limit: 128
    read_only: true
    tmpfs:
      - /tmp:size=64m,mode=1777
    security_opt:
      - no-new-privileges:true
    user: "1000:1000"

Benötigte Datenverzeichnisse der Anwendung erfordern ausdrücklich beschreibbare Volumes oder Bind-Mounts. Ein Volume ist von Docker verwalteter Speicher. Ein Bind-Mount macht ein ausgewähltes Verzeichnis des Hosts im Container verfügbar. Das Beispiel enthält bewusst keine Datenbank und keine persistenten Anwendungsdaten. Abweichende Berechtigungen, Ports, temporäre Verzeichnisse oder Ressourcen benötigen eine passende Konfiguration; bloßes Übernehmen der Einstellungen für ein beliebiges Image genügt nicht.

Neustart und Zustandsmeldung

unless-stopped startet einen beendeten Container einer dauerhaft laufenden Anwendung gemäß Docker-Regel neu und erhält einen absichtlichen Stopp über Daemon-Neustarts hinweg. Bei always kann ein manuell angehaltener Container nach einem Daemon-Neustart zurückkehren; das abgeschlossene Stoppkommando allein bewirkt keinen sofortigen Neustart. Der Standard no deaktiviert automatische Neustarts. Einmalige Aufgaben können berechtigt eine andere Regel benötigen.

Eine Neustartregel startet einen Prozess nicht allein wegen des Status unhealthy neu. Im gewöhnlichen Compose-Betrieb erfasst ein Healthcheck den Zustand; Wiederherstellung benötigt ein Anwendungsende oder eine gesondert entworfene Steuerung. Wachsende Wartezeiten reduzieren die Neustartfrequenz, beseitigen aber weder Ressourcenverbrauch noch die Notwendigkeit eines Alarms.

Das Beispiel prüft einen anwendungsspezifischen Endpunkt im Container. Ein fehlendes curl, eine falsche URL, ausgefallene Abhängigkeiten oder langsame Antworten können unhealthy verursachen; der Status beweist keinen hängenden Prozess. Eine Bereitschaftsprüfung kann wesentliche Abhängigkeiten einbeziehen. Automatische Neustartentscheidungen benötigen jedoch eine getrennte Bewertung, damit ein gemeinsamer Datenbankausfall nicht alle Anwendungen in Neustartschleifen bringt.

start_period erlaubt Anlauffehler, ohne sofort den Fehlversuchszähler zu erhöhen. Eine erfolgreiche Prüfung innerhalb dieses Zeitraums beendet dieses Schonverhalten für nachfolgende Fehler. Prüfdauer und Zeitplanung beeinflussen den Ablauf ebenfalls; drei Versuche bei 30-Sekunden-Intervallen ergeben deshalb keine exakt 90-sekündige Startfrist.

Für eine Datenbankabhängigkeit gehört die Bedingung service_healthy unter depends_on für den Dienst db in den Anwendungsdienst und ein echter Healthcheck in den Datenbankdienst. Diese Bedingung steuert den Start durch Compose. Das ist keine laufende Abhängigkeitsüberwachung; nach einem Host-Neustart prüft ein eigenständiger Docker-Containerneustart diese Compose-Startbedingung nicht erneut. Anwendungen benötigen weiterhin begrenzte Wiederholungsversuche und einen erneuten Verbindungsaufbau.

Begrenzte Protokolle und Ressourcen

Dockers voreingestellter Treiber json-file rotiert ohne Konfiguration nicht. Das Beispiel hält je Container bis zu drei rotierte Protokolldateien mit ungefähr 10 MB vor. Replikate vervielfachen dieses Budget; Anwendungsdateien, Volumes und andere Hostprotokolle sind nicht eingeschlossen. Weder 30 MB noch eine bestimmte Aufbewahrungsdauer passen universell. Docker empfiehlt für viele Installationen auch den local-Treiber mit bereits aktivierter Rotation.

Daemonweite Vorgaben werden mit den übrigen Einstellungen der bestehenden Daemon-Konfiguration zusammengeführt. Nach Übernahme der neuen Vorgaben behalten bestehende Container ihre alte Protokollkonfiguration bis zur Neuerstellung. Auch journald ist möglich, macht Protokolle allein aber nicht flüchtig: Storage=volatile speichert neue Journaldaten unter /run/log/journal; dieser Bestand geht beim Neustart verloren. Vorhandene persistente Journaldateien werden nicht automatisch gelöscht. Externe Aufbewahrung benötigt eine eigene Konfiguration.

mem_limit, cpus und pids_limit begrenzen verschiedene Ressourcen. Speicherdruck kann einen Prozess im Container beenden; der gesamte Container startet erst neu, wenn der Hauptprozess endet und die Neustartregel greift. Limits garantieren keinen unbeeinträchtigten Host oder Weiterbetrieb anderer Dienste. Gemeinsame Budgets benötigen Reserven für Betriebssystem, Docker, Caches und Lastspitzen. Tmpfs hält temporäre Dateien im Arbeitsspeicher und verbraucht einen Teil des Speicherbudgets. Beim Stoppen des Containers gehen diese Dateien verloren.

Für Raspberry Pi OS gilt keine allgemeine Aussage, dass Speichersteuerung deaktiviert wäre. Die Unterstützung hängt von Kernel, cgroup-Konfiguration, Betriebssystemversion und Betriebsmodus ab. docker info, die wirksame Containerkonfiguration und ein kontrollierter Test mit einer entbehrlichen Testlast zeigen das tatsächliche Verhalten. Bootparameter benötigen nur dann Änderungen, wenn Dokumentation und Diagnose des installierten Systems dies erfordern. In cmdline.txt bleiben Parameter auf einer logischen Zeile: Ein Umbruch zwischen Parametern kann spätere Angaben ausblenden; ein normaler abschließender Zeilenumbruch macht das System nicht zwangsläufig unstartbar.

Image-Identität und Berechtigungen

Der Neustart eines bestehenden Containers lädt kein neues Image. Eine Neuerstellung kann je nach Abrufregel und Cache ein neueres lokales oder heruntergeladenes Image verwenden. Ein Tag kann wechseln; ein Digest bezeichnet festen Image-Inhalt. Eine Fixierung benötigt deshalb einen bewussten Aktualisierungsprozess mit geprüften Digest-Änderungen, Plattformkompatibilität und Rückfallplanung. Ein festes Image friert weder Volume-Inhalte noch externe Dienste oder Datenbankschemata ein.

docker buildx imagetools inspect IMAGE:TAG zeigt Registry-Metadaten. Der Digest eines Mehrplattform-Index verweist auf mehrere Plattformmanifeste; ein plattformspezifischer Manifest-Digest fixiert eine Variante. Beides kann bei bewusster Auswahl passend sein. Ein lokaler RepoDigests-Eintrag garantiert nicht allgemein einen Index-Digest; lokal gebaute Images besitzen möglicherweise gar keinen Repository-Digest. Die benötigte Pi-Architektur muss tatsächlich im Index vorkommen. Fehlzuordnungen können abhängig von Plattformwahl und Emulation schon bei Auswahl, Erstellung oder erst beim Ausführen scheitern.

user: "1000:1000" legt eine Identität fest; die Verträglichkeit hängt von Image-Startverhalten und Volume-Rechten ab. no-new-privileges verhindert zusätzliche Rechte durch Mechanismen wie setuid-Programme, entfernt aber keine bereits gewährten Rechte und verwandelt root nicht in einen unprivilegierten Benutzer. Ein schreibgeschütztes Wurzeldateisystem lässt ausdrücklich beschreibbare Mounts beschreibbar. Diese Maßnahmen benötigen Anwendungstests statt einer Zusage, dass jede Anwendung unverändert weiterläuft.

Konfigurationsprüfung mit ausdrücklicher Regel

docker compose config -q prüft und löst das Compose-Modell auf, ohne das Ergebnis auszugeben. Fehlende gewöhnliche Variablen führen normalerweise zu einer Warnung und einer leeren Ersetzung; ${VAR:?message} lehnt dagegen fehlende oder leere Pflichtwerte ab. Aufgelöste Ausgaben können Geheimnisse enthalten und benötigen geschützte Ablage.

Der folgende Prüfer wird als check-compose.py gespeichert. Die enge Beispielregel verlangt für jeden aufgeführten Dienst Dauerbetrieb, einen ausdrücklich aktiven Healthcheck, die gezeigte JSON-Rotation und eine SHA-256-Image-Referenz. Geerbte Image-Healthchecks, Protokollvorgaben des Hosts und andere gültige Regeln werden dabei nicht automatisch abgeleitet.

Python
import json
import re
import sys

stack = json.load(sys.stdin)
failed = False
for name, svc in stack.get("services", {}).items():
    issues = []
    if svc.get("restart") not in ("always", "unless-stopped"):
        issues.append("restart policy needs review")
    health = svc.get("healthcheck") or {}
    test = health.get("test") or []
    if health.get("disable") or not test or test[0] == "NONE":
        issues.append("explicit active healthcheck missing")
    log = svc.get("logging") or {}
    opts = log.get("options") or {}
    if log.get("driver") != "json-file":
        issues.append("logging differs from this example policy")
    elif opts.get("max-size") != "10m" or str(opts.get("max-file")) != "3":
        issues.append("example log rotation limits missing")
    if not re.search(r"@sha256:[0-9a-f]{64}$", svc.get("image", "")):
        issues.append("SHA-256 image reference missing")
    result = "; ".join(issues) if issues else "example policy matched"
    print("{}: {}".format(name, result))
    failed = failed or bool(issues)
if not stack.get("services"):
    print("no services found", file=sys.stderr)
    sys.exit(2)
sys.exit(1 if failed else 0)
umask 077
docker compose config --format json > resolved.json && python3 check-compose.py < resolved.json

Exitstatus 0 bestätigt ausschließlich die Übereinstimmung des aufgelösten Modells mit dieser Regel. Weder Registry-Digest noch Anwendung, wirksame Ressourcenbegrenzung oder Sicherheit sind damit nachgewiesen. Bei Beanstandungen endet der Prüfer mit Status 1; eine leere Dienstliste ergibt 2. Der vorausgehende Compose-Aufruf muss erfolgreich sein, bevor dessen Ausgabe geprüft wird.

Wiederherstellungstests und nützliche Signale

Ein kontrollierter Wartungstest umfasst Anwendungsende, Abhängigkeitsausfall, Host-Neustart, absichtlich angehaltene Dienste und Wiederherstellung aus einer Sicherung. Ein bloßer Containerneustart prüft keine Datenwiederherstellung. Containerstatus, steigende Neustartzähler, Health-Ausgabe, freier Speicherplatz, Speicherdruck und eine externe Verfügbarkeitsprüfung liefern ergänzende Hinweise. Ein Alarm sollte ein anhaltendes Problem beschreiben statt jede harmlose Zustandsänderung zu melden.

Bedeutet unhealthy eine automatische Reparatur durch Docker?

Nein. Der Health-Status ist in diesem Compose-Aufbau eine Diagnoseinformation. Fehlgeschlagene Prüfung und Ursache benötigen Untersuchung; automatische Wiederherstellung verlangt eine ausdrückliche Strategie.

Schließen feste Images automatische Aktualisierungen aus?

Nein. Ein Aktualisierungswerkzeug kann eine geprüfte Digest-Änderung vorschlagen oder übernehmen. Wiederholtes Abrufen desselben Digests allein wählt keine neue Version; Erfolgsmeldungen hängen vom jeweiligen Werkzeug ab.

Warum kann die Härtung einen Start verhindern?

Ein Einstiegsskript kann beschreibbare Pfade oder eine anfängliche Rechteanpassung benötigen. Dokumentierter Laufzeitbenutzer und notwendige Mounts bestimmen die Lösung. Beliebiges Ändern der Dateieigentümer auf dem Host ist keine allgemeine Abhilfe.

Quellen

Lukas Wojcik

Lukas Wojcik

Systems architect and technology enthusiast specializing in scalable tracking solutions, GMP Stack (GA4 & GTM), and robust backend architectures. Advocate for clean code and privacy-first design.

Kontakt

Eine Frage oder eine Idee für ein Webprojekt? Schreib mir kurz, worum es geht. Das Formular nutzt reCAPTCHA und formconfirmed zum Schutz vor Spam.

Kommentar schreiben

Erfahrungen mit anderen Modellen und Rückfragen zum Aufbau sind hier willkommen.

Die E-Mail-Adresse wird nicht veröffentlicht. Pflichtfelder sind mit einem Stern versehen.