Потоки событий нескольких контейнеров поступают в ограниченное хранилище с ротацией
DevOps

Логи Docker: stdout, logging driver и безопасная ротация

Настраиваем журналы контейнеров без переполнения диска: выбираем logging driver, задаём max-size и max-file, читаем события и проверяем новые контейнеры.

Содержание

Контейнер может работать исправно и одновременно заполнять диск журналом. Docker получает обычный вывод программы из двух потоков: stdout для сообщений о работе и stderr для ошибок. Затем logging driver — выбранный механизм хранения журналов — записывает эти строки на диск или передаёт внешней системе. Ротация ограничивает размер и число старых файлов, чтобы журнал не занял всё свободное место.

Как не дать журналу заполнить диск

Пишите оперативные события приложения в stdout и stderr, выясните фактический logging driver и задайте ему пределы ротации. Для одного Compose-сервиса удобно настроить logging.driver: local и строковые параметры max-size и max-file. Не редактируйте внутренние файлы Docker вручную и помните: изменение default daemon не переносится на уже созданные контейнеры.

Разделите журналы по назначению

В стандартные потоки полезно отправлять события, необходимые для эксплуатации:

  • запуск и корректную остановку;
  • ошибки запросов и фоновых задач;
  • идентификатор запроса или операции;
  • длительность и итог без чувствительных данных;
  • предупреждения о приближении к внутренним лимитам.

Не записывайте пароли, токены, session cookies, полные строки подключения и тела запросов по умолчанию. Журнал не становится безопасным только потому, что находится внутри Docker host.

Аудит, метрики и трассировки решают отдельные задачи. docker logs удобен для оперативной диагностики одного экземпляра, но не является долгосрочным централизованным хранилищем с поиском и политикой доступа.

Узнайте, где Docker хранит журнал

Первая команда выполняется на хосте с Docker и показывает механизм журналирования, который получат новые контейнеры по умолчанию:

docker info --format 'default={{.LoggingDriver}}'

Следующая команда проверяет уже созданный контейнер example-app. Она выводит сохранённый при его создании драйвер и параметры:

docker inspect example-app \
  --format 'driver={{.HostConfig.LogConfig.Type}} options={{json .HostConfig.LogConfig.Config}}'

Контейнер сохраняет параметры, с которыми был создан. Поэтому его driver может отличаться от текущего default.

Проверьте журнал без бесконечного вывода:

docker logs --tail 100 --timestamps example-app
docker logs --since 15m example-app
docker logs --follow --tail 50 example-app

Ctrl+C завершает только просмотр --follow. Если строки отсутствуют, убедитесь, что приложение действительно пишет в stdout/stderr и выбранный driver поддерживает чтение командой docker logs.

Настройте ротацию для Compose-сервиса

Driver local хранит журналы в оптимизированном внутреннем формате и по умолчанию использует ротацию и сжатие. Явные значения делают намерение проекта заметным:

services:
  web:
    image: example/web:tested
    logging:
      driver: local
      options:
        max-size: "10m"
        max-file: "3"

Числа приведены как пример, а не универсальная норма. Оцените обычный поток строк, время расследования и свободное место. Слишком маленькое окно сотрёт причину редкой ошибки до того, как вы её заметите.

Проверьте и примените конфигурацию:

docker compose config
docker compose up -d --force-recreate web
docker compose ps web
docker inspect "$(docker compose ps -q web)" \
  --format '{{json .HostConfig.LogConfig}}'

--force-recreate прерывает старый экземпляр, поэтому на рабочем сервисе планируйте окно или используйте безопасный способ замены, поддерживаемый вашей архитектурой.

Если нужен json-file

json-file часто является default driver. Без max-size его файл может расти без ротации. Для отдельного контейнера:

docker run -d --name log-demo \
  --log-driver json-file \
  --log-opt max-size=10m \
  --log-opt max-file=3 \
  alpine:3.22 sh -c 'while :; do date -u; sleep 5; done'

