DockerMiddleLive coding
Контейнер в Restarting бесконечно. Алгоритм действий?
Читай логи через docker logs --tail 100, смотри ExitCode через docker inspect, временно отключи restart (docker update --restart=no), запусти с переопределённым entrypoint для интерактивной диагностики.
Алгоритм диагностики контейнера в Restarting-петле
Состояние Restarting означает, что контейнер запускается, его главный процесс завершается с ненулевым кодом (или с нулевым, если политика restart не on-failure), и Docker перезапускает его снова. Действуем по шагам.
Шаг 1 — Читаем логи до краша
# Логи последнего (неудавшегося) запуска
docker logs --tail 100 <container>
# Следим в реальном времени, пока контейнер перезапускается
docker logs -f <container>
# Узнать код выхода и дату последнего краша
docker inspect <container> | jq '.[0].State'
# => {"ExitCode": 1, "Error": "", "StartedAt": "...", "FinishedAt": "..."}
Шаг 2 — Временно отключаем автоперезапуск
# Меняем политику на "no", чтобы контейнер остался в Exited
docker update --restart=no <container>
docker stop <container>
Теперь контейнер в состоянии Exited — его можно инспектировать без гонки.
Шаг 3 — Запускаем с переопределённой точкой входа
# Запускаем shell вместо основного процесса
docker run --rm -it --entrypoint sh my-image:latest
# Если shell нет (distroless) — используем nsenter или debug-образ
docker run --rm -it --entrypoint /busybox/sh gcr.io/distroless/python3:debug
Внутри проверяем: существуют ли нужные файлы, корректны ли переменные окружения, доступны ли сетевые зависимости.
Шаг 4 — Проверяем типичные причины
- Отсутствует переменная окружения: приложение падает с
KeyErrorилиpanic: ... not set. Решение —--env-fileили Secret в Compose. - БД не готова: контейнер стартует раньше postgres/redis. Решение —
depends_on: condition: service_healthyв Compose + healthcheck. - Порт занят: ошибка
bind: address already in use. Проверьdocker ps -aиlsof -i :<port>. - Ошибка монтирования: volume path не существует на хосте, или несовместимые права (
Permission denied). - OOM Kill: kernel убивает процесс из-за нехватки памяти. Проверь
docker inspect ... | jq '.[0].State.OOMKilled'. - Неверный ENTRYPOINT: CMD не находит исполняемый файл (exit code 127 — command not found, 126 — permission denied).
Шаг 5 — Healthcheck и depends_on
services:
db:
image: postgres:16
healthcheck:
test: ["CMD-SHELL", "pg_isready -U postgres"]
interval: 5s
timeout: 5s
retries: 10
app:
image: my-app:latest
depends_on:
db:
condition: service_healthy
restart: on-failure
Шаг 6 — Анализируем события Docker
docker events --filter container=<id> --since 30m
# Видим: die, start, die, start ... — петля подтверждена
Подводные камни
- Если restart policy = always, контейнер перезапускается даже при коде выхода 0 — это может замаскировать нормальное завершение как ошибку.
- docker logs для контейнера в Restarting может показывать логи только от последнего запуска — ранние ошибки теряются. Используй log driver с persistence (journald, loki).
- OOMKill не всегда виден в логах приложения — проверяй State.OOMKilled через docker inspect.
- Exit code 137 = SIGKILL (OOM или docker stop), 143 = SIGTERM, 1 = общая ошибка приложения — знание кодов ускоряет диагностику.
- depends_on без healthcheck только ждёт старта контейнера, а не готовности сервиса — это не защищает от race condition с БД.
- Монтирование директории хоста поверх директории образа (например, VOLUME /app) скрывает файлы образа пустой директорией — приложение не находит свои файлы.
- В Kubernetes аналог — CrashLoopBackOff; диагностика аналогична, но вместо docker logs используй kubectl logs --previous.
Common mistakes
- Сразу удалять контейнер и терять inspect/logs.
- Игнорировать restart policy and treating loop as root cause.
- Оставлять loop, который повторно выполняет dangerous migrations or jobs.
What the interviewer is testing
- Смотрит RestartCount, exit code, OOMKilled and logs.
- Проверяет command/env/mounts/dependencies.
- Умеет временно остановить loop для диагностики.