Що таке lifecycle контейнера Docker?
Lifecycle Docker container описує, як container переходить між станами від створення до видалення. Розуміння діаграми (і що кожна команда з нею робить) відрізняє людину, яка запускає container, від людини, яка їх дебажить.
Теорія
TL;DR
- Шість станів: created, running, paused, restarting, exited, dead, плюс неявний термінальний removed.
docker run=docker create+docker start(created → running одним махом).docker stopшле SIGTERM, чекає 10 секунд, тоді SIGKILL. Container переходить у exited.- Exited container ще на диску.
docker startйого оживляє.docker rmнарешті видаляє. - Exit code (видно у колонці
Statusdocker ps -a) каже, чому: 0 = чистий вихід, 137 = SIGKILL, 143 = SIGTERM, 1 = помилка з застосунку. - Restart policy (
--restart) автоматизує частину цього:unless-stoppedозначає «якщо exited, daemon рестартує (якщо ти не зупинив вручну)».
Діаграма станів
docker create docker start
│ │
▼ ▼
┌─────────┐ docker run ┌─────────┐ docker stop / kill ┌────────┐
│ created │ ──────────────► │ running │ ────────────────────► │ exited │
└─────────┘ └─────────┘ застосунок вийшов сам └────────┘
▲ │ │
│ │ │
│ ▼ docker pause ▼
│ ┌────────┐ docker rm
│ │ paused │ │
│ └────────┘ ▼
│ │ docker unpause ┌─────────┐
└─────┘ │ removed │
└─────────┘
┌────────────┐
│ restarting │ (тимчасовий, працює restart policy)
└────────────┘
┌──────┐
│ dead │ (термінальний, daemon не зміг прибрати)
└──────┘Кожен стан з командами, що в нього ведуть
created
Container існує у БД daemon з config + готовою файловою системою, але жоден процес не запущений.
$ docker create --name foo alpine sleep 3600
2a3b4c5d...
$ docker ps -a --filter name=foo
CONTAINER ID STATUS NAMES
2a3b4c5d... Created fooКорисно, коли хочеш inspect/edit config перед стартом (docker network connect, прикріпити volume, поставити env). Більшість людей це пропускає і використовує docker run напряму.
running
Головний процес container (PID 1 всередині) живий. CPU споживається, пам'ять виділена, мережа активна.
$ docker start foo # з created
$ docker run -d alpine sleep 3600 # одразу running (без ручного create)paused
Процеси container заморожені через freezer cgroup kernel, вони існують у пам'яті, але не плануються. Розморожуються рівно там, де були.
$ docker pause foo
$ docker ps --filter name=foo
STATUS
Up 5 minutes (Paused)
$ docker unpause fooРідко на практиці; іноді для snapshot або швидкого CPU-offload.
exited
Головний процес зупинився (чисто або інакше). Container ще існує на диску; його writable-шар цілий. Можна рестартувати.
$ docker stop foo
$ docker ps -a --filter name=foo
STATUS NAMES
Exited (0) 3 seconds ago foo # 0 = чистий вихід
$ docker start foo # знов runningExit code, що зустрічаються у реальному житті:
| Код | Значення |
|---|---|
| 0 | Чистий вихід (твій застосунок повернув 0) |
| 1 | App-level помилка (застосунок кинув і вийшов 1) |
| 137 | SIGKILL, зазвичай docker kill, OOM або docker stop після grace |
| 143 | SIGTERM, чисте завершення без exit 0 |
| 139 | Segfault (SIGSEGV), бінар крашнувся |
| 130 | SIGINT (Ctrl+C), інтерактивний run перервано |
restarting
Перехідний стан. Restart-policy спрацювала і daemon стартує container знов.
Status: Restarting (1) 4 seconds ago
Якщо бачиш це довго, твій застосунок у crash-loop (стартує → виходить non-zero → рестарт → повтор). Глянь docker logs для причини.
dead
Термінальний failure. Daemon не зміг перевести container у чистий стан (corrupt filesystem-шар, kernel-проблема). Рідко.
Status: Dead
Зазвичай docker rm -f і перестворити. Якщо повторюється, дивися логи daemon.
removed
Не стан daemon, як тільки docker rm спрацював, container зникає з daemon повністю. docker ps -a його більше не показує.
Що насправді робить docker stop
t=0 daemon шле SIGTERM на PID 1 всередині container
t=0..10 застосунок має зловити SIGTERM і чисто завершитися
t=10 якщо ще running, daemon шле SIGKILL
exitedGrace-період 10 секунд налаштовується: docker stop -t 30 mycontainer для довшого чекання. Застосунки, що ігнорують SIGTERM, отримують жорсткий kill, exit 137, без шансу зберегти стан. Завжди перехоплюй SIGTERM у прод-застосунках.
Restart-policy як автоматизація
Флаг --restart каже daemon застосовувати переходи за тебе:
--restart=no # дефолт: нічого не робити на exit
--restart=on-failure # рестартити лише якщо exit != 0
--restart=always # рестартити завжди (навіть після reboot host)
--restart=unless-stopped # рестартити, якщо ТИ не зупинив вручнуЗ unless-stopped daemon оживляє крашнуті container автоматично; ручний docker stop тримає їх зупиненими (daemon поважає намір користувача).
Типові помилки
Думати, що docker stop видаляє container
$ docker stop web
$ docker run -d --name web nginx:1.27 # помилка: ім'я зайнятеstop лише зупиняє процес. Ім'я container і writable-шар лишаються. Щоб видалити, бери docker rm (або docker rm -f для stop+remove одразу).
Дозволяти exited container накопичуватися
$ docker ps -a | wc -l
247За замовчуванням exited container лишаються назавжди. Або --rm на docker run (auto-clean на exit), або періодичний docker container prune.
Інтерпретувати exit 137 як «daemon вбив»
137 = 128 + 9 (SIGKILL). Причина може бути:
- Твій
docker killабоdocker stop(після timeout) - Linux OOM killer (досягнуто memory cgroup-ліміту)
- Host вичерпав диск і daemon вбиває щось
Глянь docker inspect <container> --format '{{.State.OOMKilled}}', true означає OOM, не external kill.
Плутати crash-loop з restart-policy спамом
Status: Restarting (1) 2 seconds ago # повторюється
Restart-policy тримає retry, поки не зупиниш. Якщо застосунок зламано, отримуєш нескінченний цикл з логами, що швидко ростуть. docker logs --tail 50 <container>, щоб побачити, що падає.
Реальне застосування
- CI білди:
docker run --rmдля build-container. Стан явно задизайнено як «запустити, вийти, зникнути». - Довгоживучі сервіси:
docker run -d --restart=unless-stopped. Container проходить running → restarting → running протягом життя, поки застосунок крашиться і відновлюється. - Debugging:
docker exec -it <name> sh, поки running, абоdocker start -ai <name>, щоб оживити exited container з прикріпленим stdin/stdout. - Cleanup hooks:
docker container prune --filter 'until=24h'видаляє все уexitedпонад день.
Питання для поглиблення
Q: Чи використовує paused container CPU?
A: Фактично ні, його процеси не на runqueue. Використовує пам'ять (стан процесу збережено). Pause ближче до «заморожено», ніж «спить».
Q: Яка різниця між docker stop і docker kill?
A: stop graceful: SIGTERM, 10-секундний grace, тоді SIGKILL. kill миттєвий: SIGKILL одразу (або сигнал, що задаси через --signal). Stop для чистих завершень; kill для зависнутих процесів.
Q: Чи можу я побачити історію lifecycle container?
A: docker inspect <container> --format '{{json .State}}' показує деталі поточного стану (started/finished timestamps, exit code, OOM flag, error message). Для повної історії подій docker events показує live-переходи; деякі сетапи це логують.
Q: Що відбувається зі змінами writable-шару, коли container виходить і рестартується?
A: Зберігаються. Writable-шар переживає, поки не зробиш docker rm. Рестарт exited container піднімає його рівно з того місця, де зупинився (filesystem-wise; in-memory стан, звичайно, зник).
Q: (Senior) Як lifecycle pod Kubernetes співвідноситься з Docker?
A: Kubernetes обгортає Docker (чи будь-який OCI-runtime) своєю state-machine: Pending → Running → Succeeded/Failed/Unknown. Кожен container всередині pod все ще має Docker container lifecycle під собою. K8s контролери (Deployment, StatefulSet) стежать за станом pod і замінюють failed pod; Docker restart policy теж іноді використовується, але K8s зазвичай володіє цим рішенням через probes і controller restart-policy.
Приклади
Прохід container через всі стани
$ docker create --name demo alpine sleep 60
# Status: Created
$ docker start demo
# Status: Up 2 seconds
$ docker pause demo
# Status: Up 5 seconds (Paused)
$ docker unpause demo
# Status: Up 8 seconds
$ docker stop demo
# Status: Exited (137) 0 seconds ago
# (sleep ігнорив SIGTERM; вбито через 10с)
$ docker start demo # exited → running знов
# Status: Up 1 second
$ docker rm -f demo
# Container зник з docker ps -aУвесь граф у семи командах.
Використання exit code для дебагу
$ docker run --name flake myapp
$ docker ps -a --filter name=flake --format '{{.Status}}'
Exited (137) 12 seconds ago
$ docker inspect flake --format '{{.State.OOMKilled}}'
true
# OOM killer спрацював. Memory-ліміт (або відсутність + жадібний застосунок) це причина.
# vs
$ docker inspect flake --format '{{.State.OOMKilled}} {{.State.Error}}'
false
# Не OOM. Скоріш за все явний kill або зовнішня причина.Exit code + OOM flag + error message разом зазвичай дають точну відповідь, що сталося.
Restart-policy у дії
# Container, що одразу крашиться
$ docker run -d --name flapper --restart=on-failure:5 alpine sh -c 'exit 1'
$ docker ps -a --filter name=flapper
STATUS NAMES
Restarting (1) Less than a second ago flapper
# Після 5 спроб daemon здається
$ docker ps -a --filter name=flapper
STATUS NAMES
Exited (1) 30 seconds ago flapperon-failure:5 обмежує retry до 5. Без cap daemon retry'їв би вічно.
Коротка відповідь
Для співбесідиКоротка відповідь допоможе вам впевнено відповідати на цю тему під час співбесіди.
Коментарі
Ще немає коментарів