Производительность и эксплуатация Docker-хоста

Рейтинг: 75.8% · 12 голосов
Практический курс по Docker: образы, контейнеры, тома, сети, Compose и продакшен. Уроки по главам с обсуждением.
Ответить
Аватара пользователя
Marina_DevOps
Сообщения: 54
Зарегистрирован: 11 май 2026, 05:31

Производительность и эксплуатация Docker-хоста

Сообщение Marina_DevOps »

Оглавление курса (46)
  1. Что такое Docker и какие задачи он решает
  2. Установка Docker и запуск первого контейнера
  3. Образы: слои, теги и реестр Docker Hub
  4. Пишем свой Dockerfile
  5. Тома и хранение данных: volumes и bind mounts
  6. Сети в Docker: связываем контейнеры между собой
  7. Переменные окружения и конфигурация контейнеров
  8. Docker Compose: поднимаем многоконтейнерное приложение
  9. Оптимизация образов: multi-stage сборка, размер и кэш слоёв
  10. Логи, отладка и мониторинг контейнеров
  11. Базовая безопасность контейнеров
  12. Подготовка к продакшену: что важно учесть
  13. Dockerfile глубже: ENTRYPOINT и CMD, HEALTHCHECK, .dockerignore, запуск не от root
  14. Реестры образов: приватные registry, push и pull, теги и digest, imagePullSecrets
  15. BuildKit и buildx: multi-arch сборки, секреты сборки, экспорт кэша
  16. Docker в CI/CD: автосборка, сканирование образов (Trivy, Docker Scout), публикация
  17. Итоговый проект и куда расти: от Dockerfile до прода, обзор оркестрации (Kubernetes, Podman, OCI)
  18. Архитектура Docker: dockerd, containerd, runc и OCI
  19. Как работает изоляция: namespaces и cgroups
  20. Образы изнутри: слои, OverlayFS и storage driver
  21. Жизненный цикл контейнера и политики перезапуска
  22. Отладка контейнеров: exec, attach, nsenter, debug
  23. Сети Docker глубже: bridge, overlay, macvlan и iptables
  24. Docker Compose глубже: profiles, healthcheck-зависимости, override
  25. Данные и тома глубже: драйверы, бэкап, права
  26. Секреты и конфигурация: как не светить пароли
  27. Ограничение ресурсов: CPU, память, OOM и cgroups
  28. Здоровье и автозапуск: HEALTHCHECK, init и сигналы
  29. Логирование контейнеров: драйверы, ротация, сбор
  30. Мониторинг контейнеров: stats, cAdvisor, Prometheus
  31. Безопасность глубже: rootless, user namespaces, seccomp, capabilities
  32. Supply chain: сканирование, SBOM и подпись образов
  33. Глубокая оптимизация образов: distroless, scratch, кэш
  34. Multi-arch и сборка под ARM: buildx и эмуляция
  35. Реестры в продакшене: Harbor и облачные registry
  36. Docker без Docker: Podman, containerd, nerdctl, Buildah
  37. От Compose к оркестрации: когда контейнеров становится много
  38. Docker Swarm: встроенная оркестрация
  39. Сборка образов в CI без демона: kaniko, BuildKit, Buildah
  40. Траблшутинг Docker: типичные ошибки и как чинить
  41. Производительность и эксплуатация Docker-хоста (вы здесь)
  42. Docker Desktop, WSL2 и альтернативы на Windows и Mac
  43. Контейнеризация приложений: бэкенд, фронтенд, БД
  44. Docker и Kubernetes: containerd, миграция, kompose
  45. Лучшие практики и антипаттерны Docker
  46. Сквозной проект: production-ready стек и путь дальше
Боль: диск кончился, демон лёг, а контейнеры с ним

