有些Docker镜像,使用CTRL+C无法终止”docker run”命令。

huangapple go评论66阅读模式
英文:

Why with some docker images, CTRL+C doesn't kill the "docker run" command?

问题

Here is the translated content:

使用这个镜像(Caddy Web服务器):

docker run --rm -p 80:80 caddy:latest

我可以通过在终端内按下CTRL+C来停止它。

但有些其他镜像不会以这种方式工作,例如MariaDB:

docker run -it --rm -p 3306:3306 -e MARIADB_ALLOW_EMPTY_ROOT_PASSWORD=true mariadb:latest

我无法使用CTRL+C停止它。

我注意到在Caddy的Dockerfile中,只有CMD而没有ENTRYPOINT,而MariaDB则同时有ENTRYPOINTCMD

为什么会这样?有没有不支持处理终止信号的任何理由?如果这是原因,如何在Docker入口点中支持SIGTERM

英文:

With this image (the Caddy webserver):

docker run --rm -p 80:80 caddy:latest 

I can stop it with sending CTRL+C within the terminal.

Some other images won't work this way, for example MariaDB:

docker run -it --rm -p 3306:3306 -e MARIADB_ALLOW_EMPTY_ROOT_PASSWORD=true mariadb:latest

I'm not able to stop it with CTRL+C

I've noticed that in Caddy Dockerfile there is no ENTRYPOINT but only CMD, whereas MariaDB has both an ENTRYPOINT and a CMD.

Why? Is there any reason NOT to support the handling of a kill signal? How can one support the SIGTERM in a docker entrypoint, if that is the reason?

答案1

得分: 13

以下是您要翻译的内容:

but I'm a bit curious about how the official images solves the problem....
Don't forget that Docker 1.13 comes with tini, originally from krallin/tini.
Any image run with docker run --init would include an init inside the container that forwards signals and reaps processes.
As mentioned here, tini works transparently, Dockerfiles don't need to be modified in any way.
So, In Unix systems, when you press CTRL+C in a terminal, a SIGINT (Signal Interrupt) is sent to the foreground process group, which in this case is the Docker container. If you use CTRL+\, it sends SIGQUIT, and if you use CTRL+Z, it sends SIGTSTP (Signal Stop).
Docker containers run a single main process, and this process runs in PID 1.
PID 1 is special on Linux: it is the first process that runs and is the ancestor of all other processes.
It also has a special relationship with Unix signals: it is the only process that can choose to ignore SIGINT and SIGTERM. Other processes cannot ignore these signals, but they can have handlers that execute when they receive them.
In Docker, when you use CMD to specify the process to run, Docker will wrap that process with a small init system (like tini), which properly handles Unix signals and forwards them to the main process (I mentioned it originally here). This is why your caddy image, which does not have an ENTRYPOINT and only has CMD, can handle the CTRL+C.
However, when you use ENTRYPOINT, Docker does not wrap the process with this small init system, and the process runs as PID 1.
If the process does not have built-in handling for SIGINT or SIGTERM, it will not respond to CTRL+C. This is why your mariadb image, which has an ENTRYPOINT, does not stop when you press CTRL+C.
The handling of a kill signal is an important part of a graceful shutdown. When a process receives a SIGTERM (or SIGINT), it should stop accepting new work, finish its current work, and then exit. This allows it to clean up any resources it is using and ensure data consistency.
If the process does not handle these signals and is simply killed (with SIGKILL), it does not have a chance to clean up and may leave resources in an inconsistent state. This could be harmful in the case of a database like MariaDB, which could have uncommitted transactions or partially written data.
To support the handling of a kill signal in a Docker entrypoint, you can:

  1. Add signal handling to the entrypoint script itself. This requires modifying the script and might not be feasible if the entrypoint is a binary.
  2. Use an init system that can handle signals and forward them to the main process. There are small init systems like tini that are designed for this purpose. You can use them in your Dockerfile like this:
FROM your-base-image
RUN apt-get update && apt-get install -y tini
ENTRYPOINT ["/usr/bin/tini", "--", "your-command"]

