Куда пишутся логи и как их читать:
Docker перехватывает stdout и stderr главного процесса контейнера и передаёт их драйверу логирования. По умолчанию это json-file: каждый контейнер пишет в /var/lib/docker/containers/<id>/<id>-json.log. Отсюда первое правило: приложение в контейнере должно писать логи в stdout и stderr, а не в файлы. Официальный образ nginx так и устроен, access.log там симлинк на /dev/stdout.
Код: Выделить всё
docker logs api # весь лог с начала
docker logs -f api # следить в реальном времени
docker logs --tail 100 api # последние 100 строк
docker logs --since 30m -t api # за полчаса, с таймстампами
Главная засада json-file: ротации по умолчанию нет. Лог растёт, пока не съест диск, на VPS за 300 рублей с 20 ГБ это вопрос недель. Лечится в /etc/docker/daemon.json:
Код: Выделить всё
{
"log-driver": "json-file",
"log-opts": {
"max-size": "10m",
"max-file": "3"
}
}
Отладка изнутри:
docker exec запускает дополнительный процесс в живом контейнере. Если контейнер уже умер, exec не поможет, тогда работаем с logs и inspect. inspect выдаёт полное состояние в JSON: код выхода, флаг OOM, тома, сети, переменные окружения.
Код: Выделить всё
docker exec -it api sh # шелл внутри контейнера
docker exec api env # переменные окружения
docker inspect api --format '{{.State.ExitCode}} {{.State.OOMKilled}}'
Мониторинг без лишнего софта:
Код: Выделить всё
docker stats # живая таблица: CPU, память, сеть, диск
docker top api # процессы внутри контейнера
docker events --since 1h # события демона: старты, падения, OOM
Типичные грабли:
Приложение пишет логи в файл внутри контейнера. docker logs пуст, файл умирает вместе с контейнером. Перенастройте вывод на stdout или сделайте симлинк на /dev/stdout, как в образе nginx.
Диск кончился из-за логов. Симптом: контейнеры падают с ошибкой no space left on device. Проверьте размер каталога /var/lib/docker/containers, виновник почти всегда там. Ротацию из примера выше ставьте сразу на любом сервере, не дожидаясь инцидента.
В минимальном образе нет шелла. Попытка вызвать bash через exec отвечает executable file not found. В alpine есть sh, в distroless нет вообще ничего. Выручает временный контейнер nicolaka/netshoot, запущенный с флагом --network container:api: он попадает в то же сетевое пространство, а внутри полный набор утилит.
docker logs молчит при стороннем драйвере. Исторически команда работала только с json-file, local и journald. Современный движок держит локальный кэш (dual logging) и показывает логи даже при syslog или gelf, но кэш иногда отключают ради экономии диска, тогда вывод смотрите уже в самой системе логирования.
Что в итоге:
Вы умеете читать и ротировать логи, попадать внутрь контейнера через exec, разбирать причины смерти через inspect и коды выхода, оценивать ресурсы через stats. Этого хватает для девяноста процентов инцидентов на одном хосте. В главе 11 займёмся безопасностью: почему root внутри контейнера это риск и как урезать контейнеру лишние права.