Классическая ситуация в продакшене. Сервер крутит десяток сервисов, всё работает месяцами, и в одно прекрасное утро мониторинг орёт: на корневом разделе кончилось место. Заходишь, делаешь - и видишь, что 80 гигабайт сожрал каталог /var/lib/docker. Никто туда руками ничего не клал. Это Docker за полгода накопил мёртвые образы, болтающиеся тома, build cache и логи контейнеров, которые никто не ротировал.

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

Этот урок - про эксплуатацию Docker-хоста как инженерную дисциплину. Разберём, где именно живёт диск, как работает storage driver overlay2, какой реальный оверхед у контейнеров, как настраивается daemon.json (включая спасительный live-restore), как организовать ротацию, бэкап и мониторинг самого демона. Не игрушки, а то, что держит боевой хост в форме.

Изображение

Где docker жрёт диск: анатомия var lib docker

Почти всё состояние Docker лежит в одном каталоге - по умолчанию /var/lib/docker (data-root). Запомни эту карту, она экономит часы паники:
  • overlay2/ - слои образов и read-write слои контейнеров. Самый жирный потребитель. Тут лежат и базовые слои ubuntu/alpine, и всё, что контейнер записал поверх.
  • image/ - метаданные образов, манифесты, дерево слоёв (что от чего наследуется).
  • containers/ - по подкаталогу на контейнер. Внутри - конфиг и, главное, файл логов вида <id>-json.log. Вот где растут логи, если не настроена ротация.
  • volumes/ - именованные тома. Данные БД, которые ты НЕ хочешь потерять. prune их тоже умеет сносить, будь осторожен.
  • buildkit/ - кеш сборки BuildKit. Может разрастись до десятков гигабайт на CI-раннере.
Посмотреть расклад - встроенная команда, не надо лезть в du вслепую:

Код: Выделить всё

docker system df

Код: Выделить всё

TYPE            TOTAL   ACTIVE  SIZE      RECLAIMABLE
Images          42      8       12.7GB    9.1GB (71%)
Containers      15      6       340MB     180MB (52%)
Local Volumes   23      9       6.2GB     3.8GB (61%)
Build Cache     310     0       18.4GB    18.4GB (100%)
Разбор полей. TOTAL - сколько объектов всего, ACTIVE - сколько реально используется (образ привязан к запущенному контейнеру, том примонтирован). RECLAIMABLE - сколько можно освободить прямо сейчас без потерь. В примере 18.4 ГБ build cache не использует никто - это чистый мусор. А вот Reclaimable у volumes - тревога: за этими гигабайтами могут стоять данные остановленных, но нужных контейнеров.

Глубже - флаг -v показывает построчно по каждому образу, тому и слою кеша:

Код: Выделить всё

docker system df -v
Именно так ты находишь, что один забытый образ с дампом базы весит 8 ГБ. Когда docker диск переполнен и system df мало, спускайся на уровень файлов:

Код: Выделить всё

