Docker-Container im Autostart: RestartPolicies von Docker statt Systemd-Dienst – Raspberry Pi

Als Video ansehen
Bereitgestellt über YouTube

Docker-Container im Autostart: RestartPolicies von Docker statt Systemd-Dienst – Raspberry Pi

Alexander hat zum Beitrag „Mein erster Container“ eine Zuschauerfrage gestellt: Er kommt aus der klassischen Linux Welt und fragt, wie er einen Docker-Container automatisch starten kann und ob dafür ein Daemon oder andere spezielle Konfiguration notwendig ist.

Ich hatte darauf schon kurz geantwortet, an dieser Stelle noch einmal Danke für dein Feedback! In diesem Beitrag möchte ich die Restart-Policys ausführlicher vorstellen. Mit Ihnen könnt ihr das Startverhalten von Containern steuern. Parallel dazu werden wir einen Blick auf den klassischen Weg mit (Systemd) Diensten und an der relevanten Stelle auch auf den Aufbau von Docker, um zu verstehen, wie Docker den Autostart ohne separate Dienste ermöglicht.

Im folgenden werde ich mich auf das Beispielszenario aus seinem Kommentar fokussieren, also einen Nginx Webserver. Grundsätzlich lässt sich dies aber auch auf andere Dienste anwenden – egal ob es sich um einen Apache2-Webserver oder ganz andere Programme handelt, die automatisch im Hintergrund gestartet werden sollen. Es geht also mehr ums Konzept als um den konkreten Anwendungsfall.

Ein Schritt zurück: So funktioniert der Autostart von Hintergrunddiensten ohne Docker

Systemd kommt mittlerweile unter nahezu allen gängigen Linux-Distributionen als Init-System zum Einsatz. Die Grundsätzliche Aufgabe eines Init-Systemes ist die gesamte Verwaltung aller Dienste. Konkret sind die wichtigsten dabei in unserem Szenario:

  1. Nach dem Booten den Nginx-Webserver im Hintergrund starten
  2. Beim Herunterfahren Nginx beenden

Das Init-System kümmert sich noch um eine Reihe zusätzlicher Dinge. Beispielsweise das parallele Starten mehrere Dienste, soweit möglich. Oder um Abhängigkeiten. Ein Dienst der aufs Internet zugreift, wird etwa erst gestartet, wenn die Netzwerkverbindung steht.

Unter Systemd legt man dafür ein sogenanntes Service Unit an, mit allen Details: Wie wird der Dienst gestartet, wie gestoppt, welche Abhängigkeiten gibt es usw. Installiert ihr entsprechende Programme per APT, kommt in der Regel ein entsprechend konfiguriertes Service Unit gleich mit. Der Nginx startet dadurch automatisch, ohne dass man mit dem Systemd-Dienst großartig in Berührung kommt – außer einem Neustart per systemctl restart nginx. Im Hintergrund lädt Systemd beim Booten die Unit und startet unseren Webserver als Hintergrundprozess.

Wie funktioniert Docker?

Bei Docker haben wir eine Client-Server Architektur: Es gibt den Docker-Daemon, welcher letztendlich auch nur ein Systemd-Dienst ist und dadurch automatisch nach dem Booten gestartet wird. Wenn ihr Docker oder Docker-Compose nutzt, kommunizierten diese Programme im Hintergrund mit dem Daemon, welcher die Befehle ausführt.

Beispiel: Wir starten einen Nginx-Container. Der Docker-Client (egal ob Docker oder Docker-Compose) sendet an den Docker-Daemon einen Befehl, dass er den Nginx-Container mit dem entsprechendem Image, Tag, Name und so weiter starten soll. Dieser lädt das Image herunter falls notwendig und erzeugt den Container. Anschließend erhält der Client eine Rückmeldung, ob es funktioniert hat oder Fehler aufgetreten sind.

Diese Architektur hat auch Nachteile und es gibt Alternativen. Das können wir uns in einem eigenen Beitrag mal genauer anschauen, an dieser Stelle würde es den Rahmen sprengen – daher fokussieren wir uns hier auf den Autostart.

Restart-Policys: Docker-Container automatisch starten, z.B. beim Systemstart

