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

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

Коментарі

Ще немає коментарів