ТЕХНОЛОГИИ

Почему docker stop не завершает worker: PID 1, сигналы и zombie

В этом материале
ГЛАВНОЕ ЗА МИНУТУ

Когда docker stop ждёт до принудительного завершения, исследуйте дерево процессов и PID 1. Shell-wrapper может не передавать сигнал worker, а основной процесс — не обрабатывать TERM. Дополнительная проблема возникает, если никто не собирает завершившиеся дочерние процессы.

Когда docker stop ждёт до принудительного завершения, исследуйте дерево процессов и PID 1. Shell-wrapper может не передавать сигнал worker, а основной процесс — не обрабатывать TERM. Дополнительная проблема возникает, если никто не собирает завершившиеся дочерние процессы.

Восстановите команду запуска

Сохраните image ENTRYPOINT, CMD и фактически запущенные аргументы. Установите, какая программа стала PID 1 внутри контейнера и кто является родителем worker. Интерактивный shell и production entrypoint могут создавать разные деревья. Для shell-script, запускающего единственный основной процесс, exec меняет модель: программа заменяет shell вместо запуска отдельным потомком. Однако корректность зависит от самого приложения и его обработки сигналов, а не только от формы Dockerfile.

Официальный источник 1.

Разделите доставку сигнала и cleanup

Docker направляет stop signal основному процессу. PID 1 имеет особое поведение обработки сигналов Linux; приложение должно реализовать нужное завершение. Передача TERM дочернему worker и reaping завершившихся детей — отдельные обязанности. —init добавляет небольшой init-процесс для соответствующих функций, но не придумывает за приложение протокол сохранения данных. Наличие init не подтверждает автоматически, что каждый worker получил сигнал и освободил свой ресурс.

Официальный источник 2.

Проверьте условный wrapper

В учебном сценарии script запускает worker в фоне и остаётся ждать. После stop лог содержит только завершение shell, а worker не записывает свой финальный маркер. Сравните это с вариантом корректного exec или явного trap/forwarding, подобранным под реальное дерево. Затем создайте отдельный короткоживущий дочерний процесс и проверьте, исчезает ли он из zombie состояния. Пример не требует запускать реальную модель или менять CUDA настройки.

Данные опытаПроверяемое свойство
PID/PPID деревоОпределён получатель stop signal
Маркер TERM workerПодтверждена обработка сигнала
Время завершенияВидно ожидание до принудительного stop
Завершившиеся детиПроверена обязанность reaping

Исправьте минимальный участок

Для одного основного приложения избегайте лишнего слоя shell, если он не нужен. Для нескольких процессов явно определите supervision и порядок остановки. Сохраните ограниченное время ожидания, достаточное для предусмотренного cleanup. Увеличение stop timeout маскирует отсутствующий forwarding, если worker вообще не получает сигнал. Проверьте права на запись финального состояния: корректно доставленный TERM не помогает, когда завершающий обработчик сам блокируется или пытается писать в недоступный путь.

Проверьте несколько завершений

Повторите запуск и stop в состоянии простоя, во время работы и после отказа дочернего процесса. Сохраните ожидаемые exit codes и финальные маркеры без пользовательских данных. Итоговый артефакт содержит дерево, способ доставки сигнала, ответственного за reaping и предельное время завершения. Эти сведения понадобятся и при Kubernetes deployment, где grace period ограничивает весь процесс остановки. Прекращение существования объекта контейнера не должно быть единственным доказательством выполненного cleanup внутри приложения.

Связанные разборы: gpu containers vs vms; vllm docker first launch.

← Все материалы