【问题标题】:Why does SIGHUP not work on busybox sh in an Alpine Docker container?为什么 SIGHUP 在 Alpine Docker 容器中的 busybox sh 上不起作用?
【发布时间】:2020-06-17 09:04:57
【问题描述】:

发送SIGHUP

kill -HUP <pid>

到我的本机系统上的busybox sh 进程按预期工作,并且外壳挂起。但是,如果我使用 docker kill 将信号发送到具有

的容器
docker kill -s HUP <container>

它什么也没做。 Alpine 容器仍在运行:

$ CONTAINER=$(docker run -dt alpine:latest)
$ docker ps -a --filter "id=$CONTAINER" --format "{{.Status}}"
Up 1 second
$ docker kill -s HUP $CONTAINER
4fea4f2dabe0f8a717b0e1272528af1a97050bcec51babbe0ed801e75fb15f1b
$ docker ps -a --filter "id=$CONTAINER" --format "{{.Status}}"
Up 7 seconds

顺便说一句,使用 Ubuntu 容器(运行 bash)它确实可以按预期工作:

$ CONTAINER=$(docker run -dt debian:latest)
$ docker ps -a --filter "id=$CONTAINER" --format "{{.Status}}"
Up 1 second
$ docker kill -s HUP $CONTAINER
9a4aff456716397527cd87492066230e5088fbbb2a1bb6fc80f04f01b3368986
$ docker ps -a --filter "id=$CONTAINER" --format "{{.Status}}"
Exited (129) 1 second ago

发送SIGKILL 确实有效,但我想知道为什么SIGHUP 无效。


更新:我将添加另一个示例。在这里你可以看到busybox sh 通常确实挂断SIGHUP 成功:

$ busybox sh -c 'while true; do sleep 10; done' &
[1] 28276
$ PID=$!
$ ps -e | grep busybox
28276 pts/5    00:00:00 busybox
$ kill -HUP $PID
$ 
[1]+  Hangup                  busybox sh -c 'while true; do sleep 10; done'
$ ps -e | grep busybox
$

但是,在 docker 容器内运行相同的无限睡眠循环不会退出。可以看到,在SIGHUP之后容器还在运行,在SIGKILL之后才退出:

$ CONTAINER=$(docker run -dt alpine:latest busybox sh -c 'while true; do sleep 10; done')
$ docker ps -a --filter "id=$CONTAINER" --format "{{.Status}}" 
Up 14 seconds
$ docker kill -s HUP $CONTAINER
31574ba7c0eb0505b776c459b55ffc8137042e1ce0562a3cf9aac80bfe8f65a0
$ docker ps -a --filter "id=$CONTAINER" --format "{{.Status}}"
Up 28 seconds
$ docker kill -s KILL $CONTAINER
31574ba7c0eb0505b776c459b55ffc8137042e1ce0562a3cf9aac80bfe8f65a0
$ docker ps -a --filter "id=$CONTAINER" --format "{{.Status}}"
Exited (137) 2 seconds ago
$

【问题讨论】:

  • 一种解释是 alpine 的 /bin/sh 忽略了 SIGHUP 而 debian 使用的是 bash。

标签: linux bash docker signals busybox


【解决方案1】:

(我手头没有 Docker 环境可供尝试。只是猜测。)

对于您的情况,docker run 必须以 PID 1 运行 busybox/shbash

根据 Docker doc:

注意:在容器内以 PID 1 运行的进程会被 Linux 特殊处理:它会忽略默认操作的任何信号。因此,该进程不会终止在 SIGINTSIGTERM 上,除非它被编码这样做。

busybox/shbash 关于 SIGHUP 的区别 ---

在我的系统(Debian 9.6,x86_64)上,busybox/shbash 的信号掩码如下:

busybox/sh:

USER     PID %CPU %MEM    VSZ   RSS TTY    STAT START   TIME COMMAND
root   82817  0.0  0.0   6952  1904 pts/2  S+   10:23   0:00 busybox sh

PENDING (0000000000000000):
BLOCKED (0000000000000000):
IGNORED (0000000000284004):
   3 QUIT
  15 TERM
  20 TSTP
  22 TTOU
CAUGHT (0000000008000002):
   2 INT
  28 WINCH

重击:

USER    PID %CPU %MEM    VSZ   RSS TTY     STAT START   TIME COMMAND
root   4871  0.0  0.1  21752  6176 pts/16  Ss    2019   0:00 /usr/local/bin/bash

