Текст и данные · Инструкция
Docker STOPSIGNAL и --stop-signal: как выбрать
Docker STOPSIGNAL и --stop-signal: как выбрать — вопрос про корректное завершение приложения, а не про ускорение.
Короткий ответ
Оставляйте SIGTERM, если приложение корректно его обрабатывает. `STOPSIGNAL` задаёт дефолт в образе, `--stop-signal` переопределяет его при создании контейнера; после тайм-аута `docker stop` всё равно может завершить процесс через SIGKILL.
Что именно происходит при docker stop
Команда `docker stop` сначала отправляет главному процессу контейнера сигнал завершения. Если образ и контейнер не переопределяли поведение, это SIGTERM. Docker ждёт grace period, чтобы приложение закрыло соединения, завершило запросы и сбросило данные, а затем при необходимости применяет SIGKILL. В документации Docker указан типичный дефолт тайм-аута: 10 секунд для Linux-контейнеров и 30 секунд для Windows-контейнеров, если отдельное значение не задано. Это означает, что проблема «контейнер всегда убивается жёстко» часто связана не с Docker как таковым, а с тем, что PID 1 не обрабатывает полученный сигнал или не успевает завершиться в заданное время.
Совет: Сначала проверьте, какой процесс является PID 1 внутри контейнера и какой сигнал он реально ожидает. Менять STOPSIGNAL без этой проверки — лечение симптома.
STOPSIGNAL в Dockerfile и --stop-signal при запуске
Инструкция `STOPSIGNAL SIGTERM` записывает предпочтительный сигнал в метаданные образа. Это удобно, когда разработчик образа точно знает, каким сигналом приложение должно корректно завершаться. Флаг `--stop-signal` у `docker run` или `docker create` имеет более высокий приоритет для конкретного контейнера и позволяет не пересобирать образ. Docker принимает имя сигнала в формате `SIG..` либо соответствующее беззнаковое число, но в конфигурации лучше использовать понятное имя: оно читается однозначнее. Важно различать сигнал и тайм-аут: `--stop-signal` выбирает первый сигнал, а `--stop-timeout` или `docker stop --timeout` определяет, сколько Docker ждёт перед принудительным завершением.
Важно: Не ставьте SIGKILL как STOPSIGNAL ради «быстрой остановки». SIGKILL нельзя перехватить, поэтому приложение лишается возможности корректно завершить работу.
Как выбрать сигнал без экспериментов на продакшене
- Проверьте документацию приложения: Узнайте, какой сигнал оно обрабатывает для graceful shutdown. Для многих серверов это SIGTERM, но отдельные процессы могут ожидать другой сигнал.
- Проверьте ENTRYPOINT и PID 1: Если приложение запускается через shell-обёртку, убедитесь, что она передаёт сигналы дочернему процессу или заменяет себя через `exec`. Иначе правильный STOPSIGNAL может не дойти до сервера.
- Оставьте образ самодостаточным: Если требование постоянно для этого приложения, задайте STOPSIGNAL в Dockerfile. Так поведение путешествует вместе с образом.
- Используйте --stop-signal для исключений: Переопределяйте сигнал на уровне контейнера, если конкретная среда требует отличающегося поведения или вы тестируете гипотезу без пересборки.
- Настройте тайм-аут: Дайте приложению достаточно времени закончить текущие операции, но не бесконечно. Отдельно измерьте нормальное время graceful shutdown.
- Проверьте логи и код возврата: Остановите тестовый контейнер, убедитесь, что приложение пишет штатное завершение, не повреждает данные и не получает принудительный kill из-за истёкшего тайм-аута.
Предупреждение: Ctrl+C и `docker stop` — не одно и то же: Dockerfile reference отмечает, что STOPSIGNAL относится к `docker stop`, тогда как Ctrl+C отправляет SIGINT процессу напрямую.
Где настраивается остановка контейнера
[object Object]
Почему смена сигнала иногда ничего не исправляет
Контейнерный сигнал приходит PID 1, а не «в приложение вообще». Если PID 1 — оболочка, которая не передаёт сигнал дальше, дочерний сервер продолжит работу до истечения тайм-аута, и Docker завершит контейнер принудительно. Аналогично приложение может получать SIGTERM, но игнорировать его из-за неправильной реализации shutdown hook или зависшего потока. Поэтому диагностика должна включать `docker inspect`, список процессов внутри контейнера и логи завершения. Часто правильное решение — исправить ENTRYPOINT на exec-form или корректно настроить процесс-менеджер, а не менять SIGTERM на экзотический сигнал. Отдельно помните о внешних оркестраторах: они тоже имеют собственные grace periods, которые должны быть согласованы со временем, нужным приложению на остановку.
Как проверить shutdown под реальной нагрузкой
Пустой тестовый контейнер может завершаться мгновенно, но продакшен-сервис часто держит HTTP-запросы, очередь, транзакции или открытые соединения. Поэтому тестируйте остановку с несколькими активными операциями и смотрите, перестаёт ли приложение принимать новые задачи после сигнала, успевает ли закончить начатые и когда закрывает процесс. Если выбранный grace period короче типичной долгой операции, Docker закономерно дойдёт до SIGKILL даже при исправном обработчике SIGTERM. В таком случае увеличьте тайм-аут или перестройте стратегию graceful shutdown, а не меняйте первый сигнал. Цель — предсказуемое завершение без повреждения данных и без бесконечного ожидания.
Почему exec-form ENTRYPOINT важен для graceful shutdown
Даже правильно выбранный STOPSIGNAL бесполезен, если главный процесс контейнера не получает его. Типичный риск появляется, когда приложение запускают shell-form командой, а оболочка остаётся PID 1 и не передаёт сигнал дочернему процессу так, как вы ожидаете. Exec-form ENTRYPOINT запускает целевой процесс напрямую и обычно делает путь сигнала понятнее; если нужна обёртка, она должна корректно форвардить сигналы и завершаться вместе с приложением. Для процессов, которые порождают дочерние процессы, полезен также встроенный init через `--init`, если он подходит вашему сценарию. Проверять это лучше не по Dockerfile «на глаз», а внутри запущенного контейнера: посмотрите дерево процессов и убедитесь, кто занимает PID 1. Если SIGTERM приходит не тому процессу, увеличение тайм-аута лишь дольше откладывает SIGKILL и не исправляет архитектуру завершения.
Как согласовать stop signal и тайм-аут с реальной работой сервиса
Graceful shutdown должен иметь измеримый сценарий: перестать принимать новые запросы, завершить уже начатые операции, сбросить буферы, закрыть соединения и выйти. Запустите контейнер под тестовой нагрузкой, отправьте `docker stop` и измерьте, сколько времени проходит до штатного завершения. Если нормальная длинная операция занимает 20 секунд, тайм-аут в 10 секунд предсказуемо приведёт к SIGKILL, даже если обработчик SIGTERM написан правильно. И наоборот, бесконечный timeout может надолго зависнуть в автоматическом деплое при реальной ошибке приложения. Выбирайте запас поверх измеренного времени и отдельно тестируйте поведение при зависании. В оркестраторе убедитесь, что его собственный termination grace period не короче контейнерного сценария. Сигнал отвечает за начало shutdown, тайм-аут — за то, сколько вы готовы ждать его завершения; это две независимые настройки.
Минимальный тест graceful shutdown
- PID 1 — ожидаемый процесс или корректный init/обёртка, передающая сигналы.
- Приложение документированно обрабатывает выбранный сигнал.
- После `docker stop` в логах видно штатное завершение, а не внезапный обрыв.
- Тайм-аут больше реального времени закрытия соединений и записи данных.
- STOPSIGNAL не используется для маскировки зависаний, которые должны чиниться в приложении.
- Разовое Ctrl+C не принимается за эквивалент теста `docker stop`.
Что учитывать
Контейнерный сигнал приходит PID 1, а не «в приложение вообще». Если PID 1 — оболочка, которая не передаёт сигнал дальше, дочерний сервер продолжит работу до истечения тайм-аута, и Docker завершит контейнер принудительно. Аналогично приложение может получать SIGTERM, но игнорировать его из-за неправильной реализации shutdown hook или зависшего потока. Поэтому диагностика должна включать `docker inspect`, список процессов внутри контейнера и логи завершения. Часто правильное решение — исправить…
Источники и проверка
Фактическая часть сверена по первичным источникам (в т.ч. Dockerfile reference). Пример и формулировки — редакция N1RO на 2026-09-20.