So führen Sie mehrere Dienste in einem Docker-Container aus
Veröffentlicht: 2022-06-30Docker ist eine Technologie zum Packen von Komponenten Ihres Stacks als isolierte Container. Es ist üblich, jeden Ihrer Prozesse in einem eigenen Container auszuführen und so eine saubere Trennung zwischen den Komponenten zu schaffen. Dies verbessert die Modularität und lässt Sie auf die Skalierbarkeitsvorteile der Containerisierung zugreifen.
Es kann immer noch Situationen geben, in denen Sie mehrere Dienste in einem einzigen Container ausführen möchten. Obwohl dies im Docker-Ökosystem nicht selbstverständlich ist, zeigen wir einige verschiedene Ansätze, die Sie verwenden können, um Container mit mehr als einem langlebigen Prozess zu erstellen.
Identifiziere das Problem
Docker-Container führen einen einzelnen Vordergrundprozess aus. Dies wird durch die ENTRYPOINT
und CMD
-Anweisungen des Bildes definiert. ENTRYPOINT
wird im Dockerfile
eines Images festgelegt, während CMD
beim Erstellen von Containern überschrieben werden kann. Container halten automatisch an, wenn ihr Vordergrundprozess beendet wird.
Sie können andere Prozesse über die CMD
starten, aber der Container wird nur ausgeführt, solange der ursprüngliche Vordergrundprozess aktiv ist. Den Container über die kombinierte Lebensdauer von zwei unabhängigen Diensten betriebsbereit zu halten, ist mit dem ENTRYPOINT/CMD
Mechanismus nicht direkt möglich.
Verpacken mehrerer Prozesse in einem Einstiegspunkt
Wrapper-Skripte sind die einfachste Lösung für das Problem. Sie können ein Skript schreiben, das alle Ihre Prozesse startet und auf ihre Beendigung wartet. Wenn Sie das Skript als Ihren Docker ENTRYPOINT
, wird es als Vordergrundprozess des Containers ausgeführt, wodurch der Container weiter ausgeführt wird, bis eines der umschlossenen Skripts beendet wird.
#!/bin/bash /opt/first-process & /opt/zweiter-prozess & warte -n Ausgang $?
Dieses Skript startet die Binärdateien /opt/first-process
und /opt/second-process
im Container. Die Verwendung von &
ermöglicht es dem Skript fortzufahren, ohne auf das Beenden jedes Prozesses warten zu müssen. wait
wird verwendet, um das Skript anzuhalten, bis einer der Prozesse beendet wird. Das Skript wird dann mit dem vom fertigen Skript ausgegebenen Statuscode beendet.
Dieses Modell führt dazu, dass der Container sowohl den first-process
als auch den second-process
bis einer von ihnen beendet wird. An diesem Punkt wird der Container angehalten, obwohl der andere Prozess möglicherweise noch ausgeführt wird.
Um dieses Skript zu verwenden, ändern Sie den ENTRYPOINT
und CMD
Ihres Docker-Images, um es zum Vordergrundprozess des Containers zu machen:
EINSTIEGSPUNKT ["/bin/sh"] CMD [./path/to/script.sh"]
Die Container-Option --init
Eine Herausforderung bei der Verwaltung von Containerprozessen ist die effektive Bereinigung beim Verlassen. Docker führt Ihre CMD
als Prozess-ID 1 aus und ist damit für die Verarbeitung von Signalen und die Eliminierung von Zombies verantwortlich. Wenn Ihr Skript diese Fähigkeiten nicht hat, könnten verwaiste untergeordnete Prozesse in Ihrem Container bestehen bleiben.
Der docker run
-Befehl hat ein --init
, das den Einstiegspunkt so ändert, dass er tini
als PID 1 verwendet. Dies ist eine minimale Init-Prozessimplementierung, die Ihre CMD
ausführt, die Signalweiterleitung handhabt und kontinuierlich Zombies erntet.
Es lohnt sich, --init
zu verwenden, wenn Sie damit rechnen, viele Prozesse zu erzeugen und die Bereinigung nicht manuell durchführen möchten. Tini ist ein leichter Init-Flavor, der für Container entwickelt wurde. Es ist viel kleiner als vollwertige Alternativen wie systemd
und upstart
.
Verwendung eines dedizierten Prozessmanagers
Manuelles Scripting wird schnell suboptimal, wenn Sie viele Prozesse verwalten müssen. Die Übernahme eines Prozessmanagers ist eine weitere Möglichkeit, mehrere Dienste in Ihren Docker-Containern auszuführen. Der Prozessmanager wird zu Ihrem ENTRYPOINT
und ist für das Starten, Warten und Aufräumen nach Ihren Arbeitsprozessen verantwortlich.

Bei der Implementierung dieses Ansatzes stehen mehrere Optionen zur Verfügung. supervisord
ist eine beliebte Wahl, die einfach über eine Datei /etc/supervisor/conf.d/supervisord.conf
konfiguriert werden kann:
[Programm:apache2] command=/usr/sbin/apache2 -DFOREGROUND [Programm:mysqld] command=/usr/sbin/mysqld_safe
Diese Konfigurationsdatei konfiguriert supervisord
zum Starten von Apache und MySQL. Um es in einem Docker-Container zu verwenden, fügen Sie alle erforderlichen Pakete zu Ihrem Image hinzu und kopieren Sie dann Ihre supervisord
-Konfigurationsdatei an den richtigen Speicherort. Legen Sie supervisord
als CMD
des Images fest, um es automatisch auszuführen, wenn Container gestartet werden.
VON ubuntu:neueste Führen Sie apt-get install -y apache2 mysql-serverSupervisor aus KOPIEREN supervisord.conf /etc/supervisor/conf.d/supervisord.conf EINSTIEGSPUNKT ["/bin/sh"] CMD ["/usr/bin/supervisord"]
Da supervisord
kontinuierlich ausgeführt wird, ist es nicht möglich, den Container zu stoppen, wenn einer Ihrer überwachten Prozesse beendet wird. Eine alternative Option ist s6-overlay
, das diese Fähigkeit hat. Es verwendet ein deklaratives Dienstmodell, bei dem Sie Dienstskripte direkt in /etc/services.d
:
# Fügen Sie Ihrem Bild ein s6-Overlay hinzu HINZUFÜGEN https://github.com/just-containers/s6-overlay/releases/download/v3.1.0.0/s6-overlay-noarch.tar.xz /tmp Führen Sie tar -C / -Jxpf /tmp/s6-overlay-noarch.tar.xz aus RUN printf "#!/bin/shn/usr/sbin/apache2 -DFOREGROUND" > /etc/services.d/first-service/run RUN chmod +x /etc/services.d/first-service/run # Verwenden Sie s6-overlay als Einstiegspunkt für Ihr Bild EINSTIEGSPUNKT ["/init"]
Sie können ein ausführbares finish
-Skript in Ihren Dienstverzeichnissen hinzufügen, um das Stoppen des Containers mit docker stop
zu handhaben. s6-overlay
führt diese Skripte automatisch aus, wenn sein Prozess aufgrund des stop
ein TERM
-Signal empfängt.
Finish-Skripte erhalten als erstes Argument den Exit-Code ihres Dienstes. Der Code wird auf 256 gesetzt, wenn der Dienst aufgrund eines nicht erfassten Signals beendet wird. Das Skript muss den endgültigen Exit-Code nach /run/s6-linux-init-container-results/exitcode
; s6-overlay liest diese Datei und beendet sich mit dem darin enthaltenen Wert, wodurch dieser Code als Stoppcode Ihres Containers verwendet wird.
#!/bin/sch echo "$1" > /run/s6-linux-init-container-results/exitcode
Wann sollten Sie mehrere Prozesse in einem Container ausführen?
Diese Technik wird am besten mit eng gekoppelten Prozessen verwendet, die Sie nicht trennen können, um sie als unabhängige Container auszuführen. Möglicherweise verfügen Sie über ein Programm, das auf einem Hilfsdienstprogramm im Hintergrund basiert, oder über eine monolithische Anwendung, die ihre eigene Verwaltung einzelner Prozesse durchführt. Die oben gezeigten Techniken können Ihnen helfen, diese Arten von Software zu containerisieren.
Das Ausführen mehrerer Prozesse in einem Container sollte nach wie vor möglichst vermieden werden. Das Festhalten an einem einzigen Vordergrundprozess maximiert die Isolation, verhindert, dass Komponenten sich gegenseitig stören, und verbessert Ihre Fähigkeit, bestimmte Teile zu debuggen und zu testen. Sie können Komponenten mithilfe von Container-Orchestratoren einzeln skalieren, wodurch Sie flexibel mehr Instanzen Ihrer ressourcenintensivsten Prozesse ausführen können.
Fazit
Container haben normalerweise einen Vordergrundprozess und werden so lange ausgeführt, wie er aktiv ist. Dieses Modell entspricht den Best Practices für die Containerisierung und lässt Sie die größten Vorteile aus der Technologie ziehen.
In manchen Situationen müssen möglicherweise mehrere Prozesse in einem Container ausgeführt werden. Da alle Images letztendlich einen einzigen Einstiegspunkt haben, müssen Sie ein Wrapper-Skript schreiben oder einen Prozessmanager hinzufügen, der die Verantwortung für das Starten Ihrer Zielbinärdateien übernimmt.
Prozessmanager geben Ihnen alles, was Sie brauchen, aber blähen Ihre Images mit zusätzlichen Paketen und Konfigurationen auf. Wrapper-Skripte sind einfacher, müssen aber möglicherweise mit Dockers --init
-Flag gekoppelt werden, um die Verbreitung von Zombie-Prozessen zu verhindern.