Heim  >  Artikel  >  Betrieb und Instandhaltung  >  Wissen Sie, wie man Signale in Docker-Containern abfängt?

Wissen Sie, wie man Signale in Docker-Containern abfängt?

王林
王林nach vorne
2021-02-22 10:29:052443Durchsuche

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:

Wissen Sie, wie man Signale in Docker-Containern abfängt?

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:

Wissen Sie, wie man Signale in Docker-Containern abfängt?

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:

Wissen Sie, wie man Signale in Docker-Containern abfängt?

现在给 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 信号并优雅的退出了:

Wissen Sie, wie man Signale in Docker-Containern abfängt?

结论

容器中的 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!

Stellungnahme:
Dieser Artikel ist reproduziert unter:cnblogs.com. Bei Verstößen wenden Sie sich bitte an admin@php.cn löschen