Эксперимент

Эксперимент

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 ...,некоторые вещи произойдут:

  1. dockerотправляет SIGTERMв основной процесс контейнера. Процесс может маскировать/игнорировать SIGTERM, и если он это делает (или обрабатывает его без завершения) "ничего" случится.
  2. После тайм-аута (по умолчанию 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) любой сигнал, с которым процесс явно не имеет дела, приводит к завершению процесса — попробуйте отправить sleepa 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. Фоновый режим и waiting 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 в качестве обычной операции обычно не рекомендуется.

Связанный контент