Ограничение памяти в Docker: как и почему

Быстрые ссылки
Как работают лимиты памяти в Docker
Установка жёстких и мягких лимитов памяти
Управление swap
Отключение убийц процессов при недостатке памяти
Краткое резюме
Docker-контейнеры по умолчанию работают без ограничений ресурсов. Процессы внутри контейнеров могут потреблять любую память, что потенциально влияет на соседние контейнеры и другие рабочие нагрузки на хосте.
Это опасно в продакшене. Каждый контейнер должен иметь разумный лимит памяти, чтобы предотвратить «бегство» потребления ресурсов. Правильные лимиты уменьшают конкуренцию за память и повышают общую стабильность системы.
Как работают лимиты памяти в Docker
Docker позволяет задать два типа лимитов: жёсткие и мягкие. Они по-разному влияют на доступность памяти и поведение при превышении лимита.
- Жёсткие лимиты ограничивают объём памяти, доступный контейнеру. При превышении ядро обычно завершает процесс (OOM-killer).
- Мягкие лимиты указывают ожидаемый объём использования. Контейнер может использовать больше памяти, если есть свободная физическая память. Но при дефиците памяти ОС может принудительно завершить процесс, если он превышает мягкий лимит.
Кроме того, Docker предоставляет настройки swap и поведение при ошибках OOM. Ниже — практические примеры и рекомендации.
Установка жёстких и мягких лимитов памяти
Жёсткий лимит задаётся флагом -m или --memory команды docker run.
-mНапример, значения:
512m(мегабайты) или
2g(гигабайты):
$ docker run --memory=512m my-app:latestКонтейнеры имеют минимальное требование памяти 6MB. Попытка указать значение меньше 6m вызовет ошибку.
Мягкий лимит задаётся через --memory-reservation. Он должен быть меньше чем --memory. Этот лимит действует только при конкуренции за память или при низком уровне свободной ОЗУ на хосте.
$ docker run --memory=512m --memory-reservation=256m my-app:latestВ этом примере контейнер резервирует 256MB памяти. Если процесс использует 300MB и на хосте идёт дефицит, процесс может быть завершён. При превышении 512MB контейнер всегда остановится.
Важные примечания
- Указывайте
--memory-reservationтолько для рабочих нагрузок, которые способны адаптироваться к уменьшению памяти. - Для JVM-приложений учитывайте –Xmx и поведение сборщика мусора. Мягкий лимит может вызвать непредсказуемое задержание GC.
Управление swap
Swap позволяет дописать содержимое памяти на диск после исчерпания RAM. Флаг --memory-swap контролирует общий объём доступной памяти вместе со swap. Работает только вместе с --memory.
$ docker run --memory=512m --memory-swap=762m my-app:latestВ этом примере контейнер видит 762MB общей памяти: 512MB RAM и 250MB swap на диске.
Если задать только --memory без --memory-swap, контейнер получает такой же объём swap, как и RAM:
$ docker run --memory=512m my-app:latestЗдесь всего 1024MB: 512MB RAM + 512MB swap.
Чтобы отключить swap, установите --memory-swap равным --memory. Тогда весь доступный объём будет только RAM.
Замечание: swap действует только если он включён на хосте. Отчёты о swap внутри контейнера ненадёжны — утилиты вроде free покажут swap хоста, а не то, что реально доступно контейнеру.
Отключение убийцы процессов при OOM
При ошибке OOM ядро обычно завершает процесс в контейнере, и контейнер выходит с кодом 137.
Флаг --oom-kill-disable отключает это поведение. Вместо завершения процессам будет блокироваться выделение новой памяти — они «зависнут», пока не освободят память или контейнер не перезапустят вручную.
$ docker run --memory=512m --oom-kill-disable my-app:latestИспользуйте этот флаг только если у вас есть механизмы для наблюдения и восстановления при OOM. Часто лучше позволить ядру завершить процесс, чтобы orchestrator (например, systemd или Kubernetes) мог перезапустить контейнер.
Практическая методология: как выбрать лимиты
Мини-методика для настройки лимитов на сервис:
- Измерьте пиковое и среднее потребление памяти в тестовой среде при рабочей нагрузке.
- Установите
--memoryчуть выше пиков (например, +10–20%). - Установите
--memory-reservationравным среднему плюс буфер (например, среднее +10%). - Тестируйте при искусственной нехватке памяти: смоделируйте наагрузйку и проверьте рестарты.
- Внедрите в staging, далее — в production по фазам.
Критерии приёмки
- Контейнер не должен регулярно завершаться с кодом 137 при нормальной нагрузке.
- При искусственной нехватке памяти процесс корректно рестартует и возвращается в работоспособное состояние.
- Метрики использования памяти стабильны и предсказуемы в течение 7 дней нагрузки.
Альтернативные подходы и совместимость
- Kubernetes: используйте limits.memory и requests.memory. Kubernetes переводит эти поля в cgroups и управляет OOM соответственно.
- cgroups v2: в современных ядрах поведение меняется; проверьте поддержку cgroup v2 на хосте и особенности ограничений.
- Systemd + docker: если вы запускаете контейнеры через systemd, дополнительно проверьте настройки slice и MemoryAccounting.
Когда этот подход не подходит
- Реaltime-приложения с жёстким временем отклика могут страдать при использовании swap.
- Приложения, которые само-оптимизируются под доступную память (например, базы данных), требуют специфичных внутренних конфигураций помимо внешних лимитов.
Хаки и эвристики для установки лимитов
- Если приложение быстро растёт по памяти — начните с мягкого лимита, чтобы дать системе гибкость.
- Для краткоживущих процессов выставляйте низкие
--memory, чтобы быстрее обнаруживать утечки. - Для JVM учитывайте не только heap (-Xmx), но и native memory и metaspace.
Факто-бокс
- Минимальная память контейнера: 6MB
- Пример команд:
--memory=512m,--memory-reservation=256m,--memory-swap=762m - Код выхода при OOM-kill: 137
Руководство по развёртыванию лимитов в кластере (Playbook)
- Соберите метрики по памяти по всем сервисам в течение 7–14 дней.
- Подготовьте список сервисов с текущим пиком, средним и 95-й перцентиль.
- Предложите
--memory= 95-й перцентиль + 10%. Предложите--memory-reservation= медиана + 10%. - Разверните в тестовой группе (10% контейнеров). Наблюдайте 72 часа.
- Если сервисы стабильны — расширьте до 50%, затем до 100%.
- Документируйте политику лимитов для новых сервисов.
Роль‑ориентированные чек‑листы
Для разработчика:
- Измерьте использование памяти локально и в staging.
- Установите JVM-параметры (если применимо).
- Обновите Dockerfile и docker run/Compose конфигурацию с рекомендуемыми лимитами.
Для инженера DevOps:
- Соберите метрики по сервисам.
- Протестируйте поведение OOM и swap в staging.
- Автоматизируйте внедрение лимитов через CI/CD.
Для инженера по наблюдаемости:
- Настройте алерты по использованию памяти и по перезапускам с кодом 137.
- Ведите дашборд по средней и пиковй памяти на контейнер.
Тесты и критерии приёмки
Тестовые сценарии:
- Нормальная нагрузка: контейнер не превышает
--memoryи не рестартует. - Пиковая нагрузка выше
--memory-reservationно ниже--memory: контейнер продолжает работу, но генерирует предупреждения алерта. - Искусственный OOM: при превышении
--memoryконтейнер завершается кодом 137, orchestrator перезапускает контейнер. - Поведение с
--oom-kill-disable: при исчерпании памяти процесс блокируется, контейнер не завершён, наблюдается зависание.
Критерии приёмки описаны выше в разделе «Критерии приёмки».
Отладка и распространённые проблемы
- Команда
docker statsпоказывает использование памяти контейнера в реальном времени. - Если внутри контейнера
freeпоказывает необычные значения — помните, что они отражают состояние хоста. - Логи с кодом выхода 137 указывают на OOM-killer. Проверьте dmesg и syslog на события OOM.
- Если swap не действует — убедитесь, что swap включён на хосте и что Docker запущен с нужными правами.
Decision tree (Как выбрать конфигурацию памяти)
flowchart TD
A[Начало: нет лимитов] --> B{Нагрузка стабильна?}
B -- Да --> C[Установить --memory = пиковое + 10%]
B -- Нет --> D{Есть ли автоскейлинг?}
D -- Да --> E[Установить --memory-reservation = медиана + 10%]\
D -- Нет --> F[Установить --memory и включить мониторинг]
C --> G[Тестировать 72 часа]
E --> G
F --> G
G --> H{Появляются OOM?}
H -- Да --> I[Увеличить --memory или оптимизировать приложение]
H -- Нет --> J[Применить на проде постепенно]Сравнение с Kubernetes
Kubernetes предоставляет поля requests и limits. Requests влияют на планирование, а limits — на поведение OOM. Карта соответствия:
- Docker
--memory~= Kubernetes limits.memory - Docker
--memory-reservation~= Kubernetes requests.memory
Если вы используете Kubernetes, применяйте те же методики измерения и тестирования.
Краткое резюме
Docker-контейнеры по умолчанию не ограничены по памяти. Используйте --memory для жёсткого контроля и --memory-reservation для гибкости. Настройте swap осознанно и тестируйте поведение при OOM. Автоматизируйте внедрение лимитов и мониторьте их эффект.
Важно: корректные лимиты повышают надёжность системы и делают поведение приложений предсказуемым.
Последние шаги
- Соберите метрики и составьте план внедрения.
- Тестируйте на staging и применяйте поэтапно.
- Настройте алерты на перезапуски и высокое использование памяти.
Обратите внимание
- Не используйте
--oom-kill-disableбез зрелых процедур восстановления. - Swap может скрыть проблемы с утечками памяти и ухудшить производительность.
Ограничения и дальнейшие шаги
Если вы управляете кластером, рассмотрите перенос управления ресурсами на оркестратор (Kubernetes) и централизованное управление метриками и алертами.
Похожие материалы
Несколько аккаунтов Skype: Multi Skype Launcher
Журнал для работы: повысить продуктивность
Персональные звуки уведомлений на Android
Скачивание шоу Hulu для офлайн‑просмотра
Microsoft Start: персонализированная новостная лента