У чому різниця між docker stop та docker kill?
docker stop і docker kill обидва способи зупинити running container, але відрізняються за агресивністю. Вибір між ними переважно про те, чи застосунку всередині потрібен час на чисту зупинку.
Теорія
TL;DR
docker stop= graceful. Шле SIGTERM, чекає N секунд (дефолт 10), шле SIGKILL, якщо ще running.docker kill= негайний. Шле SIGKILL за замовчуванням, або будь-який сигнал через--signal.- Обидва врешті змушують container вийти. Різниця в тому, чи отримує застосунок шанс прибрати.
- Для баз, сервісів зі станом, чого-небудь з відкритими з'єднаннями, бери
stop. - Для зависнутих процесів, застосунків з unresponsive PID 1, debug,
kill. docker rm -f=docker kill+docker rmв одному кроці.
Що робить docker stop
t=0 daemon шле SIGTERM на PID 1 всередині container
t=0..N застосунок має обробити SIGTERM:
- закінчити in-flight запити
- flush логів / WAL / кешу
- закрити DB-з'єднання
- чисто вийти з кодом 0
t=N якщо ще running, daemon шле SIGKILL
(де N = --time, дефолт 10 секунд)Grace-період налаштовується per stop:
docker stop -t 30 mycontainer # 30 секунд
docker stop -t 0 mycontainer # 0 = SIGKILL негайно (як kill)Або per container при run:
docker run --stop-timeout 60 myappЩо робить docker kill
docker kill <name> # SIGKILL за замовчуванням
docker kill --signal=SIGTERM <name> # явний SIGTERM (без grace-вікна)
docker kill --signal=SIGUSR1 <name> # будь-який сигнал
docker kill -s 9 <name> # числовий сигналФлаг --signal робить docker kill гнучкішим, ніж його ім'я підказує. Це також спосіб надіслати non-fatal сигнал running-застосунку, наприклад, сказати nginx перечитати config:
docker kill --signal=SIGHUP nginx-container
# nginx перечитує config без рестарту.Незважаючи на ім'я, kill не завжди вбиває, лише SIGKILL і SIGTERM (без trap) завершують процес.
Side-by-side
docker stop | docker kill | |
|---|---|---|
| Дефолтний сигнал | SIGTERM, потім SIGKILL | SIGKILL |
| Grace-період | так (дефолт 10с, налаштовується) | жодного |
| Кастомний сигнал | ні | так (--signal) |
| Для чистого shutdown | ТАК | ні |
| Для зависнутих процесів | ні | ТАК |
| Для надсилання сигналів (HUP, USR1) | ні | ТАК |
Чому graceful важить
# Postgres з --memory тиском у фоні
docker stop pg # SIGTERM → postgres flush WAL, закриває з'єднання, exit 0 → усе ок
docker kill pg # SIGKILL → postgres помирає мід-write → на наступному старті WAL-replay → потенційно повільне recoveryДля баз: stop, ніколи не kill (якщо не свідомо).
# Web-сервіс з in-flight запитами
docker stop api # SIGTERM → api припиняє приймати нові, добиває існуючі, exit → без client-помилок
docker kill api # SIGKILL → з'єднання падають мід-flight → 502 для юзераДля user-facing сервісів: stop. Завжди.
Коли kill правильний
- Зависнутий процес, що ігнорує SIGTERM і блокує усе. Stop чекав; kill закінчує.
- Надсилання control-сигналів як SIGHUP (reload), SIGUSR1 (rotate logs), SIGUSR2 (debug dump) running-застосунку.
- Тести / disposable container, де graceful shutdown не важить.
docker rm -fвикористовує kill внутрішньо, нормально для cleanup уже-зупинених або таких, що не важили, container.
Типові помилки
Вбивати базу мід-write
# НЕПРАВИЛЬНО
docker kill postgres-prod
# Іноді recovers нормально; іноді корумповані індекси; іноді WAL-replay 30 хвилин.Бери docker stop. Ще краще: спочатку чисто зупини застосунок, потім зупини container.
Застосунок ігнорує SIGTERM, тоді отримує SIGKILL через 10с
$ docker stop web
# Виглядає повільно; бере усі 10 секунд; container виходить з 137.Застосунок не обробляє SIGTERM = брудний shutdown після grace-періоду. Фікс застосунку: trap SIGTERM і виходь чисто. Тестуй через docker stop і підтверджуй exit 0 (або який чистий код) за пару секунд.
Брати kill для --signal=SIGTERM замість просто stop
# Те саме, що docker stop -t 0 (без grace-вікна)
docker kill --signal=SIGTERM webЦе шле SIGTERM, але НЕ супроводжує SIGKILL'ом після grace-періоду, якщо застосунок ігнорує сигнал, container лишається running. Зазвичай хочеш docker stop, що ДОДАЄ SIGKILL.
Забути, що PID 1 має особливу signal-семантику
Linux PID 1 ігнорує більшість сигналів за замовчуванням, поки процес явно їх не обробляє. Якщо твій застосунок це PID 1 (так, у container), маєш trap SIGTERM у коді:
// Node.js приклад
process.on('SIGTERM', () => {
server.close(() => process.exit(0));
});Без цього trap docker stop чекає повний grace-період і шле SIGKILL.
Реальне застосування
- Прод-деплої:
docker stop(або еквівалент stack-level оркестратора) для graceful shutdown. - CI cleanup:
docker rm -f $(docker ps -aq)(kill + rm) для тестів, де graceful не важить. - Reload config nginx:
docker kill --signal=SIGHUP nginx. - Ротація логів:
docker kill --signal=USR1 myapp, щоб застосунок reopen log-файли. - Hung-container debug:
docker killпісля того, якdocker stopчекав повний grace-період безуспішно.
Питання для поглиблення
Q: Який дефолтний grace-період і як його змінити?
A: 10 секунд. Override per-stop через docker stop -t N, per-container через docker run --stop-timeout N, або у Compose через stop_grace_period: 30s.
Q: Чи daemon retry'їть SIGTERM протягом grace-періоду?
A: Ні. SIGTERM шлеться раз. Якщо застосунок його ігнорує, daemon чекає і потім SIGKILL.
Q: Що означають exit code 137 vs 143?
A: 137 = 128 + 9 (SIGKILL). 143 = 128 + 15 (SIGTERM). Container вийшов через відповідний сигнал. Якщо docker stop і бачиш 137, застосунок не обробив SIGTERM і його SIGKILL'нули після grace-періоду.
Q: Чи можу надіслати сигнал, що running-процес отримає?
A: Так, docker kill --signal=<SIG> <container> шле сигнал на PID 1 всередині. Чи робить застосунок з ним щось, залежить від коду застосунку (signal-handlers).
Q: (Senior) Як забезпечити, що твій dockerized застосунок правильно обробляє SIGTERM?
A: Три перевірки. (1) Код: встанови signal-handlers у entrypoint застосунку, що trap SIGTERM і починають graceful shutdown. (2) Init: уникай обгортання у shell-форму /bin/sh -c, це робить sh PID 1 і твій застосунок дитиною, що не отримує сигнал напряму. Бери exec-форму (CMD ["node", "server.js"]) або tini через --init. (3) Тестуй: docker stop свого container і assert, що він виходить за ~1 секунду з кодом 0 (або твоїм designated-чистим кодом), не після повного grace-періоду.
Приклади
Graceful-stop з продовженим grace-періодом
# Прод-DB потребує час на flush
$ docker stop -t 60 postgres-prod
postgres-prod
# Postgres обробляє SIGTERM, крутить checkpoint, виходить чисто. Зайняло ~12 секунд.Довгий graceful-shutdown для stateful-сервісу. Швидше, ніж дозволив би дефолтний 10с timeout.
Reload nginx-config без рестарту
$ docker kill --signal=SIGHUP nginx-prod
nginx-prod
# nginx отримує HUP, перечитує config з /etc/nginx/nginx.conf, продовжує крутитися.
# Усі існуючі з'єднання продовжуються без переривань.Класичне використання docker kill для надсилання non-fatal control-сигналу.
Діагностика зависнутого container
# Застосунок ні на що не реагує
$ docker stop -t 5 hung-container
# Чекає 5с, шле SIGKILL.
# Або скіпни чекання повністю:
$ docker kill hung-containerДля container, що застряг, kill правильний tool, graceful марне чекання.
Compose grace-period налаштування
services:
api:
image: myapp
stop_grace_period: 30s # docker compose stop чекатиме до 30с
stop_signal: SIGUSR1 # бери SIGUSR1 замість SIGTERM (рідко)Compose дозволяє кастомізувати per service. Для більшості застосунків дефолт нормально; для slow-shutting-down DB або worker подовжуй.
Коротка відповідь
Для співбесідиКоротка відповідь допоможе вам впевнено відповідати на цю тему під час співбесіди.
Коментарі
Ще немає коментарів