Как 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
— код завершения.OOMKilled
—true
, если контейнер был убит из-за нехватки памяти.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 и т.д.
Понимание этих механизмов позволяет эффективно управлять контейнерами и быстро реагировать на сбои.