This will run tini as PID 1, which will handle signals and forward them to your command.
3. Use Docker's built-in init, which is a minimal tini implementation. You can enable it with the --init option when you run your container:

docker run --init -it --rm -p 3306:3306 -e MARIADB_ALLOW_EMPTY_ROOT_PASSWORD=true mariadb:latest

This will run Docker's built-in init as PID 1, which will handle signals and forward them to MariaDB.

以下是翻译好的部分:

但我对官方镜像是如何解决这个问题有点好奇...
不要忘记 Docker 1.13 版本附带了 tini,最初来自于 krallin/tini
使用 docker run --init 运行的任何镜像都会在容器内部包含一个 init 进程,用于转发信号和清理进程。
如此所述,tini 透明地工作,Dockerfile 无需以任何方式进行修改。
因此,在Unix系统中,当您在终端中按下 CTRL+C 时,会向前台进程组发送一个 SIGINT(信号中断),在这种情况下是Docker容器。如果您使用 CTRL+\,它会发送 SIGQUIT,如果您使用 CTRL+Z,它会发送 SIGTSTP(信号停止)。
Docker容器运行一个主要进程,此进程在PID 1中运行。
PID 1在Linux上很特殊:它是第一个运行的进程,也是所有其他进程的祖先。
它还与Unix信号有特殊关系:它是唯一可以选择忽略 SIGINTSIGTERM 的进程。其他进程不能忽略这些信号,但它们可以有处理程序,在接收到信号时执行。
在Docker中,当您使用 CMD 指定要运行的进程时,Docker会使用一个小的 init 系统(如 tini)包装该进程,它可以正确处理Unix信号并将它们转发给主要进程(我最初在这里提到它)。这就是为什么您的 caddy 镜像,没有 ENTRYPOINT 只有 CMD,可以处理 CTRL+C 的原因。
但是,当您使用 ENTRYPOINT 时,Docker不会使用这个小的 init 系统包装进程,进程会以PID 1运行。
如果进程没有内置处理 SIGINTSIGTERM 的功能,它将不会响应 CTRL+C。这就是为什么您的 mariadb 镜像,具有 ENTRYPOINT,在按下 CTRL+C 时不会停止的原因。
处理终止信号是 优雅关闭 的重要组成部分。当进程接收到 SIGTERM(或 SIGINT)时,它应该停止接受新工作,完成当前工作,然后退出

英文:

> but I'm a bit curious about how the official images solves the problem....

Don't forget that Docker 1.13 comes with tini, originally from krallin/tini.

Any image run with docker run --init would include an init inside the container that forwards signals and reaps processes.
As mentioned here, tini works transparently, Dockerfiles don't need to be modified in any way.

So, In Unix systems, when you press CTRL+C in a terminal, a SIGINT (Signal Interrupt) is sent to the foreground process group, which in this case is the Docker container. If you use CTRL+\, it sends SIGQUIT, and if you use CTRL+Z, it sends SIGTSTP (Signal Stop).

Docker containers run a single main process, and this process runs in PID 1.
PID 1 is special on Linux: it is the first process that runs and is the ancestor of all other processes.
It also has a special relationship with Unix signals: it is the only process that can choose to ignore SIGINT and SIGTERM. Other processes cannot ignore these signals, but they can have handlers that execute when they receive them.

In Docker, when you use CMD to specify the process to run, Docker will wrap that process with a small init system (like tini), which properly handles Unix signals and forwards them to the main process (I mentioned it originally here).
This is why your caddy image, which does not have an ENTRYPOINT and only has CMD, can handle the CTRL+C.

However, when you use ENTRYPOINT, Docker does not wrap the process with this small init system, and the process runs as PID 1.

If the process does not have built-in handling for SIGINT or SIGTERM, it will not respond to CTRL+C. This is why your mariadb image, which has an ENTRYPOINT, does not stop when you press CTRL+C.

The handling of a kill signal is an important part of a graceful shutdown. When a process receives a SIGTERM (or SIGINT), it should stop accepting new work, finish its current work, and then exit. This allows it to clean up any resources it is using and ensure data consistency.

