Skip to content

Как Docker определяет, работает ли контейнер или упал

Docker — мощная платформа для запуска приложений в изолированных средах, называемых контейнерами. Однако, как и любая система, контейнеры могут "падать" (завершаться с ошибкой, зависать или останавливаться). Важно понимать, как Docker определяет состояние контейнера — работает он, остановлен или аварийно завершился. Это критически важно для мониторинга, логирования и автоматического восстановления в production-средах.

1. Что значит "контейнер работает" или "свалился"?

Прежде чем углубляться в механизм, уточним терминологию:

  • Работающий контейнер (running) — контейнер активно выполняет процесс, указанный в CMD или ENTRYPOINT, и не завершился.
  • Остановленный контейнер (exited) — контейнер завершил выполнение (по любой причине).
  • "Свалился" (crashed) — разговорное выражение, означающее, что контейнер завершился аварийно (например, с ненулевым кодом возврата).

Docker не использует термин "свалился" напрямую, но может определить, было ли завершение штатным или нет.


2. Как Docker отслеживает состояние контейнера

Docker использует механизм процессов для отслеживания жизненного цикла контейнера. Основной принцип прост:

Контейнер живёт ровно столько, сколько живёт его основной процесс (PID 1).

2.1. PID 1 — главный процесс контейнера

При запуске контейнера Docker запускает один процесс, который становится процессом с PID 1 внутри контейнера. Именно этот процесс определяет судьбу контейнера.

Пример:

docker run nginx

Здесь nginx запускается как PID 1. Пока nginx работает — контейнер считается запущенным.

Если nginx аварийно завершится (например, из-за ошибки конфигурации), контейнер тоже остановится.

2.2. Код возврата процесса

Когда процесс с PID 1 завершается, он возвращает код возврата (exit code). Docker использует этот код для определения причины остановки:

  • 0 — успешное завершение.
  • Любой ненулевой код (например, 1, 137, 127) — ошибка.

Пример:

docker run alpine echo "Hello"

Команда echo выполнится и завершится с кодом 0. Контейнер остановится, но это штатное завершение.

А вот:

docker run alpine exit 1

Контейнер завершится с кодом 1 — Docker запишет это как ошибку.


3. Как посмотреть состояние контейнера

3.1. Команда docker ps

Показывает только запущенные контейнеры:

docker ps

3.2. Команда docker ps -a

Показывает все контейнеры, включая остановленные:

docker ps -a

Пример вывода:

CONTAINER ID   IMAGE     COMMAND       CREATED        STATUS                     PORTS       NAMES
abc123        nginx     "nginx -g ..."  5 min ago     Exited (0) 2 minutes ago             web-server
def456        alpine    "exit 1"        1 min ago     Exited (1) 10 seconds ago            broken-container

Здесь: - Первый контейнер завершился с кодом 0 — всё нормально. - Второй — с кодом 1 — ошибка.

3.3. Команда docker inspect

Даёт детальную информацию о контейнере:

docker inspect <container_id>

Ключевые поля:

"State": {
  "Status": "exited",
  "Running": false,
  "Paused": false,
  "Restarting": false,
  "OOMKilled": false,
  "Dead": false,
  "Pid": 0,
  "ExitCode": 1,
  "StartedAt": "2024-04-05T10:00:00Z",
  "FinishedAt": "2024-04-05T10:00:05Z"
}
  • ExitCode — код завершения.
  • OOMKilledtrue, если контейнер был убит из-за нехватки памяти.
  • FinishedAt — время остановки.

4. Почему контейнер может "свалиться"?

Docker сам не решает, что "сбой" — это плохо. Он просто фиксирует факт завершения и код. Причины могут быть разными:

Причина Как Docker это видит
Приложение завершилось штатно ExitCode: 0
Ошибка в коде приложения ExitCode: 1, 2, и т.д.
Не хватило памяти (OOM) OOMKilled: true, ExitCode: 137
Получен сигнал SIGKILL ExitCode: 137
Получен сигнал SIGTERM ExitCode: 143
Ошибка при запуске (например, команда не найдена) ExitCode: 127

🔍 Пример: ExitCode: 137 = процесс убит сигналом SIGKILL, часто из-за OOM (Out of Memory).


5. Мониторинг и автоматическое восстановление

Docker предоставляет политики перезапуска (restart policies), которые позволяют автоматически перезапускать контейнеры при падении:

docker run --restart=always nginx

Возможные значения: - no — не перезапускать (по умолчанию). - on-failure — перезапускать только при ненулевом коде. - always — всегда перезапускать, даже после ручной остановки. - unless-stopped — всегда, кроме случая, когда остановлен вручную.

Это позволяет строить отказоустойчивые системы.


6. Логи и диагностика

Чтобы понять, почему контейнер упал, нужно смотреть логи:

docker logs <container_id>

Если контейнер упал сразу после старта — логи покажут ошибку инициализации, отсутствие зависимостей, неправильные переменные окружения и т.д.


7. Важные нюансы

7.1. "Работает" ≠ "приложение живо"

Контейнер может быть в статусе running, но приложение внутри может "зависнуть" (например, не отвечать на запросы). Docker не проверяет работоспособность приложения, только факт наличия процесса PID 1.

👉 Для проверки "здоровья" приложения используют healthcheck.

7.2. Healthcheck — проверка работоспособности

В Dockerfile можно добавить:

HEALTHCHECK --interval=30s --timeout=3s --start-period=5s --retries=3 \
  CMD curl -f http://localhost/ || exit 1

Тогда docker inspect покажет:

"State": {
  "Status": "running",
  "Health": {
    "Status": "unhealthy",
    "FailingStreak": 3,
    "Log": [...]
  }
}

Теперь Docker знает, что процесс жив, но приложение нездорово.


Для надёжной эксплуатации важно: - Следить за ExitCode и логами. - Использовать restart policies. - Настроить HEALTHCHECK для контроля прикладного уровня. - Мониторить контейнеры через инструменты вроде Prometheus, Grafana, Portainer и т.д.

Понимание этих механизмов позволяет эффективно управлять контейнерами и быстро реагировать на сбои.