Как я могу остановить Docker-контейнер, работающий в режиме сна?
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, но я еще не видел справочную документацию по этому вопросу.
2 ответа
Когда ты бежишь 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
работает с PID = 1.
Как правило (например, запуск с PID!= 1), любой сигнал, с которым процесс явно не имеет дело, приводит к завершению процесса - попробуйте отправить sleep
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"
run.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
для sleep
из-за того, как оболочка POSIX обрабатывает сигналы (подробнее об этом).
run.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
Еще несколько вариантов:
- добавить
--init
переключитесь на команду запуска контейнера. Таким образом, сон не является PID 1, и init делает правильные вещи в TERM. - добавить
--stop-signal=KILL
к команде запуска контейнера. Однако использование KILL как нормальной операции обычно не рекомендуется.