n1ro°
RU

Текст и данные · Инструкция

Docker memory-swap и memory: как работают лимиты

Docker memory-swap и memory часто путают из-за названия второго флага.

Редакция N1RO · Проверено

Короткий ответ

`--memory` ограничивает физическую память контейнера, а `--memory-swap` задаёт общий предел RAM+swap и имеет смысл только вместе с `--memory`. Например, `--memory=512m --memory-swap=1g` даёт до 512 МБ RAM и ещё до 512 МБ swap.

Почему --memory-swap без --memory не решает задачу

Docker описывает `--memory-swap` как модификатор, который имеет смысл только при заданном `--memory`. Сначала вы устанавливаете жёсткий потолок RAM, затем решаете, сколько общего виртуального объёма контейнер может занять с учётом swap. Минимальный допустимый `--memory` в документации Docker — 6 МБ, но практически настолько маленькие значения подходят только для очень специфичных процессов.

Важно понимать и поведение по умолчанию. Если `--memory` задан, а `--memory-swap` не указан, Docker документирует суммарный доступный объём RAM+swap как примерно удвоенный `--memory`, если на хосте вообще есть swap и ядро поддерживает соответствующее ограничение. Это поведение часто удивляет: оператор думает, что ограничил контейнер 512 МБ, а процесс некоторое время продолжает жить, активно свопясь.

Отдельно учитывайте поведение, когда `--memory-swap` не задан. По текущей документации Docker, если `--memory` установлен, а `--memory-swap` пропущен, контейнер при наличии swap на хосте может получить swap объёмом примерно с установленный memory limit: например, 300 МБ RAM плюс до 300 МБ swap. Значение `--memory-swap=0` трактуется как отсутствие настройки, а `--memory-swap=-1` снимает лимит swap до доступного хосту объёма. Поэтому `0`, «не указано» и `-1` нельзя считать тремя вариантами одного и того же поведения. Если цель — запретить swap, задайте `--memory-swap` равным `--memory`; если цель — разрешить контролируемый запас, задайте общий потолок больше `--memory` и посчитайте разницу.

Совет: Проверяйте `docker info`: Docker предупреждает, если ядро не поддерживает swap limits. На таких системах флаг может не дать ожидаемого контроля.

Как выбрать лимиты на практике

  1. Сначала измерьте реальное потребление приложения под нормальной и пиковой нагрузкой через `docker stats` и метрики самого процесса.
  2. Задайте `--memory` выше устойчивого рабочего набора с запасом на кратковременные пики, а не по среднему значению.
  3. Решите, допустим ли swap. Для latency-чувствительного сервиса лучше небольшой запас RAM и ограниченный либо отключённый swap; для фонового процесса swap может быть приемлем как буфер.
  4. Если swap нужен, задайте общий предел: например, `--memory=1g --memory-swap=1500m` даёт около 1 ГБ RAM и до 500 МБ swap.
  5. Нагрузочно протестируйте контейнер и проверьте, не появляются ли OOMKill, длительные паузы или сильная деградация времени ответа.
  6. Зафиксируйте те же ограничения в Compose или оркестраторе, если контейнер запускается не вручную.

Предупреждение: Не копируйте лимиты между разными сервисами. JVM, Node.js, базы данных и небольшие CLI-процессы по-разному реагируют на жёсткий потолок памяти.

Как полностью запретить swap для контейнера

По документации Docker, чтобы контейнер не использовал swap, задайте `--memory-swap` равным `--memory`. Например: `docker run --memory=512m --memory-swap=512m ..`. Общий потолок равен объёму RAM, поэтому места для дополнительного swap не остаётся. Это полезно, когда производительность при свопинге хуже контролируемого падения процесса и автоматического рестарта.

