为什么SIGHUP不能在Alpine Docker容器中的busybox-sh上工作



使用发送SIGHUP

kill -HUP <pid>

到我的本机系统上的busybox sh进程按预期工作,shell挂起。但是,如果我使用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
$

(我手头没有Docker环境。只是猜测。(

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

根据Docker文档:

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

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

在我的系统(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

bash:

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将被终止,因为根据其手册;外壳在接收到CCD_。


更新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明确捕获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] #

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

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

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

有多种解决方案:

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

    您可以使用--init标志来指示init进程应该用作容器中的PID 1。指定init进程可以确保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,如默认情况下在SIGHUP上退出的bash,无论PID是否为1。

最新更新