Jedem Container kann man eine sogenannte Restart-Policy zuweisen. Es gibt 4 verschiedene Policies:

  • no ist die Standard-Policy: Es gibt keine automatischen Starts oder Neustarts. Der Container läuft nur, wenn ihr ihn von Hand startet.
  • on-failure startet den Container nur neu, wenn er abstürzt und dies sauber durch einen Exitcode ungleich 0 mitteilt. Es gibt keinen Autostart beim Booten!
  • always sorgt dafür, dass der Container immer gestartet wird.
  • unless-stopped verhält sich wie always, mit einem einzigen Unterschied: Wird der Docker-Daemon neu gestartet (z.B: Systemneustart), dann startet er den Container nur dann, wenn er vor dem Neustart auch lief. Wenn wir also den Container mit z.B. docker stop stoppen und dann neu starten, läuft er danach nicht. Wogegen always ihn starten würde.

Beim Beispiel des Webservers machen nur always und unless-stopped Sinn, wobei ich an dieser Stelle always nutzen würde. Bootet ihr neu, startet Systemd den Docker-Daemon. Dieser prüft anhand der Restart-Policies, welche Container er starten soll. Auf die Startreihenfolge habt ihr allerdings nur innerhalb einer Docker-Compose Datei durch die Abhängigkeiten Einfluss! Dies ist meistens kein Problem und wird es erst bei größeren Docker-Hosts, die entsprechend schwergewichtige Container wie z.B. umfangreiche Java-Anwendungen starten.

Demonstration der Restart-Policies

Um das zu verdeutlichen, erstellen wir in einer docker-compose.yml vier Nginx Webserver:

version: "2.4"

services:
  nginx_no:
    image: nginx:1.21
    restart: "no"

  nginx_on-failture:
    image: nginx:1.21
    restart: on-failure

  nginx_always:
    image: nginx:1.21
    restart: always

  nginx_unless-stopped:
    image: nginx:1.21
    restart: unless-stopped

Die Portfreigaben werden für den Test nicht benötigt und wurden daher weggelassen. Starten wir die Container mit docker-compose up -d und starten den Pi neu, sollten nur jene mit der Restart-Policy always und unless-stopped automatisch gestartet worden sein:

$ docker-compose ps
NAME                                             COMMAND                  SERVICE                STATUS              PORTS
docker-restart-policies-nginx_always-1           "/docker-entrypoint.…"   nginx_always           running             80/tcp
docker-restart-policies-nginx_no-1               "/docker-entrypoint.…"   nginx_no               exited (0)
docker-restart-policies-nginx_on-failture-1      "/docker-entrypoint.…"   nginx_on-failture      exited (0)
docker-restart-policies-nginx_unless-stopped-1   "/docker-entrypoint.…"   nginx_unless-stopped   running             80/tcp

Stoppen wir den Container mit der Policy unless-stopped und starten den Docker-Daemon neu, läuft nach dem Starten des Daemon nur noch der Container mit der Policy always:

$ docker stop docker-restart-policies-nginx_unless-stopped-1
$ sudo systemctl restart docker
...
$ docker-compose ps
NAME                                             COMMAND                  SERVICE                STATUS              PORTS
docker-restart-policies-nginx_always-1           "/docker-entrypoint.…"   nginx_always           running             80/tcp
docker-restart-policies-nginx_no-1               "/docker-entrypoint.…"   nginx_no               exited (0)
docker-restart-policies-nginx_on-failture-1      "/docker-entrypoint.…"   nginx_on-failture      exited (0)
docker-restart-policies-nginx_unless-stopped-1   "/docker-entrypoint.…"   nginx_unless-stopped   exited (0)

Fazit: Eigene Systemd-Dienste braucht man mit Docker in der Regel nicht

Wird Docker eingesetzt, müssen wir uns lediglich für eine Restart-Policy entscheiden – um alles weitere kümmert sich der Docker-Daemon. Auch für selbst entwickelte Programme braucht keine Systemd Unit erstellt werden. Das reicht in den meisten Fällen völlig aus.

Ich hoffe, damit etwas Klarheit in das Autostartkonzept von Docker-Containern gebracht zu haben. Falls du noch weitere Fragen und Themenvorschläge hast, poste sie gerne ins Forum. Ich versuche sie zu beantworten und werde bei Themen, die für viele interessant sein können, weitere Beiträge anhand von Zuschauerfragen erstellen. In jedem Falle wünsche ich dir viel Erfolg mit deinen Projekten!

Leave a Reply