du -h -d1 /var/lib/docker | sort -rh
sudo du -sh /var/lib/docker/overlay2/* | sort -rh | head -10
Как устроен docker overlay2 и почему почти нет оверхеда

overlay2 - storage driver по умолчанию на современных ядрах. Под капотом это OverlayFS - union-файловая система прямо в ядре Linux, без патчей и пользовательских демонов. Идея: сложить несколько слоёв-каталогов в один видимый.

Терминология OverlayFS:
  • lowerdir - нижние слои, read-only. Это слои образа. Их может быть много, они складываются стопкой.
  • upperdir - верхний слой, read-write. Личный слой конкретного контейнера. Сюда уходят все его записи.
  • merged - то, что контейнер видит как свою корневую ФС: объединение lower и upper.
  • workdir - служебный каталог ядра для атомарных операций.
Ключевой механизм - copy-on-write (CoW). Пока контейнер только читает файл из образа, он берётся напрямую из lowerdir, никакого копирования, никакого оверхеда. Как только контейнер пытается ИЗМЕНИТЬ файл, ядро сначала копирует его целиком из lower в upper, и дальше правки идут уже в копию. Отсюда два практических следствия. Первое: запуск ста контейнеров из одного образа почти не ест диск - они делят общие lower-слои, у каждого свой тощий upper. Второе: первая запись в большой файл стоит дорого (копируется весь файл, даже если меняешь один байт), а удаление файла из образа создаёт whiteout-файл вместо реального удаления.

Теперь про оверхед честно. CPU и память - оверхед практически нулевой. Контейнер это обычный процесс хоста, обёрнутый в namespaces и cgroups; нет гипервизора, нет эмуляции. Диск - вот тут CoW аукается на write-heavy нагрузках: если БД активно пишет в свой upper-слой через overlay2, ты теряешь на CoW и накладных расходах union-FS. Правило: данные с интенсивной записью (Postgres, MySQL, очереди) выноси в volumes. Том это bind в чистую ФС хоста мимо overlay2 - запись идёт нативно. Сеть - дефолтный bridge добавляет NAT и iptables/nftables-правила, и на больших pps это заметно. Для предельной производительности есть --network host (контейнер в сетевом namespace хоста, без NAT, но и без изоляции портов).

daemon.json: центр управления демоном

Поведение демона задаётся файлом /etc/docker/daemon.json. Это JSON, его читает dockerd при старте. После правки нужен

Код: Выделить всё

systemctl reload docker
(часть опций) или полный restart. Боевой конфиг:

Код: Выделить всё

{
  "data-root": "/data/docker",
  "storage-driver": "overlay2",
  "log-driver": "json-file",
  "log-opts": {
    "max-size": "20m",
    "max-file": "5",
    "compress": "true"
  },
  "default-ulimits": {
    "nofile": { "Name": "nofile", "Soft": 65536, "Hard": 65536 }
  },
  "live-restore": true,
  "registry-mirrors": ["https://mirror.example.ru"],
  "default-address-pools": [
    { "base": "172.30.0.0/16", "size": 24 }
  ]
}
Разбор по строкам, что тут зачем:
  • data-root - переезд /var/lib/docker на отдельный диск/раздел. Это спасает корень от переполнения: даже если Docker распухнет, система останется жива. Переносить надо с остановленным демоном и rsync -aP старого каталога.
  • log-opts - вот лекарство от бесконечно растущих логов. max-size 20m, max-file 5 = на контейнер максимум 5 файлов по 20 МБ = жёсткий потолок 100 МБ. compress дожимает повёрнутые файлы. Без этого один болтливый сервис за месяц напишет гигабайты в json.log.
  • default-ulimits - дефолтные лимиты для всех контейнеров. nofile (число открытых дескрипторов) часто упирается в дефолтный 1024 на нагруженных воркерах - поднимаем заранее, чтобы не ловить "too many open files".
  • registry-mirrors - RU-реалия. Docker Hub бывает недоступен или душит по rate-limit. Прописываешь зеркало (корпоративное, Yandex/VK Container Registry как pull-through или публичные зеркала) - и docker pull идёт через него прозрачно.
  • default-address-pools - из каких подсетей нарезать bridge-сети compose-проектов. Без этого Docker может занять подсеть, конфликтующую с твоим VPN или офисной.
Перед reload ВСЕГДА валидируй JSON, иначе демон не поднимется:

Код: Выделить всё

dockerd --validate --config-file /etc/docker/daemon.json
live-restore: контейнеры переживают рестарт демона

Опция

Код: Выделить всё

"live-restore": true
- то, ради чего стоит читать этот урок. По умолчанию dockerd - родитель shim-процессов контейнеров, и его остановка валит их. С live-restore демон при выходе оставляет контейнеры работать (их реально держит containerd/shim, а не сам dockerd), а при старте подхватывает их обратно.

Что это даёт на практике: обновляешь Docker Engine или перезапускаешь демон - сетевые сервисы продолжают отвечать, простой нулевой. Что НЕ покрывает: рестарт самого хоста (тут контейнеры всё равно стартуют заново по своей restart policy) и смену мажорной версии демона. Ещё нюанс: пока демон лежит, ты не можешь управлять контейнерами через docker (логи, exec, stop) - они работают, но "вслепую", до возврата демона. Включать live-restore стоит почти везде в проде; единственное ограничение - он несовместим со swarm-режимом.

Регулярный prune и автоочистка

Ручная зачистка мусора - точечно и предсказуемо:

Код: Выделить всё

docker image prune        # только dangling-образы (<none>)
docker image prune -a     # все образы, не привязанные к контейнерам
docker builder prune      # кеш сборки BuildKit
docker container prune    # остановленные контейнеры
docker system prune       # всё вместе, КРОМЕ томов
Главная граната - флаг --volumes. Команда

Код: Выделить всё

docker system prune -a --volumes
снесёт и неиспользуемые именованные тома. Один забытый --volumes на проде = удалённый дамп БД. Поэтому в автоматизации тома не трогают. Безопасный шаблон для крона - чистить только заведомо мусорное, с возрастным фильтром:

Код: Выделить всё

# чистим образы старше 7 дней и build cache, тома не трогаем
docker image prune -af --filter "until=168h"
docker builder prune -af --filter "unused-for=168h"
Это кладётся в /etc/cron.daily или systemd-таймер. Фильтр until/unused-for страхует от удаления свежих слоёв, которые вот-вот понадобятся. Build cache на CI-раннерах надо чистить агрессивнее всего - именно он раздувает docker диск незаметнее остальных.

Бэкап Docker-хоста и мониторинг демона

Что бэкапить, важно понимать концептуально: образы воспроизводимы (это артефакты сборки, они в registry), конфигурация в коде (Dockerfile, compose.yaml - в git). А вот состояние - в томах. Бэкапить надо тома и daemon.json, а не весь /var/lib/docker целиком (копировать overlay2 на живом демоне бессмысленно и опасно). Снять том через временный контейнер:

Код: Выделить всё

docker run --rm \
  -v pgdata:/data:ro \
  -v "$PWD":/backup \
  alpine tar czf /backup/pgdata-$(date +%F).tar.gz -C /data .
Контейнер монтирует том только на чтение, рядом - каталог хоста, и tar-ит данные наружу. Restore - симметрично, с распаковкой в пустой том. Для БД корректнее логический дамп (pg_dump/mysqldump) изнутри сервиса - он консистентен, в отличие от копирования файлов под нагрузкой.

Мониторинг самого демона - чтобы узнавать о проблемах не от пользователей. Минимум метрик: жив ли сокет, свободное место на data-root, число контейнеров, логи демона. Быстрая проверка здоровья:

Код: Выделить всё

docker info --format '{{.ServerVersion}} containers={{.Containers}} running={{.ContainersRunning}}'
journalctl -u docker --since "1 hour ago" -p err
journalctl с -p err вытащит только ошибки демона - там всплывают OOM, проблемы со storage driver, отвалы сети. Для нормального мониторинга dockerd умеет отдавать метрики в формате Prometheus: в daemon.json добавляешь

Код: Выделить всё

"metrics-addr": "127.0.0.1:9323"
и собираешь /metrics. Сверху - cAdvisor или node-exporter для метрик контейнеров и диска. Алерт на "data-root заполнен >85%" обязателен - это та самая утренняя паника, которую мы лечим заранее.

Грабли и антипаттерны из практики
  • Нет log-opts - дефолтный json-file пишет логи без лимита. Самая частая причина "внезапно кончился диск". Ставь max-size/max-file в daemon.json в первый же день.
  • rm -rf /var/lib/docker/overlay2 руками - так не чистят. Ты порвёшь метаданные, и демон сойдёт с ума. Только docker prune.
  • Запись БД в слой контейнера вместо тома - бьёт и по производительности (CoW), и по сохранности (данные умрут с контейнером).
  • system prune --volumes в кроне - тихий убийца данных. В автоматизации никогда.
  • Один большой раздел под корень и Docker вместе - переполнение Docker валит всю систему. Выноси data-root на отдельный диск.
  • Кривой daemon.json без валидации - демон не стартует, прод лежит. Всегда dockerd --validate перед reload.
Мини-лаба: приручаем хост за 10 минут
  • Сделай

    Код: Выделить всё

    docker system df
    , затем

    Код: Выделить всё

    docker system df -v
    . Найди самый жирный образ и самый большой слой build cache. Прикинь, сколько освободит prune.
  • Создай /etc/docker/daemon.json с log-opts (max-size 10m, max-file 3) и live-restore true. Прогони

    Код: Выделить всё

    dockerd --validate --config-file /etc/docker/daemon.json
    , затем

    Код: Выделить всё

    systemctl reload docker
    .
  • Запусти контейнер, дай ему пописать в stdout (например, ping в цикле). Через минуту посмотри размер файла логов в /var/lib/docker/containers/<id>/ и убедись, что ротация держит потолок.
  • Подними тестовый том, положи в него файл, сделай бэкап-командой выше, удали том, восстанови из tar.gz, проверь, что файл на месте.
  • Безопасно почисти мусор:

    Код: Выделить всё

    docker image prune -af --filter "until=24h"
    и

    Код: Выделить всё

    docker builder prune -af
    . Сравни docker system df до и после.
Контрольные вопросы
  • Какие подкаталоги /var/lib/docker и за что отвечают, и почему overlay2 обычно самый жирный?
  • Что делает live-restore, какие сценарии простоя он закрывает, а какие нет?
  • Почему запись из контейнера лучше уводить в volume, а не оставлять в слое overlay2 - при чём тут copy-on-write?
  • Чем опасен флаг --volumes у docker system prune и как построить безопасную автоочистку через крон?
Итог

Docker-хост в проде требует не магии, а гигиены. Держи в голове карту /var/lib/docker и регулярно смотри docker system df. Настрой daemon.json: log-opts против раздувания логов, live-restore против простоя при рестарте демона, default-ulimits и registry-mirrors под свои реалии. Понимай overlay2 и copy-on-write - тогда понятно, почему БД живёт в томе, а не в слое. Автоматизируй prune с возрастным фильтром и без --volumes, бэкапь тома (а не overlay2), мониторь демон и свободное место. Сделай это один раз нормально - и утренняя паника "диск кончился, контейнеры легли" тебя больше не навестит.
👍2 ❤️3 🔥 😄 🤔
Аватара пользователя
bullish
Сообщения: 1
Зарегистрирован: 05 июн 2026, 04:37

Re: Производительность и эксплуатация Docker-хоста

Сообщение bullish »

Воу, у меня на CI как раз overlay2 на 60 гигов распух, теперь понял что это build cache. Добавил builder prune в крон, освободилось 40 ГБ
👍 ❤️1 🔥 😄 🤔1
Аватара пользователя
nrobson
Сообщения: 1
Зарегистрирован: 15 май 2026, 06:43

Re: Производительность и эксплуатация Docker-хоста

Сообщение nrobson »

А live-restore безопасно включать если уже есть запущенные контейнеры в проде? Боюсь reload демона дёрнуть на боевом
👍1 ❤️3 🔥 😄 🤔
Ответить
← Предыдущая глава
Траблшутинг Docker: типичные ошибки и как чинить
Следующая глава →
Docker Desktop, WSL2 и альтернативы на Windows и Mac

Все главы курса «Docker: контейнеризация от основ до продакшена»

Поделиться темой: ✈ Telegram VK
Похожие запросы: docker команды шпаргалка для новичкаdocker desktop и wsl2 на windows как настроитьdocker rootless и безопасность контейнеровpodman vs docker что выбрать в продеdocker swarm встроенная оркестрация или kubernetesТюнинг и производительность nginx

Вернуться в «Docker с нуля»

Кто сейчас на конференции

Сейчас этот форум просматривают: нет зарегистрированных пользователей и 1 гость