If the process does not handle these signals and is simply killed (with SIGKILL), it does not have a chance to clean up and may leave resources in an inconsistent state. This could be harmful in the case of a database like MariaDB, which could have uncommitted transactions or partially written data.

To support the handling of a kill signal in a Docker entrypoint, you can:

  1. Add signal handling to the entrypoint script itself. This requires modifying the script and might not be feasible if the entrypoint is a binary.

  2. Use an init system that can handle signals and forward them to the main process. There are small init systems like tini that are designed for this purpose. You can use them in your Dockerfile like this:

    FROM your-base-image
    RUN apt-get update && apt-get install -y tini
    ENTRYPOINT ["/usr/bin/tini", "--", "your-command"]
    

    This will run tini as PID 1, which will handle signals and forward them to your command.

  3. Use Docker's built-in init, which is a minimal tini implementation. You can enable it with the --init option when you run your container:

    docker run --init -it --rm -p 3306:3306 -e MARIADB_ALLOW_EMPTY_ROOT_PASSWORD=true mariadb:latest
    

    This will run Docker's built-in init as PID 1, which will handle signals and forward them to MariaDB.

These methods ensure that your Docker entrypoint can handle kill signals and perform a graceful shutdown when necessary.


> Is there any reason NOT to support the handling of a kill signal?

Not sure, unless you want your image to be really:

  • simple, for a very simple or short-lived processes, with no need of signal handling
  • fast, to avoid any shutdown signals during critical sections of their code
  • resilient, when you want to prevent unwanted or unauthorized shutdown.

But generally, ignoring termination signals can lead to issues like data loss, resource leakage, or zombie processes, and should be avoided.


> The only thing left is: if not supporting the shutdown signal is somehow dangerous for service like MariaDB, why MariaDB itself is not supporting it directly in the entrypoint?

This could be for a legacy reason, inherited from MySQL, which MariaDB is a fork of. MySQL was developed before Docker existed, and in a traditional server environment, it's less common for a process to receive a SIGINT. It's more common for a process to receive a SIGTERM when the system is shutting down, which MySQL and MariaDB do handle.
Its entrypoint reflects that, and you run it with --init to ensure a basic SIGINT support.

But keep in mind it is not the only issue. I made the mistake of running a MariaDB instance on an Azure ACI (container) instead of AKS (Kubernetes).
When an ACI closes (at least when I used it in 2021), it sends... a SIGKILL (a signal which cannot be caught, blocked, or ignored). When the kernel sends a SIGKILL to a process, that process is immediately terminated, and it doesn't get a chance to clean up or do any other work before it's killed.

So, independently of whether your image supports graceful shutdown, be mindful of your execution environment, which might not allow any graceful shutdown in the first place.

答案2

得分: 4

MariaDB在处理 SIGINT 时会使用 sigprocmask 来屏蔽其传递给进程的操作。

这是很久以前做的,可能是出于某种假设,比如在终端中启动时意外触发关闭操作。在旧式 sysv 脚本中可能存在一种情况,即在执行 service mariadb start 后迅速关闭终端,可能会向 mariadbd 传递 SIGINT(未经验证)。我没有看到要移除 SIGINT 屏蔽的 任何请求

在容器中,入口脚本在基本设置后使用 exec 执行 mariadbd 服务器可执行文件,因此忽略 SIGINT 的行为是历史上的行为,而不是明确的决策。

命令行上的 --gdb 选项(用于服务器或容器)将阻止忽略 SIGINT,并实现了通过 SIGINT 安全终止 MariaDB 的结果。它也可以在 .cnf 文件中提供。

示例:

$ podman run --env MARIADB_ALLOW_EMPTY_ROOT_PASSWORD=1 --rm mariadb:10.6 --gdb

.
2023-06-18 22:42:49 0 [Note] mariadbd: ready for connections.
Version: '10.6.14-MariaDB-1:10.6.14+maria~ubu2004'  socket: '/run/mysqld/mysqld.sock'  port: 0  mariadb.org binary distribution

