Жизненный цикл контейнера
Чтение и практика ~18 мин
Ситуация
Вы запустили контейнер, а через минуту docker ps его не показывает. Он удалён? Упал? Его кто-то остановил?
Где теперь искать причину?
Главная мысль
Контейнер живёт, пока работает его главный процесс (PID 1). Процесс завершился — контейнер переходит
в состояние Exited с кодом завершения этого процесса. Остановленный контейнер не исчезает: его журнал,
файлы и код завершения остаются, пока вы его не удалите.
docker run ──▶ Up (работает) ──процесс завершился / docker stop──▶ Exited (код)
▲ │
└───────────────── docker start ─────────────────┘
docker rm ──▶ удалён
Новые слова
docker ps— работающие контейнеры;docker ps -a— все, включая остановленные.docker logs— журнал контейнера: всё, что главный процесс написал в stdout и stderr.docker exec— выполнить ещё одну команду в работающем контейнере.- Код завершения — число, с которым закончил главный процесс:
0— успех, другое — ошибка или сигнал.137= 128 + 9: процесс убит сигналомSIGKILL;143= 128 + 15: завершён поSIGTERM.
Разбор: учебный веб-сервер
Команды выполняются на учебной машине от root. Все имена — с префиксом rpd-.
Запуск и список
В Python есть простой веб-сервер — модуль http.server. Запустим его в фоне (-d) с именем rpd-web.
Ключ -u отключает буферизацию вывода Python — зачем, станет ясно в разделе про журнал.
docker run -d --name rpd-web python:3.12-slim python3 -u -m http.server 8000
sleep 1
docker ps --filter name=rpd-
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
842d741fbb22 python:3.12-slim "python3 -u -m http.…" 2 seconds ago Up 1 second rpd-web
--filter name=rpd- показывает только учебные контейнеры. Столбец PORTS пуст: снаружи к серверу пока не
подключиться — это тема главы 4.
Журнал и команда внутри
docker logs rpd-web
Serving HTTP on 0.0.0.0 port 8000 (http://0.0.0.0:8000/) ...
Сделаем запрос изнутри контейнера. В образе нет curl, но есть Python:
docker exec rpd-web python3 -c "import urllib.request; print(urllib.request.urlopen('http://localhost:8000/').status)"
200
docker logs rpd-web
Serving HTTP on 0.0.0.0 port 8000 (http://0.0.0.0:8000/) ...
127.0.0.1 - - [16/Oct/2026 11:51:21] "GET / HTTP/1.1" 200 -
Запрос виден в журнале. localhost внутри контейнера — это сам контейнер.
Следить за журналом в реальном времени — docker logs -f rpd-web, выход — Ctrl+C (контейнер при этом
продолжит работать). Последние строки — docker logs --tail 20 rpd-web.
Остановка и код завершения
Предскажите, сколько займёт остановка.
time docker stop rpd-web
rpd-web
real 0m10.160s
Десять секунд — не случайность. docker stop посылает главному процессу SIGTERM и ждёт 10 секунд. Процесс
с номером 1 по правилам ядра игнорирует сигналы, для которых у программы нет обработчика, — а у http.server
его нет. Через 10 секунд Docker посылает SIGKILL, который не игнорируется:
docker ps -a --filter name=rpd-
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
842d741fbb22 python:3.12-slim "python3 -u -m http.…" 12 seconds ago Exited (137) Less than a second ago rpd-web
Exited (137) — убит SIGKILL. Приложение курса (глава 3) обрабатывает SIGTERM и останавливается сразу
с кодом 0. Код можно получить и отдельно:
docker inspect -f '{{.State.Status}} {{.State.ExitCode}}' rpd-web
exited 137
Снова запустить — тот же контейнер
docker start rpd-web
sleep 1
docker logs rpd-web
start запускает тот же контейнер с той же командой. В журнале остались строки прошлого запуска — журнал
принадлежит контейнеру, а не запуску. docker run каждый раз создал бы новый контейнер.
Контейнер, который сразу завершается
docker run --name rpd-fail python:3.12-slim python3 -c "import sys; print('нет файла настроек /etc/app.conf'); sys.exit(3)"
нет файла настроек /etc/app.conf
Без -d вывод идёт прямо в терминал, а команда docker run сама завершается с кодом процесса (echo $? → 3).
docker ps -a --filter name=rpd- --format '{{.Names}} {{.Status}}'
rpd-fail Exited (3) Less than a second ago
rpd-web Up Less than a second
В остановленном контейнере exec не работает:
docker exec rpd-fail ls
Error response from daemon: container df2aa82c148f… is not running
Но журнал читается: docker logs rpd-fail покажет то же сообщение.
Уборка
docker rm -f rpd-web rpd-fail
Без -f работающий контейнер не удалится: cannot remove container "rpd-web": container is running.
Практика: почему контейнер не работает
Запустите «сломанный» контейнер:
docker run -d --name rpd-broken python:3.12-slim python3 -c "open('/etc/app.conf')"
Команда напечатала идентификатор — кажется, всё в порядке. Выясните, ничего не меняя:
- Работает ли контейнер сейчас?
- С каким кодом он завершился?
- Какая строка журнала называет причину?
Затем удалите его.
Признак успеха: три ответа, для каждого — команда, которая его показала; docker ps -a --filter name=rpd- пуст.
Подсказка 1
В обычном docker ps его нет. Какой ключ показывает и остановленные контейнеры?
Подсказка 2
Код виден в столбце STATUS у docker ps -a и в docker inspect -f '{{.State.ExitCode}}' ….
Подсказка 3
Причину ищут в docker logs rpd-broken — последняя строка трассировки Python.
Решение — в answers/02-lifecycle.md.
Если результат другой
docker logsпустой, хотя программа точно что-то печатает. Python буферизует вывод, когда пишет не в терминал, и сбрасывает его поздно. Запустите сpython3 -uили задайте переменнуюPYTHONUNBUFFERED=1. Приложение курса печатает с немедленным сбросом.Conflict. The container name "/rpd-web" is already in use. Контейнер с этим именем уже есть, может быть остановленный:docker ps -a --filter name=rpd-web. Нужен прежний —docker start rpd-web; не нужен — удалите.docker stopждёт 10 секунд. Главный процесс не обрабатываетSIGTERM(см. разбор). Это не ошибка Docker; лечится в самом приложении или запуском с--init.
Проверка понимания
- Контейнер в
docker ps -aсо статусомExited (0). Он упал? - Чем
docker start rpd-webотличается от повторногоdocker run --name rpd-web …?
Ответы — в answers/02-lifecycle.md.
Дальше: 3. Dockerfile и контекст сборки.
Проверка автора и источники
- Воспроизведено: все команды разбора и практики — Docker Engine 29.8.2, образ-заменитель
python:3.12-slimс Python 3.12.3 (см. карту курса). Задержкаdocker stop10 секунд и код 137, буферизация вывода без-u(журнал без строкиServing HTTP…), сохранение журнала послеstart— проверены. Идентификаторы и время — из этого запуска. Таблицыdocker psпоказаны как есть; на узком экране они переносятся. - Источники: docker container stop (SIGTERM, затем
SIGKILL через 10 секунд), docker container logs,
docker container exec,
docker run:
--init, Python-u.
Разбор и ответы — откройте после своей попытки
Практика
1. Работает ли?
docker ps --filter name=rpd-broken
Таблица пустая — не работает. Остановленные показывает -a:
docker ps -a --filter name=rpd-broken --format '{{.Names}} {{.Status}}'
rpd-broken Exited (1) 1 second ago
2. Код завершения — 1 (в столбце STATUS или docker inspect -f '{{.State.ExitCode}}' rpd-broken).
3. Причина:
docker logs rpd-broken
Traceback (most recent call last):
File "<string>", line 1, in <module>
FileNotFoundError: [Errno 2] No such file or directory: '/etc/app.conf'
Главный процесс попытался открыть файл, которого нет в образе, и завершился с ошибкой. Контейнер жил ровно столько, сколько процесс.
Уборка: docker rm rpd-broken — контейнер уже остановлен, -f не нужен.
Воспроизведено: Docker Engine 29.8.2, образ-заменитель python:3.12-slim.
Проверка понимания
- Нет. Код
0— главный процесс завершился успешно: например, выполнил свою работу и вышел, или обработалSIGTERMприdocker stop. Упал бы с ненулевым кодом. Чтобы понять, что было, —docker logs. startзапускает существующий контейнер: те же настройки, тот же журнал, те же изменения файлов в его слое.runсоздаёт новый контейнер из образа; с тем же именем не получится, пока старый не удалён, — Docker ответитConflict.
Прочитали и разобрались? Отметьте урок — прогресс сохранится в этом браузере.