Функція Kubernetes NodeSwap, яка, ймовірно, отримає статус stable у майбутньому релізі Kubernetes v1.34, дозволяє використання swap: це значна зміна порівняно з традиційною практикою вимкнення swap для передбачуваної продуктивності. Ця стаття зосереджена виключно на налаштуванні swap на вузлах Linux, де ця функція доступна. Дозволяючи вузлам Linux використовувати вторинне сховище для додаткової віртуальної памʼяті, коли фізична RAM вичерпана, підтримка swap на вузлах спрямована на покращення використання ресурсів та зменшення кількості припинень процесів через нестачу памʼяті (OOM kills).
Однак, увімкнення swap не є рішенням "під ключ". Продуктивність та стабільність ваших вузлів під тиском на памʼять критично залежать від набору параметрів ядра Linux. Неправильна конфігурація може призвести до погіршення продуктивності та втручання в логіку виселення Kubelet.
У цьому дописі ми розглянемо критичні параметри ядра Linux, які керують поведінкою swap. Ми дослідимо, як ці параметри впливають на продуктивність робочих навантажень Kubernetes, використання swap та критичні механізми виселення. Також представимо різні результати тестів, що демонструють вплив різних конфігурацій, та поділимось своїми висновками щодо досягнення оптимальних налаштувань для стабільних та високопродуктивних кластерів Kubernetes.
На високому рівні, ядро Linux керує памʼяттю через сторінки, зазвичай розміром 4КіБ. Коли фізична памʼять стає обмеженою, алгоритм заміни сторінок ядра вирішує, які сторінки перемістити у swap-простір. Хоча точна логіка є складною оптимізацією, на цей процес прийняття рішень впливають певні ключові фактори:
Важливо розуміти, що не всі сторінки памʼяті однакові. Ядро розрізняє анонімну памʼять та памʼять з файловою підтримкою.
Анонімна памʼять: Це памʼять, яка не підтримується конкретним файлом на диску, така як купа та стек програми. З точки зору програми це приватна памʼять, і коли ядру потрібно звільнити ці сторінки, воно повинно записати їх на спеціально відведений пристрій swap.
Памʼять з файловою підтримкою: Ця памʼять підтримується файлом у файловій системі. Сюди входять виконуваний код програми, спільні бібліотеки та кеші файлової системи. Коли ядру потрібно звільнити ці сторінки, воно може просто відкинути їх, якщо вони не були модифіковані ("чисті"). Якщо сторінка була модифікована ("брудна"), ядро спочатку повинно записати зміни назад у файл, перш ніж її можна буде відкинути.
Хоча система без swap все ще може звільняти памʼять з чистими файлами під тиском, скидаючи їх, у неї немає способу скинути анонімну памʼять. Увімкнення swap надає цю можливість, дозволяючи ядру переміщати менш часто використовувані сторінки памʼяті на диск, щоб зберегти памʼять і уникнути використання механізму OOM-очищення системи.
Щоб ефективно налаштувати поведінку swap, Linux надає кілька параметрів ядра, якими можна керувати за допомогою sysctl.
vm.swappiness: Це найвідоміший параметр. Це значення від 0 до 200 (100 у старіших ядрах), яке контролює перевагу ядра для обміну анонімними сторінками памʼяті проти відновлення сторінок памʼяті з файловою підтримкою (кеш сторінок).vm.min_free_kbytes: Цей параметр вказує ядру зберігати мінімальну кількість памʼяті вільною як буфер. Коли кількість вільної памʼяті падає нижче цього буфера безпеки, ядро починає більш агресивно відновлювати сторінки (робити їх своп, а врешті-решт виконувати OOM-очищення).min_free_kbytes ефективно підвищує мінімальне значення для вільної памʼяті, змушуючи ядро раніше ініціювати своп під час тиску на памʼять.vm.watermark_scale_factor: Ця настройка контролює проміжок між різними водяними знаками: min, low та high, які обчислюються на основі min_free_kbytes.low: Коли вільна памʼять нижча за цю позначку, процес ядра kswapd прокидається, щоб відновити сторінки у фоновому режимі. Саме тоді починається цикл свопу.min: Коли вільна памʼять досягає цього мінімального рівня, агресивне відновлення сторінок блокує виділення процесів. Нездатність відновити сторінки призведе до OOM-очищень.high: Відновлення памʼяті зупиняється, як тільки вільна памʼять досягає цього рівня.watermark_scale_factor створює більший буфер між водяними знаками low та min. Це дає kswapd більше часу для поступового відновлення памʼяті, перш ніж система потрапить у критичний стан.У типовому серверному робочому навантаженні у вас може бути довготривалий процес з деякою памʼяттю, яка стає "холодною". Вищий показник swappiness може звільнити RAM, обмінюючи холодну памʼять, для інших активних процесів, які можуть виграти від збереження свого кешу файлів.
Налаштування параметрів min_free_kbytes та watermark_scale_factor для раннього переміщення вікна обміну надасть більше місця для kswapd, щоб скинути памʼять на диск і запобігти OOM-очищенням під час раптових сплесків потреби в памʼяті.
Щоб зрозуміти реальний вплив цих параметрів, ми провели серію стрес-тестів.
n2-standard-2 (8GiB RAM, 50GB swap на диску pd-balanced, без шифрування), Ubuntu 22.04memory.swap.max=0 у їхніх відповідних cgroups.Був запущений под зі стрес-тестом на вузлах з різними налаштуваннями swappiness (0, 60 та 90) з варіюванням параметрів min_free_kbytes та watermark_scale_factor, щоб спостерігати за результатами під час сильного виділення памʼяті та тиску введення/виведення.
Графік нижче, з тесту на стрес з швидкістю 100MBps, показує роботу swap. Коли вільна памʼять (на графіку "Використання памʼяті") зменшується, використання swap (Використано swap (GiB)) та активність swap-виводу (Swap Out (MiB/s)) зростають. Критично, оскільки система більше покладається на swap, також зростає I/O активність та відповідний час очікування (IO Wait % на графіку "Використання CPU"), що вказує на навантаження на CPU.