明确的 SIGINT,尽管在命令行上也可以使用 ctrl-C。

$ podman kill --signal SIGINT mariadbsiginttest 
mariadbsiginttest
2023-06-18 22:44:31 0 [Note] mariadbd (initiated by: unknown): Normal shutdown
2023-06-18 22:44:31 0 [Note] InnoDB: FTS optimize thread exiting.
2023-06-18 22:44:31 0 [Note] InnoDB: Starting shutdown...
2023-06-18 22:44:31 0 [Note] InnoDB: Dumping buffer pool(s) to /var/lib/mysql/ib_buffer_pool
2023-06-18 22:44:31 0 [Note] InnoDB: Buffer pool(s) dump completed at 230618 22:44:31
2023-06-18 22:44:31 0 [Note] InnoDB: Removed temporary tablespace data file: "./ibtmp1"
2023-06-18 22:44:31 0 [Note] InnoDB: Shutdown completed; log sequence number 42294; transaction id 15
2023-06-18 22:44:31 0 [Note] mariadbd: Shutdown complete

MariaDB 可以在任何时候处理终止操作,包括 SIGKILL(除了 init 进程外无法捕获)或断电。这是事务性数据库的一部分,实现了持久性(ACID 中的 D)。在重新启动时,未提交的事务将被清理。进行优雅终止操作确实意味着启动时不需要崩溃恢复,因此更快。

英文:

MariaDB ignores SIGINT by masking its delivery to the process using sigprocmask.

This was done a long time ago on some assumptions maybe around accidental triggering on shutdown when started in a terminal. There might be a case in old-school sysv scripts where a closed terminal quickly after service mariadb start may have delivered a SIGINT to mariadbd (unverified). I can't see any requests to remove the SIGINT masking.

In the container the entrypoint script execs the mariadbd server executable after a basic setup, so the behaviour of ignoring SIGINT is what was historically there rather than an explicit decision.

The --gdb option on the command line (for server or container) will prevent the ignoring of SIGINT and achieve the result of being about to SIGINT to terminate MariaDB safely. It can also be provided in a .cnf file.

example:

$  podman run --env MARIADB_ALLOW_EMPTY_ROOT_PASSWORD=1 --rm mariadb:10.6 --gdb

.
2023-06-18 22:42:49 0 [Note] mariadbd: ready for connections.
Version: '10.6.14-MariaDB-1:10.6.14+maria~ubu2004'  socket: '/run/mysqld/mysqld.sock'  port: 0  mariadb.org binary distribution

explicit SIGINT, though ctrl-C on command line also works.

$ podman kill --signal SIGINT mariadbsiginttest 
mariadbsiginttest
2023-06-18 22:44:31 0 [Note] mariadbd (initiated by: unknown): Normal shutdown
2023-06-18 22:44:31 0 [Note] InnoDB: FTS optimize thread exiting.
2023-06-18 22:44:31 0 [Note] InnoDB: Starting shutdown...
2023-06-18 22:44:31 0 [Note] InnoDB: Dumping buffer pool(s) to /var/lib/mysql/ib_buffer_pool
2023-06-18 22:44:31 0 [Note] InnoDB: Buffer pool(s) dump completed at 230618 22:44:31
2023-06-18 22:44:31 0 [Note] InnoDB: Removed temporary tablespace data file: "./ibtmp1"
2023-06-18 22:44:31 0 [Note] InnoDB: Shutdown completed; log sequence number 42294; transaction id 15
2023-06-18 22:44:31 0 [Note] mariadbd: Shutdown complete

MariaDB can handle termination at any point, including SIGKILL (cannot be caught except by an init process) or power failures. This is part of the durability (the D in ACID) for transactional databases. Upon restart uncommitted transactions are cleaned up. Doing a graceful termination does mean the startup doesn't need a crash recovery and its quicker however.

huangapple
  • 本文由 发表于 2023年6月12日 22:25:51
  • 转载请务必保留本文链接:https://go.coder-hub.com/76457622.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定