Перейти к содержанию

0008. Подкачка на узле и разрешение её подам

Дата: август 2026. Состояние: принято.

Контекст

На узле 3,8 ГБ доступной памяти. Сумма запросов всех подов — около 2,6 ГБ, сумма пределов — существенно больше: пределы поставлены с запасом на пики, и это правильно. Значит, в момент одновременного пика памяти не хватит.

Kubernetes по умолчанию требует, чтобы подкачка была выключена: kubelet не запускается на узле с включённой подкачкой без явного разрешения. Логика понятна — подкачка делает поведение непредсказуемым и прячет нехватку памяти вместо того, чтобы её показать.

Решение

Включить файл подкачки 4 ГБ и разрешить подам им пользоваться в ограниченном режиме:

failSwapOn: false
memorySwap:
  swapBehavior: LimitedSwap

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

Дополнительно: vm.swappiness снижен до 10 — ядро использует подкачку только при реальной нехватке, а не для упреждающего вытеснения. Заданы резервы под системные и служебные процессы, чтобы вытеснение начиналось предсказуемо.

Последствия

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

Первая же проверка сценария с утечкой памяти показала цену настройки. Балласт был ограничен 512 МБ — больше, чем куча пода, и больше, чем предел его контейнера. Под честно исчерпал память и перезапустился, но по пути занял почти всю подкачку узла, узел объявил нехватку памяти, и kubelet вытеснил тот же под ещё раз, уже как нарушителя. Наблюдаемость при этом уцелела только потому, что Prometheus расходовал меньше своего запроса и стоял в очереди на вытеснение последним.

Вывод занесён в настройку: предел балласта опущен до 256 МБ. Сценарий по-прежнему кончается исчерпанием памяти внутри виртуальной машины Java и перезапуском контейнера, но узел до нехватки памяти уже не доходит.

Чем заплатили. Замедление вместо честного отказа. Под, ушедший в подкачку, отвечает медленно, и это выглядит как деградация приложения, хотя причина — в узле. Поэтому есть отдельное правило оповещения MetroskopSwapHeavilyUsed: занятая больше чем на 60 % четверть часа подкачка означает, что памяти не хватает по-настоящему, и это уже не страховка, а образ жизни.

Второе: подкачка на виртуальном диске сильно медленнее оперативной памяти. Перцентили времени ответа при активной подкачке ухудшаются заметно, и на графике это видно.

Что рассматривалось

Без подкачки, как требует умолчание — честнее по поведению, но на этом узле означало бы, что любой пик заканчивается убийством случайного пода. Для стенда, где отказы вызывают нажатием кнопки, это гарантированная потеря наблюдаемости в самый нужный момент.

Уменьшить пределы до запросов — устранило бы перерасход, но лишило бы поды права на пик. Приложение на Java с непредсказуемым поведением сборщика мусора в такой схеме перезапускалось бы регулярно.

Неограниченный режим подкачки — под смог бы уйти в подкачку целиком, и деградация стала бы неограниченной. Ограниченный режим держит её в пределах разницы между пределом и запросом.