Maison  >  Article  >  Opération et maintenance  >  Savez-vous comment capter les signaux dans les conteneurs Docker ?

Savez-vous comment capter les signaux dans les conteneurs Docker ?

王林
王林avant
2021-02-22 10:29:052424parcourir

Savez-vous comment capter les signaux dans les conteneurs Docker ?

Je pense que vous devez avoir utilisé la commande docker stop pour arrêter un conteneur en cours d'exécution. Parfois, nous pouvons également utiliser la commande docker kill pour fermer de force le conteneur ou transmettre un signal au processus. le conteneur.

En fait, les opérations que nous effectuons sont essentiellement l'interaction entre l'hôte et le programme dans le conteneur en envoyant des signaux de l'hôte au conteneur. Par exemple, si nous envoyons un signal de rechargement à l'application dans le conteneur, alors l'application dans le conteneur exécutera le gestionnaire correspondant pour terminer la tâche de rechargement du fichier de configuration après avoir reçu le signal.

Signal (linux)

Le signal est une forme de communication inter-processus. Un signal est un message envoyé par le noyau à un processus pour lui indiquer qu'un certain événement s'est produit. Lorsqu'un signal est envoyé à un processus, le processus interrompra immédiatement le flux d'exécution en cours et commencera à exécuter le gestionnaire de signal (il n'est pas exact de dire que le signal est traité à un moment précis). Si aucun gestionnaire n'est spécifié pour ce signal, le gestionnaire par défaut est exécuté.
Le processus doit enregistrer des gestionnaires pour les signaux qui l'intéressent. Par exemple, afin de permettre au programme de se terminer normalement (pour nettoyer les ressources après avoir reçu la demande de sortie), le programme gérera généralement le signal SIGTERM. Contrairement au signal SIGTERM, le signal SIGKILL mettra fin violemment à un processus. Par conséquent, notre application doit implémenter un tel répertoire : capturer et gérer le signal SIGTERM pour quitter le programme en douceur. En cas d'échec, l'utilisateur devra recourir au signal SIGKILL en dernier recours. En plus de SIGTERM et SIGKILL, il existe des signaux comme SIGUSR1 qui prennent spécifiquement en charge un comportement défini par l'utilisateur. Le code suivant explique simplement comment enregistrer un gestionnaire pour un signal dans nodejs :

process.on('SIGTERM', function() {
  console.log('shutting down...');
});

Pour plus d'informations sur les signaux, l'auteur l'a mentionné dans l'article "linux kill command" et ne sera pas répété ici.

Signaux dans le conteneur

Les commandes stop et kill de Docker sont utilisées pour envoyer des signaux au conteneur. Notez que seul le processus n°1 dans le conteneur peut recevoir le signal, c'est très critique ! La commande
stop enverra d'abord le signal SIGTERM et attendra que l'application se termine correctement. S'il s'avère que l'application n'est pas terminée (l'utilisateur peut spécifier le temps d'attente), envoyez un autre signal SIGKILL pour forcer la fin du programme.
La commande kill envoie le signal SIGKILL par défaut. Bien sûr, vous pouvez spécifier n'importe quel signal via l'option -s.

Ci-dessous, nous utilisons une application nodejs pour démontrer le processus de fonctionnement des signaux dans le conteneur. Créez un fichier app.js avec le contenu suivant :

'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]);
  });
});

Cette application est un serveur http écoutant sur le port 3000, avec des gestionnaires enregistrés pour les signaux SIGINT et SIGTERM. Nous présenterons ensuite comment les signaux sont traités de différentes manières lors de l'exécution de programmes dans des conteneurs.

L'application sert de processus n°1 dans le conteneur

Créez un fichier Dockerfile et emballez l'application ci-dessus dans l'image :

FROM iojs:onbuild
COPY ./app.js ./app.js
COPY ./package.json ./package.json
EXPOSE 3000ENTRYPOINT ["node", "app"]

Veuillez faire attention à l'écriture de l'instruction ENTRYPOINT. La façon dont elle est écrite fera que le nœud s'exécutera en tant que processus n°1 dans le conteneur.

Créez ensuite l'image :

$ docker build --no-cache -t signal-app -f Dockerfile .

Ensuite, démarrez le conteneur pour exécuter l'application :

请注意 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:

À ce stade, le numéro de processus de l'application du nœud dans le conteneur est 1 :

Savez-vous comment capter les signaux dans les conteneurs Docker ?

Maintenant, nous laissons le programme se terminer et exécutons la commande :

$ docker container kill --signal="SIGTERM" my-app

À ce moment, l'application se fermera comme prévu :

Savez-vous comment capter les signaux dans les conteneurs Docker ?

L'application n'est pas le processus n°1 dans le conteneur

Créez un fichier script app1.sh qui démarre l'application, avec le contenu suivant :

#!/usr/bin/env bash
node app

Créez ensuite un fichier Dockerfile1 avec le contenu suivant :

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"]

Créez ensuite l'image :

$ docker build --no-cache -t signal-app1 -f Dockerfile1 .

Démarrez ensuite le conteneur pour exécuter l'application :

$ docker run -it --rm -p 3000:3000 --name="my-app1" signal-app1

À ce stade fois, le numéro de processus de l'application du nœud dans le conteneur n'est plus 1 :

Savez-vous comment capter les signaux dans les conteneurs Docker ?

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

Savez-vous comment capter les signaux dans les conteneurs Docker ?

结论

容器中的 1 号进程是非常重要的,如果它不能正确的处理相关的信号,那么应用程序退出的方式几乎总是被强制杀死而不是优雅的退出。究竟谁是 1 号进程则主要由 EntryPoint, CMD, RUN 等指令的写法决定,所以这些指令的使用是很有讲究的。

相关推荐:docker入门教程

Ce qui précède est le contenu détaillé de. pour plus d'informations, suivez d'autres articles connexes sur le site Web de PHP en chinois!

Déclaration:
Cet article est reproduit dans:. en cas de violation, veuillez contacter admin@php.cn Supprimer