По умолчанию Docker пишет stdout и stderr каждого контейнера в JSON-файл на хосте. Без явной настройки ротации этот файл растёт неограниченно — пока не заполнит диск. Проблема проявляется незаметно: контейнер работает месяцами, а в один момент
Когда процесс внутри контейнера пишет в stdout или stderr, Docker перехватывает этот поток через pipe. Демон
Это означает две вещи. Во-первых, если приложение внутри контейнера пишет логи в файл (а не в stdout), Docker их не увидит и ротация не сработает. Во-вторых, интенсивный вывод создаёт нагрузку на pipe и на дисковую подсистему хоста — при высоком throughput логирование становится узким местом.
Проверить, какой драйвер активен для конкретного контейнера:
Параметры
Пример при запуске:
Здесь Docker хранит не более трёх файлов по 50 МБ каждый — суммарный потолок 150 МБ на контейнер.
В
Значения
Чтобы не дублировать
После изменения файла перезапустите демон:
Глобальные настройки применяются ко всем новым контейнерам. Уже запущенные контейнеры сохраняют ту конфигурацию, с которой были созданы — для них нужно пересоздание (
Docker применяет настройки логирования по следующему правилу: параметры, заданные при создании контейнера (через
Практический вывод: глобальный
Начиная с Docker 18.09 доступен драйвер
Ограничение:
При использовании
Когда файловая система, на которой расположен
Если диск уже заполнен и Docker не отвечает, можно безопасно обнулить лог конкретного контейнера без его остановки:
Это не удаляет файл и не ломает файловый дескриптор, который держит dockerd. После освобождения места демон обычно восстанавливает работоспособность без перезапуска.
Быстрый способ оценить, сколько места занимают логи всех контейнеров:
Или проще — найти файлы больше определённого порога:
Для оценки общего объёма:
Чтобы подобрать
Повторите через час и вычислите разницу. Если контейнер генерирует 10 МБ/час, за сутки это 240 МБ. При
Ротация задана, но не работает. Параметры
max-size без max-file. Если указан только
Логи в контейнере, а не в stdout. Если приложение пишет в файл внутри контейнера (например,
docker logs блокирует терминал. Без флага
Смешивание драйверов в compose-файле. Если в одном compose-проекте часть сервисов использует
Если ротация не была настроена с самого начала, план действий:
Пересоздание означает кратковременный простой. Для сервисов с restart policy
После настройки убедитесь, что ротация работает:
Если в выводе
docker logs возвращает гигабайты, или файловая система уходит в read-only.Как Docker перехватывает вывод контейнера
Когда процесс внутри контейнера пишет в stdout или stderr, Docker перехватывает этот поток через pipe. Демон
dockerd читает данные из pipe и передаёт их активному logging-драйверу. Для json-file каждая строка сериализуется в JSON с метаданными (timestamp, stream) и дописывается в файл на хосте:
Код:
/var/lib/docker/containers/<container-id>/<container-id>-json.log
Это означает две вещи. Во-первых, если приложение внутри контейнера пишет логи в файл (а не в stdout), Docker их не увидит и ротация не сработает. Во-вторых, интенсивный вывод создаёт нагрузку на pipe и на дисковую подсистему хоста — при высоком throughput логирование становится узким местом.
Проверить, какой драйвер активен для конкретного контейнера:
Bash:
docker inspect --format='{{.HostConfig.LogConfig.Type}}' <container_name>
Ротация на уровне контейнера
Параметры
max-size и max-file управляют ротацией для драйвера json-file. max-size задаёт порог, при достижении которого текущий файл закрывается и создаётся новый. max-file — количество хранимых файлов (включая текущий). Старейший файл удаляется при превышении лимита.Пример при запуске:
Bash:
docker run -d \
--log-opt max-size=50m \
--log-opt max-file=3 \
--name myapp \
myimage:latest
Здесь Docker хранит не более трёх файлов по 50 МБ каждый — суммарный потолок 150 МБ на контейнер.
В
docker-compose.yml:
YAML:
services:
myapp:
image: myimage:latest
logging:
driver: json-file
options:
max-size: "50m"
max-file: "3"
Значения
max-size принимают суффиксы k, m, g. Если указать число без суффикса, Docker интерпретирует его как байты.Глобальные настройки через daemon.json
Чтобы не дублировать
--log-opt для каждого контейнера, задайте параметры ротации на уровне демона. Файл конфигурации — /etc/docker/daemon.json:
JSON:
{
"log-driver": "json-file",
"log-opts": {
"max-size": "100m",
"max-file": "5"
}
}
После изменения файла перезапустите демон:
Bash:
sudo systemctl restart docker
Глобальные настройки применяются ко всем новым контейнерам. Уже запущенные контейнеры сохраняют ту конфигурацию, с которой были созданы — для них нужно пересоздание (
docker rm + docker run или docker compose up --force-recreate).Приоритеты конфигурации
Docker применяет настройки логирования по следующему правилу: параметры, заданные при создании контейнера (через
--log-opt или секцию logging в compose), имеют приоритет над глобальными из daemon.json. Если контейнер создан без явного указания драйвера и опций, используются глобальные значения. Если и они не заданы — драйвер json-file без ротации.Практический вывод: глобальный
daemon.json — это страховка для контейнеров, которые запускаются без явных настроек (например, через docker run в скриптах или ad-hoc). Для критичных сервисов лучше дублировать параметры в compose-файле, чтобы конфигурация не зависела от состояния демона на конкретном хосте.Драйвер local: сжатие из коробки
Начиная с Docker 18.09 доступен драйвер
local, который пишет логи в формате protobuf и сжимает их по умолчанию. Это существенно экономит место при большом объёме вывода.
JSON:
{
"log-driver": "local",
"log-opts": {
"max-size": "100m",
"max-file": "3",
"compress": "true"
}
}
Ограничение:
docker logs работает с драйвером local, но сторонние инструменты, которые парсят JSON-файлы напрямую из /var/lib/docker/containers/, не смогут их прочитать без Docker API.Альтернативные драйверы
| Драйвер | Куда пишет | Ротация | Когда использовать |
|---|---|---|---|
json-file | Локальный JSON | max-size / max-file | Простые сценарии, dev |
local | Локальный protobuf + сжатие | max-size / max-file | Продакшн без внешнего стека |
syslog | Системный syslog-демон | Зависит от syslog | Интеграция с rsyslog/syslog-ng |
journald | systemd journal | Зависит от journald | Хосты с systemd |
fluentd | Fluentd-агент | Зависит от Fluentd | Централизованный сбор логов |
gelf | Graylog | Зависит от Graylog | Graylog-инфраструктура |
awslogs | CloudWatch Logs | Политика CloudWatch | AWS-окружения |
При использовании
syslog или journald ротация управляется соответствующим системным сервисом, а не Docker. Для journald лимит задаётся в /etc/systemd/journald.conf через SystemMaxUse. Для rsyslog — через правила в /etc/rsyslog.conf или /etc/rsyslog.d/.Что происходит при заполнении диска
Когда файловая система, на которой расположен
/var/lib/docker/, заполняется на 100%, последствия каскадные:- Docker-демон не может создавать новые контейнеры и образы.
- Запущенные контейнеры могут аварийно завершиться, если их процессы пытаются писать на диск (не только логи, но и данные).
dockerdсам может перейти в нестабильное состояние, потому что ему нужно место для внутренних операций.
- Файловая система может переключиться в read-only (зависит от настроек
errors=в fstab и типа ФС).
Если диск уже заполнен и Docker не отвечает, можно безопасно обнулить лог конкретного контейнера без его остановки:
Bash:
truncate -s 0 /var/lib/docker/containers/<container-id>/<container-id>-json.log
Это не удаляет файл и не ломает файловый дескриптор, который держит dockerd. После освобождения места демон обычно восстанавливает работоспособность без перезапуска.
Диагностика: найти контейнеры с раздутыми логами
Быстрый способ оценить, сколько места занимают логи всех контейнеров:
Bash:
docker ps -q | xargs -I {} sh -c 'echo "$(du -sh /var/lib/docker/containers/{}/{}-json.log 2>/dev/null) {}"' | sort -rh | head -20
Или проще — найти файлы больше определённого порога:
Bash:
find /var/lib/docker/containers/ -name "*-json.log" -size +100M -exec ls -lh {} \;
Для оценки общего объёма:
Bash:
du -sh /var/lib/docker/containers/*/
Оценка необходимого пространства
Чтобы подобрать
max-size и max-file, оцените скорость вывода контейнера. Запустите на несколько часов в продакшн-режиме и замерьте рост файла:
Bash:
stat --format='%s' /var/lib/docker/containers/<id>/<id>-json.log
Повторите через час и вычислите разницу. Если контейнер генерирует 10 МБ/час, за сутки это 240 МБ. При
max-size=100m и max-file=3 потолок — 300 МБ, что покрывает чуть больше суток. Для недельного запаса потребуется max-file=7 или больший max-size.Типичные ошибки
Ротация задана, но не работает. Параметры
max-size и max-file в daemon.json применяются только к новым контейнерам. Если контейнер был создан до изменения конфига, его логи продолжат расти без ограничений. Решение — пересоздать контейнер.max-size без max-file. Если указан только
max-size, Docker по умолчанию хранит один файл (max-file=1). При ротации старый файл удаляется сразу. Это может быть приемлемо, но если нужен запас — задавайте оба параметра явно.Логи в контейнере, а не в stdout. Если приложение пишет в файл внутри контейнера (например,
/var/log/app.log), Docker-драйвер это не перехватит. Ротация max-size на такой файл не действует. Нужно либо перенаправить вывод в stdout, либо настроить ротацию внутри образа (logrotate, встроенные механизмы приложения).docker logs блокирует терминал. Без флага
--tail команда выводит весь накопленный лог. Для файлов в несколько гигабайт это занимает минуты и нагружает I/O. Используйте docker logs --tail 100 -f <container> для оперативного просмотра.Смешивание драйверов в compose-файле. Если в одном compose-проекте часть сервисов использует
json-file, а часть — journald, диагностика усложняется: нужно проверять разные места хранения. Единообразие упрощает мониторинг.Миграция существующих контейнеров
Если ротация не была настроена с самого начала, план действий:
- Зафиксировать текущие настройки:
docker inspect <container>.
- Обновить
daemon.jsonили compose-файл с нужными параметрами.
- Пересоздать контейнеры:
docker compose up -d --force-recreateили вручнуюdocker stop→docker rm→docker run.
- Убедиться, что новые контейнеры подхватили настройки:
docker inspect --format='{{json .HostConfig.LogConfig}}' <container>.
- Удалить старые лог-файлы, если они остались (Docker обычно убирает их при удалении контейнера, но стоит проверить).
Пересоздание означает кратковременный простой. Для сервисов с restart policy
always или unless-stopped это штатная операция, но для stateful-контейнеров убедитесь, что данные хранятся в volumes, а не в writable layer.Проверка результата
После настройки убедитесь, что ротация работает:
Bash:
## Проверить конфигурацию логирования контейнера
docker inspect --format='{{json .HostConfig.LogConfig}}' <container_name>
## Посмотреть список файлов логов
ls -lh /var/lib/docker/containers/<container-id>/
## Убедиться, что общий объём логов в пределах нормы
du -sh /var/lib/docker/containers/*/
Если в выводе
ls видно несколько файлов вида <id>-json.log, <id>-json.log.1, <id>-json.log.2 — ротация активна и работает корректно.Практический чек-лист
- Задать
max-sizeиmax-fileглобально в/etc/docker/daemon.jsonдля всех новых контейнеров.
- Для критичных сервисов продублировать настройки в compose-файле, чтобы конфигурация не зависела от порядка развёртывания.
- Рассмотреть драйвер
localдля продакшн-хостов с большим объёмом вывода.
- Настроить мониторинг использования
/var/lib/docker/containers/(alert при превышении порога).
- Убедиться, что приложения пишут в stdout/stderr, а не в файлы внутри контейнера.
- Пересоздать старые контейнеры, которые были запущены до введения глобальной ротации.
- Оценить скорость генерации логов и подобрать
max-size×max-fileс запасом на целевой период хранения.
