Heim > Artikel > Betrieb und Instandhaltung > Wissen Sie, wie man Signale in Docker-Containern abfängt?
Ich glaube, Sie müssen den Docker-Stop-Befehl verwendet haben, um einen laufenden Container zu stoppen. Manchmal verwenden wir auch den Docker-Kill-Befehl, um den Container zwangsweise zu schließen oder ein Signal an den Prozess im Container zu übergeben.
Tatsächlich handelt es sich bei den von uns ausgeführten Vorgängen im Wesentlichen um die Interaktion zwischen dem Host und dem Programm im Container, indem Signale vom Host an den Container gesendet werden. Wenn wir beispielsweise ein Neuladesignal an die Anwendung im Container senden, führt die Anwendung im Container nach dem Empfang des Signals den entsprechenden Handler aus, um die Aufgabe des Neuladens der Konfigurationsdatei abzuschließen.
Signale (Linux)
Signale sind eine Form der Kommunikation zwischen Prozessen. Ein Signal ist eine Nachricht, die der Kernel an einen Prozess sendet, um ihm mitzuteilen, dass ein bestimmtes Ereignis eingetreten ist. Wenn ein Signal an einen Prozess gesendet wird, unterbricht der Prozess sofort den aktuellen Ausführungsfluss und beginnt mit der Ausführung des Signalhandlers (es ist nicht korrekt zu sagen, dass das Signal zu einem bestimmten Zeitpunkt verarbeitet wird). Wenn für dieses Signal kein Handler angegeben ist, wird der Standardhandler ausgeführt.
Der Prozess muss Handler für die Signale registrieren, an denen er interessiert ist. Damit das Programm beispielsweise ordnungsgemäß beendet werden kann (um Ressourcen nach Erhalt der Exit-Anfrage zu bereinigen), verarbeitet das Programm im Allgemeinen das SIGTERM-Signal. Im Gegensatz zum SIGTERM-Signal beendet das SIGKILL-Signal einen Prozess gewaltsam. Daher sollte unsere Anwendung ein solches Verzeichnis implementieren: Erfassen und verarbeiten Sie das SIGTERM-Signal, um das Programm ordnungsgemäß zu beenden. Wenn wir scheitern, muss der Benutzer als letzten Ausweg auf das SIGKILL-Signal zurückgreifen. Neben SIGTERM und SIGKILL gibt es Signale wie SIGUSR1, die gezielt benutzerdefiniertes Verhalten unterstützen. Der folgende Code erklärt einfach, wie man einen Handler für ein Signal in nodejs registriert:
process.on('SIGTERM', function() { console.log('shutting down...'); });
Weitere Informationen zu Signalen hat der Autor im Artikel „Linux-Kill-Befehl“ erwähnt und wird hier nicht wiederholt.
Signale im Container
Dockers Stop- und Kill-Befehle werden verwendet, um Signale an den Container zu senden. Beachten Sie, dass nur Prozess Nr. 1 im Container das Signal empfangen kann. Dies ist sehr kritisch! Der Befehl
stop sendet zunächst das SIGTERM-Signal und wartet darauf, dass die Anwendung ordnungsgemäß beendet wird. Wenn festgestellt wird, dass die Anwendung nicht beendet wurde (der Benutzer kann die Wartezeit angeben), senden Sie ein weiteres SIGKILL-Signal, um das Programm zwangsweise zu beenden.
Der Kill-Befehl sendet standardmäßig das SIGKILL-Signal. Natürlich können Sie über die Option -s jedes beliebige Signal angeben.
Im Folgenden verwenden wir eine NodeJS-Anwendung, um den Arbeitsprozess von Signalen im Container zu demonstrieren. Erstellen Sie eine app.js-Datei mit folgendem Inhalt:
'use strict'; var http = require('http'); var server = http.createServer(function (req, res) { res.writeHead(200, {'Content-Type': 'text/plain'}); res.end('Hello World\n'); }).listen(3000, '0.0.0.0'); console.log('server started'); var signals = { 'SIGINT': 2, 'SIGTERM': 15 }; function shutdown(signal, value) { server.close(function () { console.log('server stopped by ' + signal); process.exit(128 + value); }); } Object.keys(signals).forEach(function (signal) { process.on(signal, function () { shutdown(signal, signals[signal]); }); });
Diese App ist ein HTTP-Server, der Port 3000 überwacht, wobei Handler für die Signale SIGINT und SIGTERM registriert sind. Als Nächstes stellen wir vor, wie Signale beim Ausführen von Programmen in Containern auf unterschiedliche Weise gehandhabt werden.
Die Anwendung dient als Prozess Nr. 1 im Container
Erstellen Sie eine Dockerfile-Datei und packen Sie die obige Anwendung in das Image:
FROM iojs:onbuild COPY ./app.js ./app.js COPY ./package.json ./package.json EXPOSE 3000ENTRYPOINT ["node", "app"]
Bitte achten Sie auf die Schreibweise der ENTRYPOINT-Anweisung Prozess Nr. 1 im Container ausführen.
Als nächstes das Image erstellen:
$ docker build --no-cache -t signal-app -f Dockerfile .
Dann den Container starten und die Anwendung ausführen:
请注意 ENTRYPOINT 指令的写法,这种写法会让 node 在容器中以 1 号进程的身份运行。 接下来创建镜像: $ docker build --no-cache -t signal-app -f Dockerfile . 然后启动容器运行应用程序: $ docker run -it --rm -p 3000:3000 --name="my-app" signal-app 此时 node 应用在容器中的进程号为 1:
Zu diesem Zeitpunkt ist die Prozessnummer der Knotenanwendung im Container 1:
Jetzt lassen wir das Programm beenden und führen Sie den Befehl aus:
$ docker container kill --signal="SIGTERM" my-app
An diesem Punkt wird die Anwendung wie erwartet beendet:
Die Anwendung ist nicht der Prozess Nr. 1 im Container
Erstellen Sie eine Skriptdatei app1.sh, die den startet Anwendung mit folgendem Inhalt:
#!/usr/bin/env bash node app
Dann erstellen Sie die Dockerfile1-Datei. Der Inhalt lautet wie folgt:
FROM iojs:onbuild COPY ./app.js ./app.js COPY ./app1.sh ./app1.sh COPY ./package.json ./package.json RUN chmod +x ./app1.sh EXPOSE 3000 ENTRYPOINT ["./app1.sh"]
Als nächstes erstellen Sie das Image:
$ docker build --no-cache -t signal-app1 -f Dockerfile1 .
Starten Sie dann den Container, um die Anwendung auszuführen:
$ docker run -it --rm -p 3000:3000 --name="my-app1" signal-app1
Zu diesem Zeitpunkt wird die Prozessnummer der Knotenanwendung im Container ist nicht mehr 1:
现在给 my-app1 发送 SIGTERM 信号试试,已经无法退出程序了!在这个场景中,应用程序由 bash 脚本启动,bash 作为容器中的 1 号进程收到了 SIGTERM 信号,但是它没有做出任何的响应动作。
我们可以通过:
$ docker container stop my-app1 # or $ docker container kill --signal="SIGKILL" my-app1
退出应用,它们最终都是向容器中的 1 号进程发送了 SIGKILL 信号。很显然这不是我们期望的,我们希望程序能够收到 SIGTERM 信号优雅的退出。
在脚本中捕获信号
创建另外一个启动应用程序的脚本文件 app2.sh,内容如下:
#!/usr/bin/env bash set -x pid=0 # SIGUSR1-handler my_handler() { echo "my_handler" } # SIGTERM-handler term_handler() { if [ $pid -ne 0 ]; then kill -SIGTERM "$pid" wait "$pid" fi exit 143; # 128 + 15 -- SIGTERM } # setup handlers # on callback, kill the last background process, which is `tail -f /dev/null` and execute the specified handler trap 'kill ${!}; my_handler' SIGUSR1 trap 'kill ${!}; term_handler' SIGTERM # run application node app & pid="$!" # wait forever while true do tail -f /dev/null & wait ${!} done
这个脚本文件在启动应用程序的同时可以捕获发送给它的 SIGTERM 和 SIGUSR1 信号,并为它们添加了处理程序。其中 SIGTERM 信号的处理程序就是向我们的 node 应用程序发送 SIGTERM 信号。
然后创建 Dockerfile2 文件,内容如下:
FROM iojs:onbuild COPY ./app.js ./app.js COPY ./app2.sh ./app2.sh COPY ./package.json ./package.json RUN chmod +x ./app2.sh EXPOSE 3000 ENTRYPOINT ["./app2.sh"]
接下来创建镜像:
$ docker build --no-cache -t signal-app2 -f Dockerfile2 .
然后启动容器运行应用程序:
$ docker run -it --rm -p 3000:3000 --name="my-app2" signal-app2
此时 node 应用在容器中的进程号也不是 1,但是它却可以接收到 SIGTERM 信号并优雅的退出了:
结论
容器中的 1 号进程是非常重要的,如果它不能正确的处理相关的信号,那么应用程序退出的方式几乎总是被强制杀死而不是优雅的退出。究竟谁是 1 号进程则主要由 EntryPoint, CMD, RUN 等指令的写法决定,所以这些指令的使用是很有讲究的。
相关推荐:docker入门教程
Das obige ist der detaillierte Inhalt vonWissen Sie, wie man Signale in Docker-Containern abfängt?. Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!