Мої початкові тести з типовими параметрами ядра (swappiness=60, min_free_kbytes=68MB, watermark_scale_factor=10) швидко призвели до OOM-очищень і навіть несподіваних перезавантажень вузлів під час високого навантаження на памʼять. З вибором відповідних параметрів ядра можна досягти хорошого балансу між стабільністю вузла та продуктивністю.
swappinessПараметр swappiness безпосередньо впливає на вибір ядра між відновленням анонімної памʼяті (свопом) та скиданням кешу сторінок. Щоб це спостерігати, я провів тест, у якому один под створював та утримував тиск на кеш файлів, після чого другий под виділяв анонімну памʼять зі швидкістю 100MB/s, щоб спостерігати за налаштуваннями ядра у відновленні:
Мої висновки виявили чіткий компроміс:
swappiness=90: Ядро проактивно робило своп неактивної анонімної памʼяті, щоб зберегти кеш файлів. Це призвело до високого та стабільного використання swap та значної I/O активності ("Blocks Out"), що, в свою чергу, викликало сплески очікування I/O на CPU.swappiness=0: Ядро надавало перевагу скиданню сторінок кешу файлів, затримуючи споживання swap. Однак важливо розуміти, що це не вимикає своп. Коли тиск на памʼять був високим, ядро все ще робило своп анонімної памʼяті на диск.Вибір залежить від робочого навантаження. Для робочих навантажень, чутливих до затримок введення/виведення, бажано нижче значення swappiness. Для робочих навантажень, які покладаються на великий та часто використовуваний кеш файлів, може бути корисним вищий показник swappiness, за умови, що диск достатньо швидкий, щоб впоратися з навантаженням.
Найбільшою проблемою, з якою я зіткнувся, було взаємодія між швидким виділенням памʼяті та механізмом виселення Kubelet. Коли мій тестовий под, який навмисно був налаштований на перевищення памʼяті, виділяв її високими темпами (наприклад, 300-500 MBps), система швидко залишалася без вільної памʼяті.
Стандартно буфер для відновлення був занадто малим. Перш ніж kswapd встигав звільнити достатньо памʼяті шляхом свопу, вузол потрапляв у критичний стан, що призводило до двох можливих наслідків:
memory.available нижче його порогу, він виселяв под.Щоб помʼякшити це, я налаштував водяні знаки:
min_free_kbytes до 512MiB: Це змушує ядро почати відновлення памʼяті набагато раніше, забезпечуючи більший буфер безпеки.watermark_scale_factor до 2000: Це розширило проміжок між водяними знаками low та high (з ≈337MB до ≈591MB у моєму тестовому вузлі в /proc/zoneinfo), ефективно збільшуючи вікно обміну.Ця комбінація надала kswapd більшу операційну зону та більше часу для обміну сторінок на диск під час сплесків памʼяті, успішно запобігаючи як передчасним виселенням, так і OOM-очищенням у моїх тестових запусках.
Таблиця порівнює рівні водяних знаків з /proc/zoneinfo (недоступний NUMA-узел):
min_free_kbytes=67584KiB та watermark_scale_factor=10 | min_free_kbytes=524288KiB та watermark_scale_factor=2000 |
|---|---|
| Вузол 0, зона Нормальна вільні сторінки 583273 підвищення 0 мін 10504 низький 13130 високий 15756 охоплено 1310720 присутній 1310720 керується 1265603 | Вузол 0, зона Нормальна вільні сторінки 470539 мін 82109 низький 337017 високий 591925 охоплено 1310720 присутній 1310720 керується 1274542 |
Графік нижче показує, що розмір буфера ядра та коефіцієнт масштабування відіграють вирішальну роль у визначенні того, як система реагує на навантаження на памʼять. З правильним поєднанням цих параметрів система може ефективно використовувати простір swap, щоб уникнути виселення та підтримувати стабільність.

