Я столкнулся со странным поведением в моем Bash
сценарий, у меня есть это Bash
скрипт, который работает с PID 1
(это entrypoint
для Docker
контейнер, если вы не знакомы с Docker, я предполагаю, что вы можете игнорировать эту информацию).
Когда я запускаю следующий сценарий, SIGTERM
завершает все очень быстро, и вроде бы все в порядке (имейте в виду, что sshd
сервис не существует! Вся моя система запускает только этот сценарий, который запускается tail
не более того, но до сих пор это не проблема).
#!/bin/bash
trap "pkill sshd" SIGTERM
export PATH=/usr/local/samba/bin/:/usr/local/samba/sbin/:$PATH
if [ -f /usr/local/samba/etc/smb.conf ]; then
exec /usr/local/samba/sbin/samba -i
else
tail -f /dev/null & wait ${!}
fi
Проблема возникает, когда я удаляю это trap
. Теперь моя система зависает, и похоже, что tail все еще работает и по какой-то причине не заканчивается (если вы знакомы с Docker, Docker ждет 10 секунд, а затем убивает контейнер, потому что он не ответил на SIGTERM
(опять же, если вы не знакомы с Docker, проигнорируйте эту информацию).
#!/bin/bash
export PATH=/usr/local/samba/bin/:/usr/local/samba/sbin/:$PATH
if [ -f /usr/local/samba/etc/smb.conf ]; then
exec /usr/local/samba/sbin/samba -i
else
tail -f /dev/null & wait ${!}
fi
Может ли кто-нибудь объяснить мне, в чем именно проблема? Почему это подделка trap
заставляет все работать (хотя практически ничего не делает, но работает, потому что просто есть).
Я просто хочу упомянуть, что использование пустого trap
: trap "" SIGTERM
не помогает, в ловушке должно быть что-то, чтобы работать (даже если ничего не делает).
Надеюсь, что кто-то может мне помочь, спасибо!
Вы не предоставили свой Dockerfile
и неясно, как вы отправляете SIGTERM
сигнал к контейнеру.
Однако вот что я придумал, пытаясь воспроизвести вашу проблему:
Мой Dockerfile
:
FROM ubuntu
ADD ./entrypoint.sh /opt/entrypoint.sh
# Using the exec form here, so that the process is assigned PID 1.
ENTRYPOINT ["/opt/entrypoint.sh"]
Контейнер сборки:
$ docker build -f Dockerfile -t test_image .
Не забывайте перестраивать контейнер каждый раз, когда вы меняете сценарий точки входа.
Запустите контейнер с помощью этой команды:
$ docker run --rm -it --name test_trap test_image
Теперь посмотрим, что происходит при каждом запуске.
1) С trap
линия в вашем Bash
сценарий:
# The main process will receive SIGTERM, trap it and exit.
$ docker stop test_trap
# The main process will receive SIGTERM, trap it and exit.
$ docker kill -s=TERM test_trap
# The main process will receive SIGKILL and will be stopped immediately.
$ docker kill -s=KILL test_trap
2) Без trap
линия:
# The main process will receive SIGTERM which will be ignored.
# After a grace period (10s by default) it will receive SIGKILL and will be stopped.
$ docker stop test_trap
# The main process will receive SIGTERM which will be ignored.
# Container will continue running.
$ docker kill -s=TERM test_trap
# The main process will receive SIGKILL and will be stopped immediately.
$ docker kill -s=KILL test_trap
Причина в том, что ядро обрабатывает процесс с помощью PID 1
специально и не убивает процесс получения SIGTERM
сигнал (а также SIGINT
).
Дополнительная информация по этому вопросу:
Любой процесс может зарегистрировать свои собственные обработчики для TERM и использовать их для выполнения очистки перед выходом. Если процесс не зарегистрировал пользовательский обработчик сигналов, ядро обычно возвращается к поведению по умолчанию для сигнала TERM: завершение процесса.
Однако для PID 1 ядро не вернется к поведению по умолчанию при пересылке TERM. Если ваш процесс не зарегистрировал свои собственные обработчики (чего нет в большинстве процессов), TERM не повлияет на процесс.
Источник - https://engineeringblog.yelp.com/2016/01/dumb-init-an-init-for-docker.html
ОБНОВИТЬ
Я пока не могу комментировать, поэтому оставлю комментарий здесь. Это то же самое PID 1
проблема. С обоими -d
и -td
обработка сигналов работает должным образом: TERM
игнорируется, так как процессу точки входа назначается PID 1
, в то время как KILL
завершает процесс. Если вы добавите trap
линия, затем TERM
сигнал будет перехвачен в обоих случаях. Если это не работает для вас по каким-либо причинам, вы должны опубликовать свой Dockerfile
, точные команды, которые вы выполняете, и соответствующим образом обновите свой вопрос.
Собственно добавление t
параметр (для выделения tty) при запуске контейнера решает проблему. Я управлял им с -d
параметр, а теперь с -td
.
Не знаю почему, но оно сработало. Было бы здорово, если бы кто-нибудь мог объяснить, почему это происходит.