Top.Mail.Ru
КОНФИГУРАТОР Серверы ▶
Сетевое оборудование ▶
СХД ▶
IP-телефоны IP-камеры Источники бесперебойного питания (ИБП) Комплектующие Готовые решения Серверы под задачу
О компании Купить в лизинг Блог Отзывы Доставка Гарантия Контакты Работа у нас Реквизиты Спецпредложения Игровые ПК на ISKRAPC Заявка в тех поддержку
Эксперты в подборе IT-оборудования

Логи Docker-контейнеров: где лежат, как смотреть и как не забить диск

6 октября 2026
Логи Docker-контейнеров: где лежат, как смотреть и как не забить диск

Логи Docker-контейнера читаются командой docker logs <имя_или_id>. Физически они лежат на хосте в файле /var/lib/docker/containers/<полный-id>/<полный-id>-json.log. Внутрь контейнера лезть не нужно: демон перехватывает стандартный вывод процесса и пишет его на диск сам.

Первую часть все знают. Вторая - причина, по которой на сервере однажды кончается место, а docker system df показывает, что всё в порядке. Разберём обе. Если контейнеров много и ими управляет оркестратор, логика та же, но появляются свои сущности - основные понятия Kubernetes: Pod, Deployment, Service мы разбирали отдельно.

Что попадает в логи, а что нет

Docker забирает то, что процесс с PID 1 внутри контейнера пишет в стандартный вывод и поток ошибок. Всё остальное для него не существует.

Если приложение пишет логи в файл внутри контейнера - /app/logs/app.log - демон о них не узнает. Файл будет расти в записываемом слое контейнера, docker logs останется пустым, а при пересоздании контейнера логи исчезнут вместе с ним. Отсюда правило контейнерных образов: приложение пишет в stdout и stderr, а куда это дальше денется - решает не приложение.

Формат файла - по строке JSON на каждую строку вывода:

{"log":"listening on :8080\n","stream":"stdout","time":"2026-08-13T09:14:02.118Z"}

Поле stream разделяет stdout и stderr, и это разделение доживает до команды. docker logs отдаёт вывод контейнера в свой stdout, а ошибки - в свой stderr, поэтому вот такой фильтр работает не так, как ожидают:

docker logs myapp 2>/dev/null | grep ERROR    # отбросит как раз stderr, где ошибки и лежат
docker logs myapp 2>&1 | grep -i error        # правильно: сначала слить потоки

Как смотреть: docker logs и его ключи

Базовый набор закрывает почти всё.

docker logs myapp                      # весь лог с начала жизни контейнера
docker logs -f --tail 100 myapp        # последние 100 строк и дальше в реальном времени
docker logs --since 15m myapp          # только за последние 15 минут
docker logs --since 2026-08-13T09:00:00 --until 2026-08-13T10:00:00 myapp
docker logs -t myapp                   # с отметками времени

--tail без -f полезнее, чем кажется: на контейнере, который живёт полгода, простой docker logs попытается прочитать и отдать весь файл целиком. На гигабайтном логе это заметная пауза и лишняя нагрузка на диск.

Для Compose всё то же самое, только по имени сервиса:

docker compose logs -f --tail 50 backend
docker compose logs --since 10m          # сразу по всем сервисам проекта

Точный путь к файлу лога конкретного контейнера отдаёт inspect:

docker inspect --format='{{.LogPath}}' myapp

Почему на узле кончилось место

Здесь начинается то, ради чего эта статья.

Драйвер логирования по умолчанию - json-file. У него три опции: max-size, max-file и compress. Значения по умолчанию - -1, 1 и false. То есть ротации нет вообще: файл растёт, пока на разделе есть место. Приложение, которое пишет пару строк на каждый HTTP-запрос, за несколько месяцев спокойно наберёт десятки гигабайт.

Дальше срабатывает вторая ловушка. docker system df считает образы, контейнеры, тома и кеш сборки - логи в этот расчёт не входят. Картина получается такая: df -h говорит, что раздел заполнен на 98 %, docker system df показывает пару гигабайт, и админ ищет проблему не там. Ошибка известная, в трекере moby она висит с 2017 года.

Частая ошибка. Считать, что раз логи не видны в docker system df, их и на диске нет. Проверять надо напрямую: sudo sh -c 'du -sh /var/lib/docker/containers/*/*-json.log' | sort -h | tail -10 - команда покажет десять самых жирных логов и сразу назовёт виновника по id контейнера. Обратите внимание на sh -c: без него звёздочки раскрывает ваша оболочка, а она в каталог /var/lib/docker заглянуть не может, и команда молча вернёт «нет такого файла».

Отдельная неприятность в том, что /var/lib/docker обычно лежит на корневом разделе. Забился он - встал не один контейнер, а весь узел: демон не может создать новый контейнер, systemd не пишет свои журналы, при неудачном стечении обстоятельств не логинится даже ssh. Поэтому на серверах, где контейнеров много, /var/lib/docker разумно выносить на отдельный раздел или диск - тогда переполнение логами останется локальной проблемой Docker, а не аварией всей машины.

Настройка ротации