Увімкнення swap у Kubernetes є потужним інструментом, але воно повʼязане з ризиками, якими потрібно управляти за допомогою ретельного налаштування.
Ризик погіршення продуктивності Своп значно повільніший, ніж доступ до RAM. Якщо активний робочий набір програми буде переміщено до свопу, її продуктивність різко постраждає через високі часи очікування введення/виведення (thrashing). Своп бажано забезпечити сховищем на базі SSD для покращення продуктивності.
Ризик маскування витоків пам'яті Своп може приховати витоки памʼяті в програмах, які інакше призвели б до швидкого OOM-очищення. З свопом програма з протіканням памʼяті може повільно погіршувати продуктивність вузла з часом, ускладнюючи діагностику першопричини.
Ризик відключення виселень Kubelet активно моніторить вузол на предмет тиску на памʼять та завершує поди, щоб відновити ресурси. Неправильне налаштування може призвести до OOM-очищень до того, як kubelet матиме можливість виселити поди відповідним чином. Правильно налаштований min_free_kbytes є критично важливим для забезпечення ефективності механізму виселення kubelet.
Разом, водяні знаки ядра та поріг виселення kubelet створюють серію зон тиску памʼяті на вузлі. Параметри порогу виселення потрібно відрегулювати, щоб налаштувати керовані Kubernetes виселення, які відбуваються до OOM-очищення.

Як показано на діаграмі, ідеальна конфігурація полягає в тому, щоб створити достатньо велике "вікно обміну" (між водяними знаками high та min), щоб ядро могло обробляти тиск памʼяті шляхом свопу, перш ніж доступна памʼять впаде в зону виселення/прямого відновлення.
На основі цих висновків, я рекомендую наступне як відправну точку для вузлів Linux з увімкненим swap. Вам слід провести тестування продуктивності з вашими власними робочими навантаженнями.
vm.swappiness=60: Linux стандартно є хорошою відправною точкою для загальних робочих навантажень. Однак, ідеальне значення залежить від робочого навантаження, і застосунки, чутливі до swap, можуть потребувати більш ретельного налаштування.vm.min_free_kbytes=500000 (500MB): Встановіть це на досить високе значення (наприклад, 2-3% від загальної памʼяті вузла), щоб забезпечити розумний буфер безпеки.vm.watermark_scale_factor=2000: Створіть більше вікно для роботи kswapd, запобігаючи OOM kills під час раптових сплесків виділення памʼяті.Я рекомендую проводити тести продуктивності з вашими власними робочими навантаженнями в тестових середовищах при першому налаштуванні swap у вашому кластері Kubernetes. Продуктивність swap може бути чутливою до різних особливостей середовища, таких як навантаження на CPU, тип диска (SSD проти HDD) та шаблони введення/виведення.