Запропонувати правкуПокращити цю статтюДопрацюйте відповідь до «Що таке lifecycle контейнера Docker?». Ваші зміни проходять модерацію перед публікацією.Потрібне підтвердженняКонтентЩо ви змінюєте🇺🇸EN🇺🇦UAПереглядЗаголовок (UA)Коротка відповідь (UA)**Lifecycle Docker container** це набір станів, через які проходить container: created → running → (paused) → exited → removed. Кожен перехід тригериться конкретною командою, а `docker ps -a` показує поточний стан кожного container. ``` created → running → exited → removed ↕ paused ``` ```bash docker create / docker run # → created → running docker pause / unpause # ↔ paused docker stop / kill # → exited docker rm # → removed ``` **Головне:** exited container ще існує (можна `docker start` знов). Тільки `docker rm` його реально видаляє. Exit code у `docker ps -a` каже, чому container зупинився (0 = чисто, 137 = SIGKILL, 143 = SIGTERM, 1 = помилка).Показується над повною відповіддю для швидкого нагадування.Відповідь (UA)Зображення**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) | | 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 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'їв би вічно.Для рев’юераПримітка для модератора (необов’язково)Бачить лише модератор. Прискорює рев’ю.