So führen Sie mehrere Dienste in einem Docker-Container aus

Veröffentlicht: 2022-06-30

Abbildung mit dem Docker-Logo

Docker 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.