docker inspect и events
Третий debug-инструмент после logs и exec — это inspect. Это команда, которая выдаёт всё, что Docker знает о контейнере: статус, exit code, networks, volume mappings, environment, restart history. Возвращает JSON, и через --format можно вытащить конкретное поле.
Параллельно — docker events, stream событий в реальном времени. Полезно, когда контейнер постоянно рестартится, и хочешь понять, в какой момент он умирает.
jq, yq, JSON и YAML из командной строки
docker inspect basic
docker inspect pg
Выведет JSON-массив с одним объектом — структурой контейнера. Это огромный документ (200+ строк), внутри:
[
{
"Id": "5fa3d8e9b1c0...",
"Created": "2026-05-15T09:42:11.234Z",
"Path": "docker-entrypoint.sh",
"Args": ["postgres"],
"State": {
"Status": "running",
"Running": true,
"Paused": false,
"Restarting": false,
"OOMKilled": false,
"Dead": false,
"Pid": 12345,
"ExitCode": 0,
"Error": "",
"StartedAt": "2026-05-15T09:42:11.567Z",
"FinishedAt": "0001-01-01T00:00:00Z"
},
"Image": "sha256:...",
"RestartCount": 0,
"Config": {
"Env": ["POSTGRES_USER=de", "POSTGRES_PASSWORD=secret", ...]
},
"HostConfig": {
"Binds": [],
"RestartPolicy": {"Name": "no", "MaximumRetryCount": 0}
},
"NetworkSettings": {
"IPAddress": "172.17.0.2",
"Ports": {"5432/tcp": [{"HostIp": "0.0.0.0", "HostPort": "5432"}]}
},
"Mounts": [...]
}
]
Чтобы вытащить конкретное поле — --format с Go template:
docker inspect pg --format '{{.State.Status}}'
# running
docker inspect pg --format '{{.NetworkSettings.IPAddress}}'
# 172.17.0.2
docker inspect pg --format '{{.RestartCount}}'
# 0
docker inspect pg --format '{{.State.ExitCode}}'
# 0
--format — это Go-шаблон. Точка . — корневой объект, .State.Status — путь к полю. Если поле — массив, можно итерировать через range:
docker inspect pg --format '{{range .Mounts}}{{.Source}} -> {{.Destination}}{{"\n"}}{{end}}'
# /var/lib/docker/volumes/pg-data/_data -> /var/lib/postgresql/data
Или вывести JSON только нужной части через json:
docker inspect pg --format '{{json .State}}'
# {"Status":"running","Running":true,...}
Вспомнить точное название поля можно так: docker inspect pg | grep -i ip — поищет «ip» в JSON-выводе, покажет окружающие строки с правильным path. Дальше делаешь --format '{{.NetworkSettings.IPAddress}}'.
Самые полезные поля для debug
| Поле | Что говорит |
|---|---|
.State.Status | running, exited, paused, dead, restarting |
.State.ExitCode | Код выхода последнего процесса. 0 = ОК, не 0 = ошибка |
.State.OOMKilled | true если killed по OOM (out of memory) |
.State.Error | Текст ошибки запуска, если контейнер не стартанул |
.RestartCount | Сколько раз перезапускался по restart policy |
.State.StartedAt / .FinishedAt | Когда стартовал / упал |
.NetworkSettings.IPAddress | IP в default bridge |
.NetworkSettings.Networks | IP в каждой сети (для compose — там IP в user-defined network) |
.HostConfig.RestartPolicy | Политика рестарта: no/on-failure/always/unless-stopped |
.Mounts | Список volumes/bind mounts |
.Config.Env | Все env-переменные |
.Config.Cmd / .Config.Entrypoint | Точка входа |
Самый частый чек при «контейнер не работает»:
docker inspect <c> --format '{{.State.Status}} (exit {{.State.ExitCode}})'
# exited (exit 1)
docker inspect <c> --format '{{.State.Error}}'
# (empty if started OK, or "OCI runtime create failed: ...")
Inspect для образа
docker inspect работает и для образов:
docker inspect postgres:16 --format '{{.Config.Env}}'
# [PATH=... GOSU_VERSION=1.17 LANG=en_US.utf8 PG_MAJOR=16 PG_VERSION=16.3 PGDATA=/var/lib/postgresql/data]
docker inspect postgres:16 --format '{{.Config.ExposedPorts}}'
# map[5432/tcp:{}]
docker inspect postgres:16 --format '{{.Config.Entrypoint}}'
# [docker-entrypoint.sh]
Удобно, когда читаешь чужой образ и хочешь понять, что именно он запускает и какие переменные ожидает.
docker events: real-time stream
docker events стримит события Docker daemon: запуск контейнера, остановка, OOM, healthcheck failed, network connect/disconnect. Полезно, когда что-то происходит и хочешь увидеть, что именно.
docker events
В другом терминале:
docker run --rm hello-world
В терминале с events:
2026-05-15T10:15:23.456Z container create 7f3e... (image=hello-world, name=quirky_pasteur)
2026-05-15T10:15:23.567Z network connect ad34... (container=7f3e..., name=bridge)
2026-05-15T10:15:23.678Z container start 7f3e... (image=hello-world, name=quirky_pasteur)
2026-05-15T10:15:23.890Z container die 7f3e... (exitCode=0, image=hello-world, name=quirky_pasteur)
2026-05-15T10:15:23.912Z network disconnect ad34... (container=7f3e..., name=bridge)
2026-05-15T10:15:23.945Z container destroy 7f3e... (image=hello-world, name=quirky_pasteur)
Видим весь жизненный цикл: create -> network connect -> start -> die -> destroy. --rm означает уничтожение после die — это видно в events.
Фильтры
--filter сужает события:
# Только события контейнера pg
docker events --filter container=pg
# Только OOM
docker events --filter event=oom
# Только конкретные типы (контейнеры, не сети/volume)
docker events --filter type=container
# С определённого момента
docker events --since 10m
Идеальный сценарий для «контейнер рестартится каждые 30 секунд»:
docker events --filter container=app
И смотришь, в каком порядке идут события. Часто видишь: start -> die exitCode=137 (OOM!) -> start -> die exitCode=137. Это OOM Killer убивает по нехватке памяти. Фикс: добавить --memory лимит и оптимизировать приложение.
OOM-детект
OOM (out of memory) — частая причина «контейнер падает без объяснений». Проверяется так:
1. docker inspect -> .State.OOMKilled:
docker inspect app --format '{{.State.OOMKilled}}'
# true
2. Exit code 137:
docker inspect app --format '{{.State.ExitCode}}'
# 137
137 = 128 + 9, где 9 — SIGKILL. OOM Killer ядра шлёт SIGKILL, процесс не может его обработать, контейнер умирает.
3. В dmesg на хосте:
dmesg | grep -i kill
# Out of memory: Killed process 12345 (python) total-vm:1024MB, ...
4. docker events --filter event=oom:
docker events --filter event=oom
# 2026-05-15T11:23:45.678Z container oom 5fa3... (image=my-etl, name=app)
Имея 4 источника подтверждения OOM, фикс понятен:
- Поставить
--memory 512mна контейнер, чтобы он умирал управляемо (с понятной ошибкой), а не хост стучался в OOM Killer. - Оптимизировать приложение (профайлинг, разбиение работы).
Health status
Если у контейнера задан HEALTHCHECK (из Dockerfile или compose), inspect покажет:
docker inspect pg --format '{{.State.Health.Status}}'
# healthy
docker inspect pg --format '{{json .State.Health}}'
# {"Status":"healthy","FailingStreak":0,"Log":[{"Start":"...","End":"...","ExitCode":0,"Output":"..."}]}
Поле Log хранит последние ~5 healthcheck-вызовов с выводом. Удобно, когда healthcheck время от времени фейлится — видишь стектрейс.
Реальный сценарий: «контейнер рестартится в цикле»
Симптомы: docker ps показывает контейнер со статусом Restarting (1) X seconds ago. Что делать.
1. Узнать exit code:
docker inspect app --format '{{.State.ExitCode}} (started {{.RestartCount}} times)'
# 1 (started 23 times)
Exit 1 = «общая ошибка приложения». RestartCount растёт — restart policy на always или on-failure.
2. Посмотреть логи последнего запуска:
docker logs app --tail 50
# Traceback (most recent call last):
# File "/app/main.py", line 12, in <module>
# from psycopg import connect
# ModuleNotFoundError: No module named 'psycopg'
Ага, не установили psycopg. Чиним Dockerfile.
3. Если логи пустые, смотрим events с момента запуска:
docker events --since $(docker inspect app --format '{{.Created}}') --filter container=app
Увидим последовательность create -> start -> die -> start -> die. Если между start и die проходит миллисекунда — приложение падает сразу.
Попробуй сам
- Запусти
redis:7-alpine. Сделайdocker inspect redis --format '{{.NetworkSettings.IPAddress}}'. - Получи статус и exit code в одну строку:
docker inspect redis --format '{{.State.Status}} ({{.State.ExitCode}})'. - В отдельном терминале запусти
docker events --filter container=redis. В первом —docker stop redis && docker start redis. Посмотри сообщения в events-окне. - Создай контейнер с лимитом памяти и сожги её:
docker run -d --name eater --memory 64m python:3.13-slim python -c "x = ' ' * 200_000_000". Сразуdocker inspect eater --format '{{.State.OOMKilled}} (exit {{.State.ExitCode}})'. Должно бытьtrue (exit 137). - Запусти
docker eventsв одном терминале, в другом сделай паруdocker run --rm hello-world. Прочитай последовательность событий. - Для compose-стенда:
docker compose events— то же, но фильтрует на сервисы compose-проекта.