
sleep
конечно, является заменой для большинства сложных процессов.
Этот Dockerfile (как вы можете видеть, использует форму exec, так что выполняется только один процесс и нет дочерних процессов bash
):
FROM busybox
CMD ["/bin/sleep", "100000"]
создает непрерываемый контейнер:
docker build -t kill-sleep .
docker run --rm --name kill-sleep kill-sleep
Когда я пытаюсь это остановить:
time docker stop kill-sleep
kill-sleep
real 0m10.449s
user 0m0.021s
sys 0m0.027s
время ожидания команды истекает через 10 секунд, после чего контейнер будет уничтожен.
Проблема не в том, что sleep
он не обрабатывает сигналы, потому что если я запущу его на хосте:
sleep 100000
# in another shell
ps faxww | grep sleep
kill -TERM 31333 # the PID
процесс немедленно останавливается.
Проблема может быть связана с тем, что в контейнере он работает как PID 1, но я пока не видел справочной документации по этому поводу.
решение1
Когда ты бежишь docker stop ...
,некоторые вещи произойдут:
docker
отправляетSIGTERM
в основной процесс контейнера. Процесс может маскировать/игнорироватьSIGTERM
, и если он это делает (или обрабатывает его без завершения) "ничего" случится.- После тайм-аута (по умолчанию 10 секунд)
docker
отправляетSIGKILL
основному процессу. Этот сигнал не может быть замаскирован процессом, и поэтому он немедленно умирает без возможности выполнить процедуру выключения.
В идеале, процессы, запущенные внутри, docker
будут реагировать на запросы SIGTERM
своевременно, выполняя все необходимые действия перед завершением.
Если вы знаете, что процессу либо не нужно выполнять никаких служебных действий (например, sleep
), либо он не будет должным образом реагировать на SIGTERM
, вы можете указать более короткий (или более длинный) тайм-аут с помощью флага -t
:
-t, --time=10 Seconds to wait for stop before killing it
Например, в вашем случае вы можете захотеть запустить docker stop -t 0 ${CONTAINER}
.
Причина, по которой поведение этого сигнала отличается, заключается в sleep
работе с ПИД = 1.
Обычно (например, при запуске с PID != 1) любой сигнал, с которым процесс явно не имеет дела, приводит к завершению процесса — попробуйте отправить sleep
a SIGUSR1
.
Однако при запуске с PID = 1 необработанные сигналы игнорируются, в противном случае вы получите панику ядра:
Kernel panic - not syncing: Attempted to kill init!
Вы можете отправить сигнал в Docker-контейнер с помощью инструментов Docker, например:
docker kill -s TERM kill-sleep
Как мы видим, это не дает желаемого эффекта, тогда как это дает:
docker kill -s KILL kill-sleep
Эксперимент
Dockerfile
FROM busybox
COPY run.sh /run.sh
RUN chmod +x /run.sh
CMD "/run.sh"
запустить.sh
#!/bin/sh
echo "sleeping"
sleep 100000
Теперь беги
docker build -t kill-sleep .
docker run --rm --name kill-sleep kill-sleep
А это в другом терминале:
docker stop kill-sleep
Мы наблюдаем ту же 10-секундную задержку/тайм-аут.
Решение
Теперь давайте разберемся с SIGTERM
. Фоновый режим и wait
ing for sleep
обусловлены тем, как оболочка POSIX обрабатывает сигналы (см.этотдля большего).
запустить.sh
#!/bin/sh
die_func() {
echo "oh no"
sleep 2
exit 1
}
trap die_func TERM
echo "sleeping"
sleep 100000 &
wait
Запустите команды еще раз, и мы увидим, что нам нужно!
$ time docker stop kill-sleep
kill-sleep
real 0m2.515s
user 0m0.008s
sys 0m0.044s
решение2
Еще несколько вариантов:
- Добавьте
--init
переключатель в команду запуска контейнера. Таким образом, sleep не будет иметь PID 1, а init сделает все правильно на TERM. - добавьте
--stop-signal=KILL
к команде запуска контейнера. Однако использование KILL в качестве обычной операции обычно не рекомендуется.