Do you know how to catch signals in docker containers?
I believe you must have used the docker stop command to stop a running container. Sometimes we may also use the docker kill command to forcefully close the container or pass a signal to the process in the container.
In fact, the operations we perform are essentially the interaction between the host and the program in the container by sending signals from the host to the container. For example, if we send a reload signal to the application in the container, then the application in the container will execute the corresponding handler to complete the task of reloading the configuration file after receiving the signal.
Signal (linux)
Signal is a form of inter-process communication. A signal is a message sent by the kernel to a process to tell the process that a certain event has occurred. When a signal is sent to a process, the process will immediately interrupt the current execution flow and start executing the signal handler (it is not accurate to say that the signal is processed at a specific time). If no handler is specified for this signal, the default handler is executed.
The process needs to register handlers for the signals it is interested in. For example, in order to allow the program to exit gracefully (to clean up resources after receiving the exit request), generally the program will handle the SIGTERM signal. Unlike the SIGTERM signal, the SIGKILL signal will violently end a process. Therefore, our application should implement a directory that captures and handles the SIGTERM signal to exit the program gracefully. If we fail, the user will have to resort to the SIGKILL signal as a last resort. In addition to SIGTERM and SIGKILL, there are signals like SIGUSR1 that specifically support user-defined behavior. The following code simply explains how to register a handler for a signal in nodejs:
process.on('SIGTERM', function() { console.log('shutting down...'); });
For more information about signals, the author mentioned it in the article "linux kill command" and will not be repeated here.
Signals in the container
Docker’s stop and kill commands are used to send signals to the container. Note that only process No. 1 in the container can receive the signal, this is very critical!
The stop command will first send the SIGTERM signal and wait for the application to end gracefully. If it is found that the application has not ended (the user can specify the waiting time), then send another SIGKILL signal to forcefully end the program.
The kill command sends the SIGKILL signal by default. Of course, you can specify any signal through the -s option.
Below we use a nodejs application to demonstrate the working process of signals in the container. Create an app.js file with the following content:
'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]); }); });
This application is an http server, listening on port 3000, with handlers registered for the SIGINT and SIGTERM signals. Next we will introduce how signals are handled when running programs in containers in different ways.
The application is used as process No. 1 in the container
Create a Dockerfile file and package the above application into the image:
FROM iojs:onbuild COPY ./app.js ./app.js COPY ./package.json ./package.json EXPOSE 3000ENTRYPOINT ["node", "app"]
Please pay attention to the way of writing the ENTRYPOINT instruction. This The way it is written will make node run as process No. 1 in the container.
Next create the image:
$ docker build --no-cache -t signal-app -f Dockerfile .
Then start the container and run the 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:
At this time, the process number of the node application in the container is 1:
Now we let the program exit and execute the command:
$ docker container kill --signal="SIGTERM" my-app
At this time the application will exit in the way we expect:
The application is not process No. 1 in the container
Create a script file app1.sh that starts the application, with the following content:
#!/usr/bin/env bash node app
Then create a Dockerfile1 file with the following content:
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"]
Next create the image:
$ docker build --no-cache -t signal-app1 -f Dockerfile1 .
Then start the container and run the application:
$ docker run -it --rm -p 3000:3000 --name="my-app1" signal-app1
At this time, the process number of the node application in the container is no longer 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入门教程
The above is the detailed content of Do you know how to catch signals in docker containers?. For more information, please follow other related articles on the PHP Chinese website!

Hot AI Tools

Undresser.AI Undress
AI-powered app for creating realistic nude photos

AI Clothes Remover
Online AI tool for removing clothes from photos.

Undress AI Tool
Undress images for free

Clothoff.io
AI clothes remover

Video Face Swap
Swap faces in any video effortlessly with our completely free AI face swap tool!

Hot Article

Hot Tools

Notepad++7.3.1
Easy-to-use and free code editor

SublimeText3 Chinese version
Chinese version, very easy to use

Zend Studio 13.0.1
Powerful PHP integrated development environment

Dreamweaver CS6
Visual web development tools

SublimeText3 Mac version
God-level code editing software (SublimeText3)

Hot Topics



The steps to update a Docker image are as follows: Pull the latest image tag New image Delete the old image for a specific tag (optional) Restart the container (if needed)

How to use Docker Desktop? Docker Desktop is a tool for running Docker containers on local machines. The steps to use include: 1. Install Docker Desktop; 2. Start Docker Desktop; 3. Create Docker image (using Dockerfile); 4. Build Docker image (using docker build); 5. Run Docker container (using docker run).

Steps to create a Docker image: Write a Dockerfile that contains the build instructions. Build the image in the terminal, using the docker build command. Tag the image and assign names and tags using the docker tag command.

Four ways to exit Docker container: Use Ctrl D in the container terminal Enter exit command in the container terminal Use docker stop <container_name> Command Use docker kill <container_name> command in the host terminal (force exit)

You can query the Docker container name by following the steps: List all containers (docker ps). Filter the container list (using the grep command). Gets the container name (located in the "NAMES" column).

Troubleshooting steps for failed Docker image build: Check Dockerfile syntax and dependency version. Check if the build context contains the required source code and dependencies. View the build log for error details. Use the --target option to build a hierarchical phase to identify failure points. Make sure to use the latest version of Docker engine. Build the image with --t [image-name]:debug mode to debug the problem. Check disk space and make sure it is sufficient. Disable SELinux to prevent interference with the build process. Ask community platforms for help, provide Dockerfiles and build log descriptions for more specific suggestions.

Methods for copying files to external hosts in Docker: Use the docker cp command: Execute docker cp [Options] <Container Path> <Host Path>. Using data volumes: Create a directory on the host, and use the -v parameter to mount the directory into the container when creating the container to achieve bidirectional file synchronization.

To save the image in Docker, you can use the docker commit command to create a new image, containing the current state of the specified container, syntax: docker commit [Options] Container ID Image name. To save the image to the repository, you can use the docker push command, syntax: docker push image name [: tag]. To import saved images, you can use the docker pull command, syntax: docker pull image name [: tag].
