Почему SIGHUP не работает на busybox sh в контейнере Alpine Docker? - PullRequest
0 голосов
/ 04 марта 2020

Отправка 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
$

Однако, выполнение того же бесконечного спящего режима l oop внутри контейнера 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
$

Ответы [ 2 ]

1 голос
/ 05 марта 2020

(у меня нет Docker env под рукой для попытки. Просто угадаю.)

В вашем случае docker run должен работать busybox/sh или bash как PID 1 .

Согласно Docker do c:

Примечание: процесс, выполняющийся как PID 1 внутри контейнера обрабатывается специально Linux: он игнорирует любой сигнал с действием по умолчанию. Таким образом, процесс не завершится на SIGINT или SIGTERM, если он не закодирован для этого.

Разница между busybox / sh и bash относительно SIGHUP ---

в моей системе (Debian 9.6, x86_64), маски сигналов для busybox/sh и bash следующие:

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 завершается, потому что, согласно руководству , "оболочка по умолчанию завершает работу при получении 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 явного перехвата 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] #
0 голосов
/ 10 марта 2020

Как уже указано в другом ответе, документы для docker run содержат следующее примечание:

Примечание: Процесс, выполняющийся как PID 1 внутри контейнера обрабатывается специально Linux: он игнорирует любой сигнал с действием по умолчанию. Таким образом, процесс не завершится на SIGINT или SIGTERM, если он не закодирован для этого.

По этой причине SIGHUP не работает на busybox sh внутри контейнера. Однако, если я запускаю busybox sh в своей собственной системе, у него не будет PID 1, и поэтому SIGHUP работает.

Существуют различные решения:

  • Использование --init для указания процесса инициализации, который должен использоваться в качестве PID 1.

    Вы можете использовать флаг --init, чтобы указать, что процесс init должен использоваться в качестве PID 1 в контейнере. Указание процесса инициализации гарантирует, что обычные обязанности системы инициализации, такие как запуск процессов zomb ie, выполняются внутри созданного контейнера.

    Используемый процесс инициализации по умолчанию - первый найденный исполняемый файл docker -init в системном пути процесса демона Docker. Этот docker -init двоичный файл, включенный в установку по умолчанию, поддерживается tini.

  • Trap SIGHUP и вызовите exit самостоятельно.

    docker run -dt alpine busybox sh -c 'trap exit HUP ; while true ; do sleep 60 & wait $! ; done'
    
  • Используйте другую оболочку, например bash, которая по умолчанию выходит на SIGHUP, не имеет значения, если PID 1 или нет.

...