Правильное место для настройки - файл /etc/docker/daemon.json на хосте. Он задаёт поведение по умолчанию для всех новых контейнеров:

{
  "log-driver": "json-file",
  "log-opts": {
    "max-size": "10m",
    "max-file": "3",
    "compress": "true"
  }
}

Такая конфигурация ограничивает каждый контейнер тридцатью мегабайтами: три файла по десять, старые сжимаются. После правки демон надо перезапустить:

sudo systemctl restart docker

Перезапуск демона по умолчанию гасит все работающие контейнеры - это не «мягкая» операция, и на боевом сервере под неё нужно окно. Исключение одно: если в том же daemon.json включён "live-restore": true, демон отцепится от контейнеров, они продолжат работать под containerd, а после старта демон подхватит их обратно. По умолчанию эта опция выключена.

И вот тут два подвоха, на которых спотыкаются чаще всего.

Перезапуск демона не трогает уже созданные контейнеры. Они сохраняют те опции логирования, с которыми были созданы. Новые настройки получат только контейнеры, созданные после перезапуска. Чтобы применить ротацию к существующим, их надо пересоздать: docker compose up -d --force-recreate или руками docker rm и заново docker run. Перезапуск через docker restart не поможет - это тот же самый контейнер.

Проверить, что реально применилось, можно только у конкретного контейнера:

docker inspect --format='{{.HostConfig.LogConfig}}' myapp

Если нужна ротация только для отдельного сервиса, а не для всего хоста, опции задаются при запуске:

docker run --log-opt max-size=10m --log-opt max-file=3 myimage

В Compose то же самое пишется в описании сервиса:

services:
  backend:
    image: myimage
    logging:
      driver: "json-file"
      options:
        max-size: "10m"
        max-file: "3"

Драйверы логирования: какой когда

json-file - не единственный вариант, и для части задач он худший из доступных.

ДрайверРотация по умолчаниюРаботает docker logsКогда брать
json-fileнетдаЗначение по умолчанию. Годится, если ротацию задали руками
localда: 20 МБ × 5 файлов, со сжатиемдаЛучший выбор для одиночного хоста: место под контролем из коробки, формат компактнее
journaldпо правилам systemd-journaldдаКогда логи контейнеров хочется читать теми же journalctl, что и логи системы
syslogпо правилам приёмниканет (только кеш)Отправка на внешний syslog-сервер
fluentdна стороне приёмниканет (только кеш)Централизованный сбор в стек с обработкой
none-нетКонтейнеры, которые пишут много и бесполезно

Про docker logs в этой таблице важно: в Docker Community команда работает только с local, json-file и journald. Для удалённых драйверов начиная с версии 20.10 есть двойное логирование - локальный кеш, из которого отдаются последние записи, - но полагаться на него как на архив нельзя.

Драйвер local заслуживает отдельного слова. Он делает ровно то, что от json-file ждут по умолчанию: ограничивает 20 мегабайтами на файл, держит пять файлов, сжимает старые. Сотня мегабайт на контейнер - предсказуемо. Единственная плата - формат бинарный, руками cat его не почитать, только через docker logs.

Когда логирование тормозит приложение

Опция, о которой вспоминают редко, - mode. По умолчанию она равна blocking: если драйвер не успевает принять строку, процесс в контейнере ждёт. На нагруженном сервисе с медленным диском или подтормаживающим удалённым приёмником это превращается в загадочные задержки ответа, которые не видно ни в одной метрике приложения.

Альтернатива - неблокирующий режим с буфером в памяти:

{
  "log-driver": "local",
  "log-opts": {
    "mode": "non-blocking",
    "max-buffer-size": "4m"
  }
}

Цена честная: при переполнении буфера строки теряются молча. Для журнала отладки это приемлемо, для аудита - нет.

У логов с дисковой подсистемой отношения вообще более близкие, чем принято думать. Логи пишутся синхронно и мелкими порциями, конкурируя с приложением за одни и те же операции ввода-вывода. На SATA-дисках под нагруженным контейнером это ощутимо, на NVMe - почти нет. Если контейнерная нагрузка живёт на общем хранилище вместе с базами, стоит хотя бы развести их по разным устройствам: сервер под виртуализацию с отдельным диском под /var/lib/docker избавляет от целого класса плавающих проблем.

Схема потока логов контейнера: стандартный вывод приложения, драйвер логирования Docker и файл на диске хоста

Централизованный сбор: когда пора

Файл на хосте - это нормально ровно до тех пор, пока хост один. Дальше начинаются знакомые симптомы: контейнер пересоздали - логи ушли вместе с ним, узел вывели из кластера - расследовать нечего, инцидент затронул четыре сервиса - и вы открываете четыре терминала. Логика тут та же, что и с обычными системными журналами, для чего они нужны и что в них искать мы разбирали отдельно, - только эфемерность контейнеров делает вопрос острее.

Централизованный сбор решает это, но требует своих ресурсов. Честные цифры перед решением: приёмник логов сам по себе - это сервис с диском, индексами и памятью, и на объёмах, которые генерирует десяток болтливых контейнеров, он попросит больше железа, чем сами эти контейнеры. Планируйте хранилище под него отдельно и с запасом, а политику хранения задавайте сразу: недавние логи на быстром диске, архив - на медленном и дешёвом.

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

