#Как проявляется проблема и где искать причину
Команда docker stop ждёт до истечения таймаута, затем Docker принудительно завершает процесс. Причина часто в том, что основной процесс не обрабатывает SIGTERM или запущен через оболочку, которая не передаёт сигнал.
| Возможная причина | Как узнать | Что делать |
|---|---|---|
| PID 1 — оболочкаКоманда CMD/ENTRYPOINT в shell-форме может оставить приложение дочерним процессом | Основной процесс внутри — sh или bash, а не приложение | Перейти на exec-форму запуска после тестирования |
| Долгое штатное завершениеСервис долго ждёт соединения или сохранения состояния | После SIGTERM пишет о сохранении и выходит по истечении таймаута | Оценить разумный stop_grace_period и время закрытия |
#Пошаговая диагностика и устранение
- 1. Найдите точный контейнерНа машине с Docker откройте терминал, выполните список работающих контейнеров и найдите имя нужного приложения. Сверьте столбцы STATUS и NAMES; если контейнер уже остановлен, исследуйте события старого запуска, а не останавливайте повторно.Linux · Терминал (Bash)КомандаТерминал Linux/macOS или PowerShell с доступом к Docker Engine; чтение без изменения.
docker ps --format "table {{.Names}} {{.Status}} {{.Image}}"
- 2. Проверьте, какой процесс получает сигналВыполните inspect, заменив ИМЯ на найденное имя. Посмотрите Path, Args, StopSignal и StopTimeout: если запуск указан через оболочку, проверьте Dockerfile и сценарий запуска в репозитории; ничего в контейнере пока не изменяйте.
- На Linux и Docker Desktop; замените ИМЯ названием контейнераLinux · Терминал (Bash)КомандаНа Linux и Docker Desktop; замените ИМЯ названием контейнера.
docker inspect --format "{{.Path}} {{json .Args}} | signal={{.Config.StopSignal}} timeout={{.HostConfig.StopTimeout}}" ИМЯ
- 3. Изучите завершение в журналеОткройте последние записи; найдите сообщения о получении SIGTERM, закрытии сокетов и окончании операций. Отсутствие записей само по себе не доказывает потерю сигнала — обработчик мог не вести журнал.
- Логи могут содержать личные данные — перед передачей удалите секретыLinux · Терминал (Bash)КомандаЛоги могут содержать личные данные — перед передачей удалите секреты.
docker logs --tail 150 --timestamps ИМЯ
- 4. Проверьте поведение в тестовом контейнереСоздайте копию окружения с отдельными данными и отправьте штатное завершение. Засеките время, сопоставьте его с настройкой таймаута; рабочие пользователи и операции в этот момент не должны зависеть от тестового контейнера.
- Только на тестовом контейнере; через 30 секунд возможен SIGKILLLinux · Терминал (Bash)С осторожностьюТолько на тестовом контейнере; через 30 секунд возможен SIGKILL.
docker stop --timeout 30 ИМЯ_ТЕСТКоманда может изменить состояние системы или её настройки. Проверьте параметры и последствия перед запуском.
- 5. Исправьте передачу сигналаВ Dockerfile образа вместо shell-формы CMD node server.js используйте exec-форму CMD ["node", "server.js"] для соответствующего приложения. Если необходим стартовый сценарий shell, завершая его, используйте exec "$@" или exec ./app после подготовительных действий; пересоберите тестовый образ.
- Пример только для Node.js; замените команду на реальный исполняемый файлТекстПример только для Node.js; замените команду на реальный исполняемый файл.
CMD ["node", "server.js"]
- 6. Настройте ожидание, если приложению нужно времяЕсли журнал подтвердил корректную, но долгую остановку, в Compose-файле для сервиса допускается stop_grace_period: 60s. Не ставьте бесконечное ожидание: сначала убедитесь, что приложение реагирует на сигнал.
- Фрагмент внутри описания сервиса Compose; влияет на время завершенияТекстФрагмент внутри описания сервиса Compose; влияет на время завершения.
stop_grace_period: 60s
- 7. Повторите проверку и сравнитеЗапустите обновлённый тестовый сервис, остановите его штатно и проверьте журналы. Успех — процесс сохраняет данные и прекращает работу до таймаута без принудительного убийства. Если нет, установите обработчик SIGTERM в приложении и исследуйте блокирующие запросы.
#Как проверить результат
Контейнер завершает работу без принудительного SIGKILL, все запросы корректно закрываются, после запуска данные целы.
#Если исправление не помогло
Если процесс зависает, соберите обезличенный журнал остановки, стек зависших обработчиков и трассировку процесса в тестовом окружении. Не применяйте kill -9 к рабочей базе.