Skip to main content

Що таке 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 (видно у колонці Status docker 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 + готовою файловою системою, але жоден процес не запущений.

bash
$ 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 споживається, пам'ять виділена, мережа активна.

bash
$ docker start foo # з created $ docker run -d alpine sleep 3600 # одразу running (без ручного create)

paused

Процеси container заморожені через freezer cgroup kernel, вони існують у пам'яті, але не плануються. Розморожуються рівно там, де були.

bash
$ docker pause foo $ docker ps --filter name=foo STATUS Up 5 minutes (Paused) $ docker unpause foo

Рідко на практиці; іноді для snapshot або швидкого CPU-offload.

exited

Головний процес зупинився (чисто або інакше). Container ще існує на диску; його writable-шар цілий. Можна рестартувати.

bash
$ docker stop foo $ docker ps -a --filter name=foo STATUS NAMES Exited (0) 3 seconds ago foo # 0 = чистий вихід $ docker start foo # знов running

Exit code, що зустрічаються у реальному житті:

КодЗначення
0Чистий вихід (твій застосунок повернув 0)
1App-level помилка (застосунок кинув і вийшов 1)
137SIGKILL, зазвичай docker kill, OOM або docker stop після grace
143SIGTERM, чисте завершення без exit 0
139Segfault (SIGSEGV), бінар крашнувся
130SIGINT (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 exited

Grace-період 10 секунд налаштовується: docker stop -t 30 mycontainer для довшого чекання. Застосунки, що ігнорують SIGTERM, отримують жорсткий kill, exit 137, без шансу зберегти стан. Завжди перехоплюй SIGTERM у прод-застосунках.

Restart-policy як автоматизація

Флаг --restart каже daemon застосовувати переходи за тебе:

bash
--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

bash
$ docker stop web $ docker run -d --name web nginx:1.27 # помилка: ім'я зайняте

stop лише зупиняє процес. Ім'я container і writable-шар лишаються. Щоб видалити, бери docker rm (або docker rm -f для stop+remove одразу).

Дозволяти exited container накопичуватися

bash
$ 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 через всі стани

bash
$ 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 для дебагу

bash
$ 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 у дії

bash
# 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 flapper

on-failure:5 обмежує retry до 5. Без cap daemon retry'їв би вічно.

Коротка відповідь

Для співбесіди
Premium

Коротка відповідь допоможе вам впевнено відповідати на цю тему під час співбесіди.

Дочитали статтю?