Why does SIGHUP not work on busybox sh in an Alpine Docker container?

孤者浪人 提交于 2020-03-21 06:53:44

问题


Sending SIGHUP with

kill -HUP <pid>

to a busybox sh process on my native system works as expected and the shell hangs up. However, if I use docker kill to send the signal to a container with

docker kill -s HUP <container>

it doesn't do anything. The Alpine container is still running:

$ 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

By the way, with an Ubuntu container (which runs bash) it does work as expected:

$ 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

Sending SIGKILL does work, but I'd rather find out why SIGHUP does not.


Update: I'll add another example. Here you can see that busybox sh generally does hang up on SIGHUP successfully:

$ 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
$

However, running the same infinite sleep loop inside the docker container doesn't quit. As you can see, the container is still running after SIGHUP and only exits after 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
$

回答1:


(I don't have Docker env at hand for a try. Just guessing.)

For your case, docker run must be running busybox/sh or bash as PID 1.

According to Docker doc:

Note: A process running as PID 1 inside a container is treated specially by Linux: it ignores any signal with the default action. So, the process will not terminate on SIGINT or SIGTERM unless it is coded to do so.

For the differece between busybox/sh and bash regarding SIGHUP ---

On my system (Debian 9.6, x86_64), the signal masks for busybox/sh and bash are as follows:

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

As we can see busybox/sh does not handle SIGHUP so it's ignored. Bash catches SIGHUP so docker kill can deliver the signal to it. Then Bash is terminated because, according to its manual, "the shell exits by default upon receipt of a SIGHUP".


UPDATE 2020-03-07 #1:

Did a quick test and my previous analysis is basically correct. You can verify like this:

[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] #

As I showed earlier, by default busybox/sh does not catch SIGHUP so the signal will be ignored. But after busybox/sh explicitly trap SIGHUP, the signal will be delivered to it.

I also tried SIGKILL and yes it'll always terminate the running container. This is reasonable since SIGKILL cannot be caught by any process so the signal will always be delivered to the container and kill it.


UPDATE 2020-03-07 #2:

You can also verify it this way (much simpler):

[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] #



回答2:


Like the other answer already points out, the docs for docker run contain the following note:

Note: A process running as PID 1 inside a container is treated specially by Linux: it ignores any signal with the default action. So, the process will not terminate on SIGINT or SIGTERM unless it is coded to do so.

This is the reason why SIGHUP doesn't work on busybox sh inside the container. However, if I run busybox sh on my native system, it won't have PID 1 and therefore SIGHUP works.

There are various solutions:

  • Use --init to specify an init process which should be used as PID 1.

    You can use the --init flag to indicate that an init process should be used as the PID 1 in the container. Specifying an init process ensures the usual responsibilities of an init system, such as reaping zombie processes, are performed inside the created container.

    The default init process used is the first docker-init executable found in the system path of the Docker daemon process. This docker-init binary, included in the default installation, is backed by tini.

  • Trap SIGHUP and call exit yourself.

    docker run -dt alpine busybox sh -c 'trap exit HUP ; while true ; do sleep 60 & wait $! ; done'
    
  • Use another shell like bash which exits on SIGHUP by default, doesn't matter if PID 1 or not.



来源:https://stackoverflow.com/questions/60533390/why-does-sighup-not-work-on-busybox-sh-in-an-alpine-docker-container

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!