PENDING (0000000000000000):
BLOCKED (0000000000000000):
IGNORED (0000000000380004):
   3 QUIT
  20 TSTP
  21 TTIN
  22 TTOU
CAUGHT (000000004b817efb):
   1 HUP
   2 INT
   4 ILL
   5 TRAP
   6 ABRT
   7 BUS
   8 FPE
  10 USR1
  11 SEGV
  12 USR2
  13 PIPE
  14 ALRM
  15 TERM
  17 CHLD
  24 XCPU
  25 XFSZ
  26 VTALRM
  28 WINCH
  31 SYS

正如我们所见,busybox/sh 无法处理SIGHUP,因此信号被忽略。 Bash 捕获 SIGHUP,因此 docker kill 可以将信号传递给 Bash,然后 Bash 将被终止,因为根据其 manual“默认情况下,shell 在收到SIGHUP".


更新 2020-03-07 #1:

快速测试了一下,我之前的分析基本正确。你可以这样验证:

[STEP 104] # docker run -dt debian busybox sh -c \
             'trap exit HUP; while true; do sleep 1; done'
331380090c59018dae4dbc17dd5af9d355260057fdbd2f2ce9fc6548a39df1db
[STEP 105] # docker ps 
CONTAINER ID        IMAGE            COMMAND                  CREATED             
331380090c59        debian           "busybox sh -c 'trap…"   11 seconds ago      
[STEP 106] # docker kill -s HUP 331380090c59    
331380090c59
[STEP 107] # docker ps 
CONTAINER ID        IMAGE               COMMAND             CREATED             
[STEP 108] #

如前所述,默认情况下busybox/sh 不会捕获SIGHUP,因此信号将被忽略。但是在busybox/sh显式trap SIGHUP之后,信号就会传递给它。

我也试过SIGKILL,是的,它总是会终止正在运行的容器。这是合理的,因为SIGKILL 不能被任何进程捕获,因此信号将始终传递到容器并杀死它。


更新 2020-03-07 #2:

你也可以这样验证(简单多了):

[STEP 110] # docker run -ti alpine
/ # ps
PID   USER     TIME  COMMAND
    1 root      0:00 /bin/sh
    7 root      0:00 ps
/ # kill -HUP 1    <-- this does not kill it because linux ignored the signal
/ # 
/ # trap 'echo received SIGHUP' HUP
/ # kill -HUP 1
received SIGHUP    <-- this indicates it can receive SIGHUP now
/ # 
/ # trap exit HUP
/ # kill -HUP 1    <-- this terminates it because the action changed to `exit`
[STEP 111] #

【讨论】:

  • "在容器内以 PID 1 运行的进程 被 Linux 特殊处理:它使用默认操作忽略任何信号。”
  • 很高兴知道--init 解决方案。我不是真正的 docker 用户。 :)
【解决方案2】:

就像其他答案已经指出的那样,docker run 的文档包含以下注释:

注意:在容器内以 PID 1 运行的进程被 Linux 特殊处理:它忽略任何具有默认操作的信号。因此,进程不会在 SIGINT 或 SIGTERM 上终止,除非它被编码为这样做。

这就是为什么 SIGHUP 在容器内的busybox sh 上不起作用的原因。但是,如果我在本机系统上运行 busybox sh,它不会有 PID 1,因此 SIGHUP 可以工作。

有多种解决方案:

  • 使用--init 指定应该用作PID 1 的初始化进程。

    您可以使用 --init 标志来指示应该将一个 init 进程用作容器中的 PID 1。指定一个 init 进程可确保 init 系统的通常职责,例如收获僵尸进程,在创建的容器内执行。

    使用的默认初始化进程是在 Docker 守护进程的系统路径中找到的第一个 docker-init 可执行文件。默认安装中包含的这个 docker-init 二进制文件由 tini 提供支持。

  • 诱捕SIGHUP并自己致电exit

    docker run -dt alpine busybox sh -c 'trap exit HUP ; while true ; do sleep 60 & wait $! ; done'
    
  • 使用另一个 shell,例如 bash,默认情况下在 SIGHUP 上退出,不管 PID 是否为 1。

【讨论】:

    猜你喜欢
    • 2021-08-04
    • 1970-01-01
    • 2016-01-23
    • 2023-03-29
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-07-11
    • 2014-11-23
    相关资源
    最近更新 更多