Виселення внаслідок тиску на вузол — це процес, під час якого kubelet проактивно завершує роботу Podʼів, щоб звільнити ресурси на вузлах.
kubelet моніторить ресурси, такі як памʼять, простір на диску та inode файлової системи на вузлах вашого кластера. Коли один або кілька з цих ресурсів досягають певних рівнів використання, kubelet може проактивно завершити зі збоєм один або кілька Podʼів на вузлі, щоб звільнити ресурси та запобігти їх вичерпанню.
Під час виселення внаслідок тиску на вузол kubelet встановлює фазу для вибраних Podʼів в значення Failed та завершує роботу Podʼа.
Виселення внаслідок тиску на вузол не є тим самим, що й виселення, ініційоване API.
kubelet не враховує ваш налаштований PodDisruptionBudget або параметр terminationGracePeriodSeconds Podʼа. Якщо ви використовуєте порогові значення мʼякого виселення, kubelet дотримується вашого налаштованого evictionMaxPodGracePeriod. Якщо ви використовуєте порогові значення жорсткого виселення, kubelet використовує період коректного завершення 0s (негайне вимкнення) для завершення роботи.
kubelet намагається звільнити ресурси на рівні вузла перед тим, як завершувати роботу Podʼів кінцевих користувачів. Наприклад, він видаляє невикористані образи контейнерів, коли дискових ресурсів недостатньо.
Якщо Podʼами керує обʼєкт керування робочим навантаженням (такий як StatefulSet або Deployment), який замінює Podʼи зі збоєм, панель управління (kube-controller-manager) створює нові Podʼи на місці виселених Podʼів.
Якщо ви запускаєте статичний Pod на вузлі, який перебуває під тиском на ресурси, kubelet може виселити цей статичний Pod. Потім kubelet намагається створити заміну, оскільки статичні Podʼи завжди представляють намір запустити Pod на цьому вузлі.
Kubelet враховує пріоритет статичного Podʼа під час створення заміни. Якщо маніфест статичного Podʼа вказує низький пріоритет, і в межах панелі управління кластера визначені Podʼи з вищим пріоритетом, і вузол перебуває під тиском на ресурси, то kubelet може не мати можливості звільнити місце для цього статичного Podʼа. Kubelet продовжує намагатися запустити всі статичні Podʼи навіть тоді, коли на вузлі є тиск на ресурси.
Kubelet використовує різні параметри для прийняття рішень щодо виселення, такі як:
Сигнали виселення — це поточний стан певного ресурсу в конкретний момент часу. Kubelet використовує сигнали виселення для прийняття рішень про виселення, порівнюючи їх з порогами виселення, які є мінімальною кількістю ресурсу, яка повинна бути доступна на вузлі.
Kubelet використовує такі сигнали виселення:
| Сигнал виселення | Опис | Linux Only |
|---|---|---|
memory.available | memory.available := node.status.capacity[memory] - node.stats.memory.workingSet | |
nodefs.available | nodefs.available := node.stats.fs.available | |
nodefs.inodesFree | nodefs.inodesFree := node.stats.fs.inodesFree | • |
imagefs.available | imagefs.available := node.stats.runtime.imagefs.available | |
imagefs.inodesFree | imagefs.inodesFree := node.stats.runtime.imagefs.inodesFree | • |
containerfs.available | containerfs.available := node.stats.runtime.containerfs.available | |
containerfs.inodesFree | containerfs.inodesFree := node.stats.runtime.containerfs.inodesFree | • |
pid.available | pid.available := node.stats.rlimit.maxpid - node.stats.rlimit.curproc | • |
У цій таблиці стовпець Опис показує, як kubelet отримує значення сигналу. Кожен сигнал підтримує відсоткове або літеральне значення. Kubelet обчислює відсоткове значення відносно загальної місткості, повʼязаної із сигналом.
На вузлах Linux значення для memory.available походить з cgroupfs замість таких інструментів, як free -m. Це важливо, оскільки free -m не працює у контейнері, і якщо користувачі використовують можливість ресурсів вузла, доступних для розподілу, рішення про вичерпання ресурсів приймаються локально в частині ієрархії cgroup, що належить Podʼам кінцевих користувачів, а також на кореневому вузлі. Цей скрипт або скрипт cgroupv2 відтворює той самий набір кроків, які kubelet виконує для обчислення memory.available. Kubelet виключає зі свого обчислення inactive_file (кількість байтів памʼяті, що має файлове резервне сховище, у неактивному списку LRU), оскільки припускає, що цю памʼять можна звільнити під тиском.
На вузлах із налаштованими hugepages kubelet віднімає загальну місткість hugepages вузла від memory.available, яку використовує менеджер виселення. Без цього коригування зарезервована під hugepages оперативна памʼять завищує AvailableBytes, оскільки контролер cgroup памʼяті не відстежує виділення hugetlb у своєму робочому наборі. Це може затримати виселення та призвести до OOM-завершень. Щоб відновити попередню поведінку, вимкніть функціональну можливість HugepageAwareEviction.
На Windows-вузлах значення memory.available отримується з глобального рівня комітів памʼяті вузла (запитується через системний виклик GetPerformanceInfo()) шляхом віднімання глобального значення CommitTotal від CommitLimit вузла. Зверніть увагу, що значення CommitLimit може змінюватися, якщо змінюється розмір файлу підкачки вузла!
Kubelet розпізнає три конкретні ідентифікатори файлової системи, які можна використовувати із сигналами виселення (<identifier>.inodesFree або <identifier>.available):
nodefs: Основна файлова система вузла, використовується для локальних дискових томів, томів emptyDir, які не підтримуються памʼяттю, зберігання логів, ефемерного зберігання та іншого. Наприклад, nodefs містить /var/lib/kubelet.
imagefs: Необовʼязкова файлова система, яку середовища виконання контейнерів можуть використовувати для зберігання образів контейнерів (які є шарами тільки для читання). Якщо окремої файлової системи containerfs немає, файлова система образів також зберігає шари контейнера, доступні для запису.
containerfs: Необовʼязкова файлова система, яку середовища виконання контейнерів можуть використовувати для зберігання записуваних шарів контейнерів. Коли використовується containerfs, файлова система imagefs може бути розділена так, щоб зберігати лише образи (шари тільки для читання) і більше нічого.
Ці ідентифікатори описують файлові системи так, як їх бачить kubelet. Вони не завжди означають три різні точки монтування: у звичайних конфігураціях два або всі три ідентифікатори можуть посилатися на одну й ту ж базову файлову систему.
Можливість split image filesystem додає нові сигнали виселення, пороги та метрики для containerfs. Щоб використовувати containerfs, у випуску Kubernetes v1.37 потрібно увімкнути функціональну можливість KubeletSeparateDiskGC. Для Kubernetes v1.37 підтримку файлової системи containerfs пропонує лише CRI-O (v1.29 або вище).
Kubelet підтримує три поширені схеми розміщення файлових систем контейнерів:
Усе зберігається на єдиній nodefs, яку також називають «rootfs» або просто «root». У цій схемі nodefs, imagefs та containerfs посилаються на одну й ту саму базову файлову систему.
Сховище середовища виконання контейнерів розміщено на окремому диску, відокремленому від кореневої файлової системи. У цій схемі imagefs та containerfs посилаються на одну й ту саму базову файлову систему, яка зберігає як шари образів, так і записувані шари контейнерів. Це часто називають файловою системою «split disk» (або «separate disk»).
Записувані шари контейнерів розміщено на nodefs, а образи контейнерів (шари лише для читання) зберігаються на окремій imagefs. У цій схемі containerfs та nodefs посилаються на одну й ту саму базову файлову систему. Це часто називають файловою системою «split image».
Kubelet спробує автоматично виявити ці файлові системи з їхньою поточною конфігурацією безпосередньо з базового середовища виконання контейнерів і ігноруватиме інші локальні файлові системи вузла.
Kubelet не підтримує інші файлові системи контейнерів або конфігурації зберігання і наразі не підтримує кілька файлових систем для образів і контейнерів.
Деякі можливості збору сміття kubelet застаріли на користь виселення:
| Наявний прапорець | Обґрунтування |
|---|---|
--maximum-dead-containers | застаріло, щойно старі логи зберігаються поза контекстом контейнера |
--maximum-dead-containers-per-container | застаріло, щойно старі логи зберігаються поза контекстом контейнера |
--minimum-container-ttl-duration | застаріло, щойно старі логи зберігаються поза контекстом контейнера |
Ви можете вказати власні пороги виселення для kubelet, які він буде використовувати під час прийняття рішень про виселення. Ви можете налаштувати мʼякі та жорсткі пороги виселення.
Пороги виселення мають формат [eviction-signal][operator][quantity], де:
eviction-signal — це сигнал виселення, який використовується.operator — це оператор відношення, який ви хочете використати, наприклад, < (менше ніж).quantity — це кількість порогу виселення, наприклад, 1Gi. Значення quantity повинно відповідати представленню кількості, яке використовується в Kubernetes. Ви можете використовувати як літеральні значення, так і відсотки (%).Наприклад, якщо вузол має загальний обсяг памʼяті 10GiB і ви хочете ініціювати виселення, коли доступна памʼять опуститься нижче 1GiB, ви можете визначити поріг виселення як memory.available<10% або memory.available<1Gi (використовувати обидва варіанти одночасно не можна).
Мʼякий поріг виселення поєднує в собі поріг виселення з обовʼязковим пільговим періодом, вказаним адміністратором. Kubelet не виселяє Podʼи до закінчення пільгового періоду. Kubelet повертає помилку під час запуску, якщо ви не вказали пільговий період.
Ви можете вказати як пільговий період для мʼякого порогу виселення, так і максимально дозволений період коректного завершення Podʼів, який kubelet використовуватиме під час виселення. Якщо ви вказали максимально дозволений період і мʼякий поріг виселення досягнуто, kubelet використовує менший із двох періодів. Якщо ви не вказали максимально дозволений період, kubelet негайно завершує роботу виселених Podʼів без коректного завершення.
Ви можете використовувати такі налаштування у файлі конфігурації kubelet:
evictionSoft: Набір порогів виселення, наприклад, memory.available<1.5Gi, які можуть ініціювати виселення Podʼів, якщо вони утримуються протягом вказаного пільгового періоду.evictionSoftGracePeriod: Набір пільгових періодів для виселення, наприклад, memory.available=1m30s, які визначають, як довго мʼякий поріг виселення повинен утримуватися, перш ніж буде ініційовано виселення Podʼа.evictionMaxPodGracePeriod: Максимально дозволений період коректного завершення (у секундах) для використання під час завершення Podʼів у відповідь на досягнення мʼякого порогу виселення.Жорсткий поріг виселення не має пільгового періоду. Коли досягнуто жорсткого порогу виселення, kubelet негайно завершує роботу Podʼів без коректного завершення, щоб звільнити дефіцитний ресурс.
Ви можете використовувати поле evictionHard для налаштування набору жорстких порогів виселення, наприклад memory.available<1Gi.
Kubelet має наступні стандартні жорсткі пороги виселення:
memory.available<100Mi (для вузлів Linux)memory.available<500Mi (для вузлів Windows)nodefs.available<10%imagefs.available<15%nodefs.inodesFree<5% (для вузлів Linux)imagefs.inodesFree<5% (для вузлів Linux)Ці стандартні значення жорстких порогів виселення будуть встановлені лише у випадку, якщо жоден з параметрів не змінено. Якщо ви зміните значення будь-якого параметра, то значення інших параметрів не будуть успадковані як стандартні та будуть встановлені в нуль. Щоб надати власні значення, ви повинні вказати всі пороги відповідно. Ви також можете встановити параметр MergeDefaultEvictionSettings у true у файлі конфігурації kubelet. Якщо встановити true і змінити будь-який параметр, то інші параметри успадкують свої стандартні значення, а не 0.
Стандартні пороги виселення для containerfs.available і containerfs.inodesFree (для Linux-вузлів) будуть встановлені таким чином:
Якщо containerfs та nodefs посилаються на одну й ту саму базову файлову систему, то пороги для containerfs встановлюються такими ж, як і для nodefs.
Якщо containerfs та imagefs посилаються на одну й ту саму базову файлову систему, то пороги для containerfs встановлюються такими ж, як і для imagefs.
Налаштування власних порогів, повʼязаних із containerfs, не підтримується, і буде видано попередження, якщо спробувати це зробити; будь-які надані власні значення будуть ігноровані.
Kubelet оцінює пороги виселення, виходячи з налаштованого housekeeping-interval, стандартне значення якого — 10s.
Kubelet повідомляє про стани вузла, показуючи, що вузол перебуває під тиском через досягнення порогу жорсткого або мʼякого виселення, незалежно від налаштованих пільгових періодів.
Kubelet зіставляє сигнали виселення зі станами вузла таким чином:
| Стан вузла | Сигнал виселення | Опис |
|---|---|---|
MemoryPressure | memory.available | Доступна памʼять на вузлі досягла порогового значення для виселення |
DiskPressure | nodefs.available, nodefs.inodesFree, imagefs.available, imagefs.inodesFree, containerfs.available, або containerfs.inodesFree | Доступне місце на диску та inode на кореневій файловій системі вузла, файловій системі образів або файловій системі контейнерів досягли порогового значення для виселення |
PIDPressure | pid.available | Доступні ідентифікатори процесів на (Linux) вузлі зменшилися нижче порогового значення для виселення |
Панель управління також зіставляє ці стани вузла на taints.
Kubelet оновлює стани вузла відповідно до налаштованого nodeStatusUpdateFrequency, стандартне значення якого — 10s.
У деяких випадках вузли коливаються вище та нижче мʼяких порогів виселення, не утримуючись протягом визначених пільгових періодів. Це призводить до того, що стан вузла, про який повідомляється, постійно перемикається між true та false, що призводить до поганих рішень щодо виселення.
Для захисту від коливань можна використовувати прапорець eviction-pressure-transition-period, який контролює, як довго kubelet повинен чекати перш ніж перевести стан вузла в інше значення. Період переходу має стандартне значення 5m.
Kubelet намагається звільнити ресурси на рівні вузла перед виселенням Podʼів кінцевих користувачів.
Коли вузол повідомляє про стан DiskPressure, kubelet звільняє ресурси на рівні вузла на основі файлових систем на вузлі.
imagefs або containerfsЯкщо на вузлі є лише файлова система nodefs, яка досягає порогів виселення, kubelet звільняє місце на диску в такому порядку:
imagefsЯкщо на вузлі є окрема файлова система imagefs, яку використовують середовища виконання контейнерів, то kubelet робить таке:
nodefs досягає порогів виселення, то kubelet виконує збір сміття для мертвих Podʼів і контейнерів.imagefs досягає порогів виселення, то kubelet видаляє всі невикористані образи.imagefs та containerfsЯкщо на вузлі налаштована окрема containerfs разом із файловою системою imagefs, які використовують середовища виконання контейнерів, то kubelet намагатиметься звільнити ресурси таким чином:
Якщо файлова система containerfs досягає порогів виселення, kubelet виконує збір сміття для мертвих Podʼів і контейнерів.
Якщо файлова система imagefs досягає порогів виселення, kubelet видаляє всі невикористані образи.
Якщо спроби kubelet звільнити ресурси на рівні вузла не знижують сигнал виселення нижче порогу, kubelet починає виселяти Podʼи кінцевих користувачів.
Kubelet використовує наступні параметри для визначення порядку виселення Podʼів:
В результаті kubelet ранжує та виселяє Podʼи в наступному порядку:
BestEffort або Burstable, де використання перевищує запити. Ці Podʼи виселяються на основі їх пріоритету, а потім за тим, наскільки їхнє використання перевищує запит.Guaranteed та Burstable, де використання менше за запити, виселяються останніми, на основі їх пріоритету.DiskPressure.Podʼи Guaranteed гарантовані лише тоді, коли для всіх контейнерів вказано запити та обмеження, і вони є однаковими. Ці Podʼи ніколи не будуть виселятися через споживання ресурсів іншим Podʼом. Якщо системний демон (наприклад, kubelet та journald) споживає більше ресурсів, ніж було зарезервовано за допомогою розподілів systemReserved або kubeReserved, і на вузлі залишилися лише Guaranteed або Burstable Podʼи, які використовують менше ресурсів, ніж запити, то kubelet повинен вибрати Pod для виселення для збереження стабільності вузла та обмеження впливу нестачі ресурсів на інші Podʼи. У цьому випадку він вибере Podʼи з найнижчим пріоритетом першими.
Якщо ви використовуєте статичний Pod і хочете уникнути його виселення під час нестачі ресурсів, встановіть поле priority для цього Podʼа безпосередньо. Статичні Podʼи не підтримують поле priorityClassName.
Коли kubelet виселяє Podʼи у відповідь на вичерпання inode або ідентифікаторів процесів, він використовує відносний пріоритет Podʼів для визначення порядку виселення, оскільки для inode та PID не існує запитів.
Kubelet сортує Podʼи по-різному залежно від того, чи є на вузлі окрема файлова система imagefs чи containerfs:
imagefs або containerfs (nodefs і imagefs використовують одну і ту ж файлову систему)nodefs ініціює виселення, kubelet сортує Podʼи на основі їх загального використання диска (локальні томи + логи та записуваний шар усіх контейнерів).imagefs (nodefs і imagefs — окремі файлові системи)Якщо nodefs ініціює виселення, kubelet сортує Podʼи на основі використання nodefs (локальні томи + логи всіх контейнерів).
Якщо imagefs ініціює виселення, kubelet сортує Podʼи на основі використання записуваного шару всіх контейнерів.
imagefs та containerfs (imagefs та containerfs розділені)Якщо containerfs ініціює виселення, kubelet сортує Podʼи на основі використання containerfs (локальні томи + логи та записуваний шар усіх контейнерів).
Якщо imagefs ініціює виселення, kubelet сортує Podʼи на основі ранжування storage of images, яке відображає використання диска для даного образу.
containerfs.available. Конфігурація для цієї конкретної метрики буде встановлена автоматично, щоб відображати значення, задані для nodefs або imagefs, залежно від конфігурації.У деяких випадках виселення Podʼів звільняє лише невелику кількість дефіцитного ресурсу. Це може призвести до того, що kubelet раз у раз досягатиме налаштованих порогів виселення та ініціюватиме кілька виселень.
Ви можете використовувати поле evictionMinimumReclaim у файлі конфігурації kubelet для налаштування мінімальної кількості звільнення для кожного ресурсу. Коли kubelet помічає нестачу ресурсу, він продовжує звільняти цей ресурс, доки не звільнить кількість, яку ви вказали.
Наприклад, наступна конфігурація встановлює мінімальні кількості звільнення:
apiVersion: kubelet.config.k8s.io/v1beta1
kind: KubeletConfiguration
evictionHard:
memory.available: "500Mi"
nodefs.available: "1Gi"
imagefs.available: "100Gi"
evictionMinimumReclaim:
memory.available: "0Mi"
nodefs.available: "500Mi"
imagefs.available: "2Gi"
У цьому прикладі, якщо сигнал nodefs.available досягає порога виселення, kubelet звільняє ресурс, доки сигнал не досягне порога в 1GiB, а потім продовжує звільняти мінімальну кількість 500MiB, доки доступний обсяг сховища nodefs не досягне 1,5GiB.
Аналогічно, kubelet намагається звільнити ресурс imagefs, доки значення imagefs.available не досягне 102Gi, що представляє 102GiB доступного сховища для образів контейнерів. Якщо кількість сховища, яку може звільнити kubelet, менша за 2GiB, kubelet нічого не звільняє.
Стандартно evictionMinimumReclaim дорівнює 0 для всіх ресурсів.
Якщо вузол зазнає події out of memory (OOM) до того, як kubelet зможе звільнити памʼять, вузол залежить від реагування oom_killer.
Kubelet встановлює значення oom_score_adj для кожного контейнера на основі QoS для Podʼа.
| Якість обслуговування | oom_score_adj |
|---|---|
Guaranteed | -997 |
BestEffort | 1000 |
Burstable | min(max(2, 1000 - (1000 × memoryRequestBytes) / machineMemoryCapacityBytes), 999) |
oom_score_adj рівне -997 для будь-яких контейнерів у Podʼах, які мають пріоритет system-node-critical.Якщо kubelet не може звільнити памʼять до того, як вузол зазнає OOM, oom_killer обчислює оцінку oom_score на основі відсотка памʼяті, яку він використовує на вузлі, а потім додає oom_score_adj, щоб отримати ефективну оцінку oom_score для кожного контейнера. Потім він примусово завершує роботу контейнера з найвищою оцінкою.
Це означає, що спочатку припиняється робота контейнерів у Podʼах низького QoS, які споживають велику кількість памʼяті в порівнянні з їх запитами на планування.
На відміну від виселення Podʼа, якщо роботу контейнера було припинено через вичерпання памʼяті, kubelet може перезапустити його відповідно до його restartPolicy.
У наступних розділах описано найкращі практики конфігурації виселення.
Під час конфігурації kubelet з політикою виселення слід переконатися, що планувальник не розміщуватиме Podʼи, які спричинять виселення через те, що вони одразу створюють тиск на памʼять.
Розгляньте такий сценарій:
kubelet тощо)Для цього файл конфігурації kubelet заповнюється таким чином:
apiVersion: kubelet.config.k8s.io/v1beta1
kind: KubeletConfiguration
evictionHard:
memory.available: "500Mi"
systemReserved:
memory: "1.5Gi"
У цій конфігурації systemReserved резервує 1,5GiB памʼяті для системи, що становить 10% від загальної памʼяті + кількість порогу виселення.
Вузол може досягти порогу виселення, якщо Pod використовує більше, ніж його запит, або якщо система використовує більше 1GiB памʼяті, що знижує значення memory.available нижче 500MiB і спричиняє спрацювання порогу.
Пріоритет Podʼа — це основний фактор під час прийняття рішень про виселення. Якщо вам не потрібно, щоб kubelet виселяв Podʼи, які належать до DaemonSet, встановіть цим Podʼам достатньо високий пріоритет, вказавши відповідний priorityClassName в специфікації Podʼа. Ви також можете використовувати нижчий пріоритет або стандартне значення, щоб дозволити запускати Podʼи з цього DaemonSet тільки тоді, коли є достатньо ресурсів.
У наступних розділах описані відомі проблеми, повʼязані з обробкою вичерпання ресурсів.
Стандартно kubelet опитує cAdvisor, щоб регулярно збирати статистику використання памʼяті. Якщо використання памʼяті швидко зростає протягом цього інтервалу, kubelet може не помітити MemoryPressure достатньо швидко, і OOM killer все одно буде викликано.
Ви можете використовувати поле конфігурації kernelMemcgNotification, щоб увімкнути API сповіщення memcg в kubelet і отримувати повідомлення негайно, коли перетинається поріг.
Якщо ви прагнете не екстремального використання, а розумного рівня overcommit, робочим обхідним рішенням цієї проблеми є використання полів конфігурації kubeReserved та systemReserved для виділення памʼяті для системи.
В Linux ядро відстежує кількість байтів файлової памʼяті в активному списку LRU (least recently used) як статистику active_file. Kubelet розглядає області памʼяті active_file як такі, що не підлягають звільненню. Для робочих навантажень, що інтенсивно використовують локальне сховище з блоковою підтримкою, включаючи ефемерне локальне сховище, кеші ядра для файлів і блоків означають, що багато нещодавно використаних сторінок кеша, ймовірно, будуть враховані як active_file. Якщо достатньо цих буферів блоків ядра перебувають на активному списку LRU, kubelet може сприйняти це як високе використання ресурсів та накласти на вузол taint тиску на памʼять, що ініціює виселення Podʼів.
Для отримання додаткової інформації дивіться https://github.com/kubernetes/kubernetes/issues/43916.
Ви можете обійти цю проблему, встановивши для контейнерів, які ймовірно виконують інтенсивну діяльність з операцій введення/виведення, однакове обмеження памʼяті та запит памʼяті. Вам потрібно оцінити або виміряти оптимальне значення обмеження памʼяті для цього контейнера.