Чего делать не стоит

Удалять файл лога у работающего контейнера. rm на -json.log место не освободит: демон держит файл открытым, и ядро освободит блоки только когда дескриптор закроется. Диск останется полным, а логи пропадут. Если совсем горит, файл нужно обнулять, а не удалять:

sudo truncate -s 0 $(docker inspect --format='{{.LogPath}}' myapp)

Это аварийная мера на один раз. Настоящее решение - ротация, иначе через неделю вернётесь к той же команде.

Навешивать logrotate поверх драйвера. Системный logrotate переименует файл, а Docker продолжит писать в старый дескриптор. Получите растущий файл без имени и пустой docker logs. Ротацией контейнерных логов занимается драйвер, и только он.

Писать в stdout всё подряд. Уровень debug в промышленной среде - самый быстрый способ превратить логи в проблему хранения. Прежде чем настраивать ротацию, стоит посмотреть, что вообще пишется: часто половина объёма - это healthcheck-запросы балансировщика, которые никому не нужны.

Если что-то пошло не так

СимптомПричинаЧто делать
df -h показывает полный диск, docker system df - малоЛоги не входят в расчёт docker system dfsudo sh -c 'du -sh /var/lib/docker/containers/*/*-json.log' | sort -h | tail
docker logs пусто, но приложение работаетПриложение пишет в файл внутри контейнера, а не в stdoutПроверить конфигурацию логирования приложения; при драйвере none - сменить драйвер
docker logs пусто и драйвер fluentd или syslogКоманда не поддерживается драйверомСмотреть в приёмнике; локальный кеш даёт только последние записи
Ротация настроена, а файл всё равно растётКонтейнер создан до правки daemon.jsondocker inspect --format='{{.HostConfig.LogConfig}}', затем пересоздать контейнер
docker logs отдаётся десятки секундФайл на гигабайты, читается целиком--tail, --since; после - настроить ротацию
Приложение периодически подвисает на записиmode: blocking при медленном диске или приёмникеmode: non-blocking с max-buffer-size, разнести диски

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

Где хранятся логи Docker-контейнера?
На хосте, в /var/lib/docker/containers/<полный-id>/<полный-id>-json.log. Точный путь для конкретного контейнера - docker inspect --format='{{.LogPath}}' <имя>.

Как посмотреть логи упавшего контейнера?
Так же, как у работающего: docker logs <имя>. Пока контейнер не удалён командой docker rm, его лог на месте. После удаления файл исчезает вместе с каталогом контейнера, и это главный аргумент за централизованный сбор.

Как ограничить размер логов Docker?
Задать max-size и max-file в /etc/docker/daemon.json, перезапустить демон и пересоздать существующие контейнеры. Либо сразу переключиться на драйвер local, где ограничение стоит по умолчанию.

Чем local отличается от json-file?
Ротацией из коробки (20 МБ × 5 файлов со сжатием) и более компактным бинарным форматом. Минус - файл нельзя прочитать обычными текстовыми утилитами, только через docker logs.

Считает ли docker system df размер логов?
Нет. Он показывает образы, контейнеры, тома и кеш сборки. Логи в этих цифрах не учитываются - именно поэтому расхождение с df -h бывает в десятки гигабайт.

По теме: Как посмотреть логи сервера · Docker vs LXC: сравнение технологий контейнеризации · Мониторинг виртуальных машин и контейнеров

Контейнеры упираются в диск, а не в процессор?

Инженеры ITTELO подберут конфигурацию под контейнерную нагрузку - с отдельным устройством под /var/lib/docker, запасом по объёму и нужным типом накопителей. Соберём и протестируем под задачу перед отгрузкой, с гарантией и поддержкой после продажи. На рынке серверов 11+ лет.

Подбор сервера под нагрузку · +7 (800) 551-80-12 · info@ittelo.ru

ПОДПИСКА

НА РАССЫЛКУ
ПОЛЕЗНЫЕ СТАТЬИ, АКЦИИ
И ЗАКРЫТЫЕ РАСПРОДАЖИ
Котик подписка
Похожие статьи
Вам также может быть интересно

Текст
Товар добавлен в список сравнения
Перейти в сравнение
Продолжить просмотр
Заявка в тех поддержку
Загрузка формы…
Не удалось загрузить форму. Обновите страницу или свяжитесь с нами по телефону.
Консультация
ИТ-специалиста
Оставьте контакты — свяжемся с вами в течение нескольких минут и подготовим коммерческое предложение
IT-архитектор подберет сервер под вашу задачу
Заполните форму — наш специалист свяжется с вами в течение 15 минут, уточнит задачу и подготовит коммерческое предложение
Заказать сервер
Отправим конфигурацию вам на почту. Менеджер перезвонит в течение 15 минут
Зарегистрироваться в бонусной программе
Консультация
ИТ-специалиста
Оставьте контакты — свяжемся с вами в течение нескольких минут и подготовим коммерческое предложение