Параметр max-file работает только вместе с max-size. В daemon.json значения options должны быть строками:

{
  "log-driver": "local",
  "log-opts": {
    "max-size": "10m",
    "max-file": "3"
  }
}

Глобальное изменение затрагивает все будущие контейнеры и требует обслуживания daemon. Перед ним проверьте существующий /etc/docker/daemon.json, валидность JSON и совместимость других настроек. Не заменяйте файл целиком фрагментом из статьи.

После изменения default потребуется контролируемый restart Docker по инструкции вашей ОС, а затем пересоздание контейнеров. Простая перезагрузка daemon не меняет LogConfig уже созданных экземпляров.

Не работайте с внутренним файлом напрямую

Для json-file Docker хранит журналы в каталоге данных daemon. Не открывайте эти файлы на запись, не обрезайте их через shell и не настраивайте внешний logrotate поверх них. Их форматом и жизненным циклом управляет Docker.

Если диск уже заполнен, сначала сохраните диагностику:

docker system df
docker ps --size
sudo du -x -h /var/lib/docker 2>/dev/null | sort -h | tail

Последняя команда требует прав и используется только для оценки каталогов, не для удаления. Выясните источник роста, настройте ограничение и пересоздайте конкретный контейнер. Массовая очистка может удалить нужные образы, кеши и volumes, но не исправит причину непрерывного потока.

Пишите по одному событию в строке

Одна строка должна описывать одно событие. Структурированный JSON удобен, если последующий сборщик умеет его разбирать, но не вкладывайте JSON-строку в несколько дополнительных оболочек. Полезные поля:

  • стабильное имя события;
  • уровень;
  • UTC timestamp, если его не добавляет транспорт;
  • request ID или job ID;
  • длительность;
  • безопасный итог операции.

Не используйте уровень error для штатных ответов клиента и не печатайте stack trace на каждую ожидаемую ошибку валидации. Иначе важные события теряются в шуме, а ротация происходит быстрее.

Проверка после изменения

  1. Сгенерируйте несколько безопасных тестовых событий.
  2. Убедитесь, что docker logs --tail их показывает.
  3. Проверьте HostConfig.LogConfig нового контейнера.
  4. Наблюдайте расход диска в течение типичной нагрузки.
  5. Проверьте, что алерт о свободном месте приходит раньше критического заполнения.
  6. Убедитесь, что секреты не попадают в обычный и ошибочный путь.

Для отката верните предыдущий блок logging, проверьте docker compose config и пересоздайте только сервис. Старые внутренние файлы не конвертируются в новый формат автоматически.

Команды чтения и фильтрации разобраны в статье о docker logs и inspect. Ограничение самого процесса рассматривается в материале о CPU и памяти, а полный стек удобно описывать через Compose.

Источники

Рекламное местоВаша компания здесьРазместить рекламу

Самопроверка

Проверьте, что материал усвоен

Ответьте на все вопросы. Результат сохранится только в этом браузере и будет учтён в статистике прочитанных материалов.

01Куда приложению в контейнере обычно следует писать оперативные журналы?
02Какие параметры вместе включают ограниченную ротацию json-file?
03Как узнать logging driver конкретного контейнера?

Разбираем коротко

Частые вопросы

Почему docker logs не показывает журнал контейнера?

Команда зависит от выбранного logging driver и его поддержки чтения. Также приложение могло писать в собственный файл вместо stdout и stderr. Сначала проверьте LogConfig контейнера.

Применится ли новый logging driver к уже работающим контейнерам?

Нет. Изменение настройки daemon влияет на вновь создаваемые контейнеры. Существующие экземпляры нужно контролируемо пересоздать и проверить после изменения.

Можно ли читать файлы json-file напрямую из каталога Docker?

Не следует. Docker предупреждает, что этими файлами управляет daemon; внешнее изменение может нарушить работу logging system. Используйте docker logs или поддерживаемый сборщик.