Но запрет swap не создаёт память из воздуха. Если рабочий набор превысит лимит и процесс не освободит память, ядро может завершить процесс внутри контейнера по OOM. Поэтому такой режим разумен только вместе с наблюдением за потреблением, healthcheck/restart policy и адекватным запасом. Docker также предупреждает не отключать OOM-killer без установленного memory limit: это способно увеличить риск нехватки памяти уже на всём хосте.

Важно: Если контейнер неожиданно завершился, проверьте `docker inspect` на `OOMKilled` и сопоставьте событие с метриками памяти. Просто увеличивать лимит без диагностики не всегда правильно.

Что делает --memory-swappiness и почему это не второй лимит

`--memory-swappiness` управляет тем, насколько охотно ядро может выгружать анонимные страницы контейнера в swap. Значение 0 отключает такую выгрузку, 100 делает анонимные страницы максимально доступными для свопинга; если параметр не задан, поведение наследуется от хоста. Это другой рычаг, чем `--memory-swap`: один задаёт допустимый объём, второй — склонность использовать swap.

Понижать swappiness имеет смысл, когда вы хотите сохранить рабочий набор в RAM, но всё же оставить swap как аварийный буфер. Однако итог зависит от Linux cgroups и настроек хоста, а Docker Desktop добавляет ещё один слой: на macOS и Windows контейнеры работают внутри Linux VM, у которой есть собственные лимиты CPU, memory и swap. Поэтому одинаковая команда может упираться не только в контейнерный лимит, но и в ресурсы виртуальной машины Docker Desktop.

Совет: Внутри контейнера `free` может показывать swap хоста, а не реальный доступный лимит контейнера. Для контроля опирайтесь на настройки Docker/cgroups и метрики, а не только на `free`.

Рабочие примеры для 512 МБ и 2 ГБ RAM

Для небольшого веб-сервиса, который стабильно использует 300–350 МБ и иногда поднимается до 450 МБ, стартовая конфигурация может выглядеть как `--memory=512m --memory-swap=768m`. Она оставляет 256 МБ потенциального swap, но не позволяет процессу бесконечно вытеснять память на диск. После теста лимит корректируют по реальным пикам.

Для сервиса, где задержки критичны и приложение умеет корректно перезапускаться, можно начать с `--memory=2g --memory-swap=2g`, то есть без swap. Если контейнер ловит OOM при ожидаемой нагрузке, это сигнал разобраться в heap, кеше и утечках, а не автоматически ставить `--memory-swap=-1`.

Не используйте unlimited swap как универсальную страховку. Docker прямо отмечает, что swap медленнее RAM. Он может выиграть время при кратком всплеске, но длительная активная подкачка обычно означает, что ресурсная модель сервиса выбрана неверно.

После выбора лимита проверьте его под реальной нагрузкой, а не только по успешному старту. Снимайте `docker stats`, смотрите, появляется ли заметный swap и не растёт ли latency приложения. Затем создайте краткий контролируемый стресс-тест с запасом по хосту и проверьте поведение при приближении к `--memory`: одни приложения корректно отдают ошибки, другие получают OOM kill. Если сервис критичен, отдельно настройте мониторинг рестартов и OOM-событий. Лимит памяти полезен только вместе с наблюдаемостью — иначе вы увидите уже конечный симптом «контейнер перезапустился», но не момент, когда рабочий набор перестал помещаться в RAM.

Что учитывать

Docker описывает `--memory-swap` как модификатор, который имеет смысл только при заданном `--memory`. Сначала вы устанавливаете жёсткий потолок RAM, затем решаете, сколько общего виртуального объёма контейнер может занять с учётом swap. Минимальный допустимый `--memory` в документации Docker — 6 МБ, но практически настолько маленькие значения подходят только для очень специфичных процессов. Важно понимать и поведение по умолчанию. Если `--memory` задан, а `--memory-swap` не указан, Docker…

Источники и проверка

Фактическая часть сверена по первичным источникам (в т.ч. Resource constraints). Пример и формулировки — редакция N1RO на 2026-09-22.