Модуль 2.5: Керування ресурсами
Складність:
[СЕРЕДНЯ]— критично для робочих навантажень у продакшеніЧас на проходження: 40-50 хвилин
Передумови: Модуль 2.1 (Поди), Модуль 2.2 (Деплойменти)
Що ви зможете зробити
Розділ «Що ви зможете зробити»Після цього модуля ви зможете:
- Налаштувати запити (requests) та ліміти (limits) ресурсів CPU й пам’яті, а потім пояснити, як ці значення впливають на планування та контроль під час виконання.
- Діагностувати контейнери зі станом OOMKilled, тротлінг CPU, виселення через тиск на ресурси та поди у стані Pending, спричинені нестачею доступних для виділення ресурсів.
- Впровадити керування ресурсами на рівні простору імен за допомогою стандартних значень LimitRange, обмежень ResourceQuota та перевірок, які доводять, що ці засоби контролю працюють.
- Спроєктувати стратегію ресурсів, що збалансовує надійність застосунку, утилізацію кластера, толерантність до сплесків навантаження та можливості зміни розміру в Kubernetes 1.35+.
Чому цей модуль важливий
Розділ «Чому цей модуль важливий»Гіпотетичний сценарій: команда запускає новий API у спільний кластер Kubernetes без запитів ресурсів і без квоти на рівні простору імен. Поди швидко стартують у тихому тестовому просторі імен, тож маніфести виглядають безпечними, але впродовж першого завантаженого ранку контейнери розростаються, доки вузол не входить у стан тиску на пам’ять. Інші робочі навантаження, які таки зарезервували пам’ять, тепер змагаються з подами класу BestEffort, kubelet починає виселяти кандидатів із нижчим пріоритетом, і інцидент здається випадковим, бо застосунок, який падає, — не єдиний застосунок, що припустився первісної помилки.
Керування ресурсами перетворює такого роду несподіванку на набір явних обіцянок. Запит (request) повідомляє планувальнику, скільки потужності потрібно поду, перш ніж його буде розміщено. Ліміт (limit) повідомляє середовищу виконання контейнерів, де примусово застосувати межу після того, як под уже запущено. LimitRange надає простору імен розумні стандартні значення та запобіжники, тоді як ResourceQuota обмежує загальний обсяг, який може спожити команда. Суть не в тому, щоб запам’ятати поля YAML; суть у тому, щоб спроєктувати контракти ресурсів, які роблять планування, збої та відповідальність передбачуваними.
Іспит CKA перевіряє це, бо та сама механіка з’являється під час сортування інцидентів у продакшені. Под у стані Pending може бути взагалі не зламаним; він може просто просити більше CPU чи пам’яті, ніж здатен надати будь-який вузол після врахування наявних запитів. Под, що перезапускається, може не мати винятку в застосунку; він може перетинати ліміт пам’яті cgroup і отримувати SIGKILL від ядра. Повільний под може бути не нездоровим; він може стикатися з тротлінгом CPU, бо низький ліміт не дає виконувати короткі сплески. У цьому модулі ви пов’яжете ці симптоми з об’єктами Kubernetes, які їх породили, а потім попрактикуєте команди, що доводять діагноз.
Аналогія з готельним номером усе ще корисна, якщо пам’ятати про її обмеження. Вузол схожий на готель, запити — це бронювання, а ліміти — правила заселення. Планувальник призначає номери на основі бронювань, а не здогадок про те, хто може з’явитися пізніше. Щойно гості прибувають, ліміти пам’яті стають суворими межами на кшталт протипожежних норм, тоді як ліміти CPU поводяться радше як правило тайм-шерингу, яке сповільнює галасливого гостя, а не виселяє його одразу.
Контракти ресурсів: запити, ліміти та одиниці
Розділ «Контракти ресурсів: запити, ліміти та одиниці»Керування ресурсами в Kubernetes починається з поділу між наміром планування та примусовим контролем під час виконання. Запит — це обсяг CPU чи пам’яті, який Kubernetes використовує, вирішуючи, чи має вузол достатньо доступної для виділення потужності для поду. Ліміт — це максимальний обсяг, який контейнер може спожити після старту. Ці поля стоять поруч у маніфесті, але відповідають на різні питання, і плутанина між ними — джерело багатьох помилок на іспиті та збоїв у продакшені.
Найпростіший маніфест показує форму контракту. Цей под просить планувальник зарезервувати 100m CPU та 128Mi пам’яті, але дозволяє контейнеру розростатися до 500m CPU та 256Mi пам’яті під час виконання. Планувальник використовує для розміщення лише запит, тож малий запит може полегшити розміщення, але нереалістично малий запит також робить под вразливішим під час тиску на вузол, бо под позичає потужність, яку він не зарезервував.
Уявляйте запит як обсяг потужності, який ви готові зробити недоступним для інших подів, навіть коли ваш контейнер простоює. Це може звучати марнотратно, але саме це є основою передбачуваного розміщення. Якщо десять подів кожен запитує 100m, планувальник може розраховувати на одне ядро CPU зарезервованої роботи, перш ніж будь-який процес стартує. Якщо ж ті самі поди опускають запити, у планувальника немає задекларованої базової лінії, і він може упакувати їх на вузол, який виглядає прийнятним лише тому, що найзавантаженіша мить ще не настала.
apiVersion: v1kind: Podmetadata: name: resource-demospec: containers: - name: app image: nginx resources: requests: # Minimum guaranteed resources memory: "128Mi" cpu: "100m" limits: # Maximum allowed resources memory: "256Mi" cpu: "500m"| Аспект | Запити (Requests) | Ліміти (Limits) |
|---|---|---|
| Призначення | Гарантія планування | Жорстка межа |
| Коли застосовується | Планувальник вирішує розміщення | Примус середовища виконання контейнерів |
| Недовикористання | Інші поди можуть використати залишок | Не застосовується |
| Перевищення | Не застосовується | Контейнер убито за пам’ять або затротлено за CPU |
Діаграма нижче відображає найважливішу операційну відмінність. Пам’ять і CPU обидва мають запити та ліміти, але вони не збоять однаково, коли контейнер перетинає ліміт. Пам’ять не є стискуваною так само, як час CPU, тож контейнер, що перетинає свій ліміт пам’яті, може бути вбито з причиною OOMKilled. CPU можна нарізати в часі, тож контейнер, що перетинає свій ліміт CPU, тротлиться й продовжує працювати повільніше.
┌────────────────────────────────────────────────────────────────┐│ Requests vs Limits ││ ││ Memory: 128Mi request, 256Mi limit ││ ││ 0 128Mi 256Mi Node Memory ││ ├─────────┼──────────┼───────────────────────────────────► ││ │ │ │ ││ │ Reserved│ Can grow │ OOMKilled if exceeded ││ │(guara- │ into this│ ││ │ nteed) │ space │ ││ ││ CPU: 100m request, 500m limit ││ ││ 0 100m 500m Node CPU ││ ├─────────┼──────────┼───────────────────────────────────► ││ │ │ │ ││ │ Reserved│ Can burst│ Throttled (not killed) ││ │ │ up to │ ││ │└────────────────────────────────────────────────────────────────┘Одиниці CPU легко прочитати неправильно під тиском іспиту. Kubernetes дозволяє записувати CPU у цілих ядрах або міліядрах, де 1000m означає одне ядро, а 100m — одну десяту ядра. Запит 100m не означає, що процес узагалі ніколи не зможе використати більше за одну десяту ядра, якщо ви також не встановите низький ліміт CPU; він означає, що планувальник резервує стільки потужності для розміщення, а середовище виконання може примусово застосувати ліміт, якщо той існує.
| Значення | Що означає |
|---|---|
1 | 1 ядро CPU |
1000m | 1 ядро CPU, записане в міліядрах |
100m | 0,1 ядра CPU, або 100 міліядер |
500m | 0,5 ядра CPU |
Одиниці пам’яті мають окрему пастку, бо Kubernetes приймає і десяткові, і двійкові суфікси. Mi означає мебібайти, на основі степенів двійки, тоді як M означає мегабайти, на основі степенів десятки. Різниця достатньо мала, щоб не помітити її під час побіжного перегляду, і достатньо велика, щоб мати значення, коли ви задаєте жорсткі ліміти на багатьох подах. У маніфестах послідовно вживайте Mi та Gi, якщо у вас немає свідомої причини використовувати десяткові одиниці.
Запити та ліміти також взаємодіють із багатоконтейнерними подами. Kubernetes планує под як єдине ціле, тож додає запити основного контейнера, sidecar-ів та облік, пов’язаний з init-контейнерами, вирішуючи, чи под уміститься. Sidecar із відсутніми чи невідповідними ресурсами може змінити і тиск на планування, і класифікацію QoS. Під час реальних оглядів не зупиняйтеся після перевірки першого контейнера з ім’ям app; перевіряйте кожен контейнер у шаблоні поду, бо планувальник і kubelet оцінюють под у сукупності, а не вашу уявну модель основного процесу.
| Значення | Що означає |
|---|---|
128Mi | 128 мебібайтів, або 128 разів по 1024 у квадраті байтів |
1Gi | 1 гібібайт |
256M | 256 мегабайтів, або 256 разів по 1000 у квадраті байтів |
Зробіть паузу й передбачте: якщо под запитує 100m CPU та 128Mi пам’яті, але не має ліміту CPU і має ліміт пам’яті 256Mi, яке значення впливає на планування, яке значення впливає на поведінку OOMKilled, а який ресурс усе ще може розростатися над своїм запитом? Запишіть свою відповідь, перш ніж читати наступний розділ, бо ця відмінність — центр майже кожного шляху усунення несправностей у цьому модулі.
Планування, поди у стані Pending та тиск на вузол
Розділ «Планування, поди у стані Pending та тиск на вузол»Планувальник не розміщує поди, спостерігаючи за поточним використанням CPU та пам’яті. Він підсумовує запити подів, уже призначених на вузол, порівнює цю суму з доступними для виділення ресурсами вузла й перевіряє, чи вміщаються запити нового поду. Доступні для виділення ресурси — це не те саме, що сира потужність, бо вузол резервує частину ресурсів для операційної системи, kubelet та іншої роботи на рівні вузла. Саме тому вузол із чотирма ядрами CPU може надавати подам менш як чотири ядра як доступні для виділення.
# Check node allocatable resourceskubectl describe node <node-name> | grep -A6 "Allocatable"
# Allocatable:# cpu: 2# memory: 4Gi# pods: 110Рішення про розміщення консервативне за задумом. Kubernetes радше залишить под у стані Pending, ніж розмістить його на вузлі, де запитані ресурси не можна гарантувати. Це не означає, що в кластера немає вільного CPU в цю мить; це означає, що запитані резервування більше не вміщаються за обліком планувальника. На завантаженому кластері ця відмінність корисна, бо вона відокремлює проблеми планування потужності від проблем запуску застосунку.
Саме тому под у стані Pending часто є сигналом про потужність, а не вадою застосунку. Контейнер не завантажив образ, не запустив процес і не видав логи застосунку, бо планувальник не прив’язав його до вузла. Коли ви бачите Pending, прочитайте події, перш ніж змінювати образ чи перезапускати контролери. Повідомлення FailedScheduling, яке називає нестачу пам’яті, каже вам перевірити запити, доступну для виділення потужність вузла, квоти та обмеження розміщення; воно не каже вам налагоджувати бінарний файл застосунку.
┌────────────────────────────────────────────────────────────────┐│ Scheduling Decision ││ ││ Node Capacity: 4Gi memory ││ Already Requested: 3Gi ││ Available: 1Gi ││ ││ Pod A requests 2Gi memory ││ → Cannot schedule (2Gi > 1Gi available) ││ → Pod stays Pending ││ ││ Pod B requests 500Mi memory ││ → Can schedule (500Mi < 1Gi available) ││ → Pod placed on node ││ │└────────────────────────────────────────────────────────────────┘Ви можете створити цей режим збою напряму, попросивши великий обсяг пам’яті. Под є валідним YAML, а образ контейнера — звичайний, але планувальник не зможе прив’язати його, якщо жоден вузол не має достатньо залишкової доступної для виділення пам’яті після врахування запитів. На іспиті найшвидший шлях — зазвичай kubectl describe pod та розділ Events, бо повідомлення FailedScheduling каже вам, чи проблема в нестачі CPU, нестачі пам’яті, taint-ах, affinity чи іншому обмеженні розміщення.
# Create pod with huge requestcat << 'EOF' | kubectl apply -f -apiVersion: v1kind: Podmetadata: name: big-podspec: containers: - name: nginx image: nginx resources: requests: memory: "100Gi"EOF
# Check statuskubectl get pod big-pod# NAME READY STATUS RESTARTS AGE# big-pod 0/1 Pending 0 10s
# Check whykubectl describe pod big-pod | grep -A5 "Events"# Warning FailedScheduling Insufficient memoryТиск на вузол відрізняється від збою планування, бо він трапляється після того, як поди вже запущені. Вузол може ввійти у стан MemoryPressure, DiskPressure чи PIDPressure, коли kubelet бачить, що локальні ресурси падають нижче налаштованих порогів. У цей момент kubelet може виселяти поди, і клас QoS плюс фактичне використання відносно запитів стає важливим. Планування захищало початкове розміщення; виселення захищає вузол, коли умови виконання змінилися.
Відмінність має значення, бо виправлення відбувається на іншому рівні. Якщо под не можна запланувати, ви або знижуєте його запит, або додаєте потужність, або змінюєте обмеження розміщення, або вивільняєте запитану потужність, переміщуючи інші робочі навантаження. Якщо вузол під тиском, ви перевіряєте, які запущені поди споживають незарезервовані ресурси, чи запити відображають реальність і чи перетинаються пороги виселення. Сприйняття обох симптомів як «у кластера закінчилася пам’ять» приховує корисні докази, які Kubernetes уже вам дає.
# Check node resource pressurekubectl describe node <node-name> | grep -A10 "Conditions"# MemoryPressure False KubeletHasSufficientMemory# DiskPressure False KubeletHasNoDiskPressure# PIDPressure False KubeletHasSufficientPIDПерш ніж запускати це в реальному кластері, який вивід ви очікуєте від поду у стані Pending, що запитав забагато пам’яті: причину статусу контейнера, подію планувальника чи рядок логу застосунку? Правильна відповідь формує порядок ваших команд. Якщо под так і не дістався вузла, логи контейнера — не перше місце, куди слід дивитися; подія планувальника — це доказ, який каже вам, що под так і не був допущений до виконання.
Примус під час виконання: тротлінг CPU та OOMKilled
Розділ «Примус під час виконання: тротлінг CPU та OOMKilled»Щойно под запущено, ліміти примусово застосовуються середовищем виконання контейнерів та механізмами ядра Linux під ним. CPU й пам’ять обидва записуються в resources.limits, але поводяться несиметрично. CPU є стискуваним, бо середовище виконання може відкласти виконання й видати менше квантів процесорного часу. Пам’ять не є безпечно стискуваною з точки зору Kubernetes, тож перевищення ліміту пам’яті може вбити контейнер, навіть коли процес не має жодного шансу залогувати коректну помилку.
# Container trying to use 2 CPUs with 500m limit# Gets throttled to 500m worth of CPU timeТротлінг CPU підступний, бо середні значення можуть його приховати. kubectl top pod може показувати помірне середнє використання CPU, тоді як застосунок усе ще переживає стрибки затримки, особливо якщо процес сплесковий і тротлиться під час коротких вікон обробки запитів. Саме тому деякі продакшен-команди ретельно задають запити CPU, але уникають лімітів CPU для чутливих до затримки сервісів, тоді як інші тримають ліміти для сильнішої багатоорендної ізоляції. Правильна відповідь залежить від поведінки робочого навантаження та політики кластера, а не від універсального співвідношення.
Іспит не вимагає від вас налаштовувати лічильники ядра, але очікує, що ви знатимете патерн симптомів. Затротлений сервіс може виглядати здоровим з точки зору лічильника перезапусків, бо контейнер лишається живим. Проби готовності (readiness probes) можуть проходити між сплесками, проте користувачі бачать повільні відповіді під час пікового трафіку. Якщо контракт ресурсів має низький ліміт CPU, підвищення лише запиту не зніме обмеження під час виконання, тоді як підвищення лише ліміту не змінить резервувань для планування. Корисне виправлення залежить від того, що є справжньою проблемою — чесність розміщення чи запас під час виконання.
Ліміти пам’яті дають помітніший симптом. Коли процес усередині контейнера використовує більше пам’яті, ніж дозволяє його ліміт cgroup, ядро може його вбити, а Kubernetes повідомляє попередній стан контейнера як завершений із причиною OOMKilled. Код виходу 137 трапляється часто, бо він відповідає SIGKILL. Застосунок може не мати стек-трейсу, бо вбивство сталося поза нормальним шляхом обробки помилок застосунку.
# Check for OOMKilledkubectl describe pod <pod-name> | grep -A5 "Last State"# Last State: Terminated# Reason: OOMKilled# Exit Code: 137
# Check eventskubectl get events --field-selector reason=OOMKillingНаступний под навмисно просить процес stress виділити більше пам’яті, ніж дозволяє його ліміт. Він корисний в одноразовому лабораторному просторі імен, бо доводить режим збою без потреби в зламаному застосунку. Не використовуйте такого роду робоче навантаження у спільному продакшен-просторі імен; це навчальний зонд, який має перетнути межу й бути вбитим.
Усунення несправностей пам’яті — це також місце, де запити та ліміти плутають найчастіше. Запит пам’яті не захищає контейнер від вбивства, коли той перетинає власний ліміт пам’яті. Ліміт пам’яті не гарантує, що под буде заплановано на вузол із достатньою зарезервованою потужністю для нормальної роботи. Вам потрібні обидві сторони контракту: запит має відображати те, що навантаженню потрібно для надійної роботи, а ліміт — максимум, який ви готові дозволити, перш ніж стримування стане безпечнішим за подальше зростання.
# Pod that will be OOMKilledapiVersion: v1kind: Podmetadata: name: memory-hogspec: containers: - name: memory-hog image: polinux/stress command: ["stress"] args: ["--vm", "1", "--vm-bytes", "200M", "--vm-hang", "1"] resources: limits: memory: "100Mi" # Limit is less than 200M stress allocatesГіпотетичний сценарій: вебконтейнер перезапускається кожні кілька хвилин, логи застосунку обриваються раптово, а контролер деплойменту далі створює замінні контейнери. Якщо kubectl describe pod показує Last State: Terminated із Reason: OOMKilled, перше виправлення — не перезапускати деплоймент знову. Вам потрібно вирішити, чи ліміт занизький для легітимного трафіку, чи запит занизький і спричиняє поганий пріоритет виселення, чи в застосунку є витік пам’яті, який зрештою перевищить будь-який розумний ліміт.
Класи якості обслуговування (QoS) та поведінка під час виселення
Розділ «Класи якості обслуговування (QoS) та поведінка під час виселення»Kubernetes призначає кожному поду клас QoS на основі його конфігурації ресурсів. Цей клас не замінює класи пріоритету, витіснення (preemption) чи пороги виселення, але є важливим сигналом під час тиску на ресурси. Поди Guaranteed дали найсильнішу обіцянку, бо кожен контейнер має запити CPU та пам’яті, рівні лімітам. Поди BestEffort не дали жодної обіцянки щодо ресурсів. Поди Burstable стоять між цими випадками, бо мають щонайменше один запит чи ліміт, але не відповідають суворому правилу Guaranteed.
| Клас QoS | Умова | Пріоритет виселення |
|---|---|---|
| Guaranteed | requests = limits для всіх контейнерів | Останній, найнижчий пріоритет виселення |
| Burstable | Задано щонайменше один запит чи ліміт | Середній |
| BestEffort | Жодних запитів чи лімітів | Перший, найвищий пріоритет виселення |
Guaranteed суворіший, ніж очікує багато хто з тих, хто навчається. Кожен контейнер у поді повинен мати і запити, і ліміти CPU та пам’яті, причому запит має дорівнювати ліміту для кожного ресурсу. Якщо одна сторона відсутня або одне значення відрізняється, под не є Guaranteed. Ця суворість важлива, бо sidecar без відповідних ресурсів може змінити клас QoS усього поду.
Тому Guaranteed має бути свідомим вибором, а не звичкою форматування. Він дає поду найсильнішу обіцянку щодо ресурсів, але може знизити утилізацію кластера, бо кожна репліка резервує свій повний ліміт. Цей компроміс може бути доречним для компонента, який мусить пережити тиск, але марнотратним для навантаження, яке зазвичай використовує невелику базову лінію й лише зрідка дає сплеск. Питання проєктування полягає в тому, чи вартість виселення або затротленої поведінки під час сплеску вища за вартість резервування більшої потужності.
apiVersion: v1kind: Podmetadata: name: guaranteed-podspec: containers: - name: app image: nginx resources: requests: memory: "128Mi" cpu: "100m" limits: memory: "128Mi" # Same as request cpu: "100m" # Same as request# Check QoS classkubectl get pod guaranteed-pod -o jsonpath='{.status.qosClass}'# GuaranteedBurstable — звичайний клас для багатьох сервісів, бо команди часто хочуть реалістичний запит і деякий запас над ним. Цей компроміс покращує утилізацію, але також означає, що под позичає потужність, коли працює над своїм запитом. Під час тиску на пам’ять вузла под Burstable, що значно перевищує свій запит, є сильнішим кандидатом на виселення, ніж под Burstable, який усе ще нижчий за свій запит, бо перший под споживає більше, ніж зарезервував.
apiVersion: v1kind: Podmetadata: name: burstable-podspec: containers: - name: app image: nginx resources: requests: memory: "128Mi" limits: memory: "256Mi" # Different from requestПоди BestEffort легко створити випадково, бо под без секції ресурсів отримує цей клас. Вони корисні для швидких експериментів в одноразовому просторі імен, але є поганими сусідами у спільних продакшен-кластерах. Планувальник може розміщувати їх без резервування CPU чи пам’яті, і під час тиску вони серед перших подів, яких kubelet розглядає для виселення.
Burstable — це місце, де живе більшість нюансованих рішень. Под Burstable може бути добре спроєктованим, коли запит чесний, а ліміт дає безпечний запас. Він також може бути погано спроєктованим, коли запит крихітний, ліміт величезний, а навантаження зазвичай споживає набагато більше, ніж зарезервувало. Самого ярлика недостатньо, щоб судити про якість. Порівняйте фактичне використання із запитом, а потім вирішіть, чи под відповідально позичає трохи потужності, чи покладається на незарезервовану потужність як на свою звичайну операційну модель.
apiVersion: v1kind: Podmetadata: name: besteffort-podspec: containers: - name: app image: nginx # No resources sectionEviction Order (first to last):1. BestEffort pods (no requests set - evicted first)2. Burstable pods using more than their requests3. Burstable pods using less than their requests4. Guaranteed pods (last resort)Зробіть паузу й передбачте: под має requests: {cpu: 100m, memory: 128Mi} та limits: {memory: 256Mi} без ліміту CPU. Він не є BestEffort, бо має налаштування ресурсів, і не є Guaranteed, бо CPU неповний, а запит пам’яті не дорівнює ліміту пам’яті. Це робить його Burstable, і якщо він спробує використати 300Mi пам’яті, ліміт пам’яті все одно може його вбити, навіть попри те, що клас QoS не є Guaranteed.
Керування простором імен за допомогою LimitRange та ResourceQuota
Розділ «Керування простором імен за допомогою LimitRange та ResourceQuota»Налаштування окремих подів необхідні, але їх недостатньо в кластері, де кілька команд незалежно створюють робочі навантаження. Політика простору імен дає операторам платформи місце, де можна задати стандартні значення та стелі, не редагуючи кожен маніфест застосунку. LimitRange застосовує обмеження до окремих об’єктів під час допуску, тоді як ResourceQuota обмежує сукупне споживання у просторі імен. Разом вони запобігають і випадковим подам BestEffort, і захопленню ресурсів на рівні простору імен.
LimitRange може задати стандартні запити та ліміти, коли контейнер їх опускає. Він також може визначати мінімальні та максимальні значення, щоб под не міг запитати абсурдно крихітні чи величезні обсяги. Це особливо корисно, коли ResourceQuota вимагає запитів чи лімітів, бо LimitRange може автоматично постачати ці відсутні поля, перш ніж буде перевірено облік квоти. Об’єкт обмежений простором імен, тож кожна команда може мати політику, що відповідає профілю її навантаження.
Стандартні значення мають бути достатньо консервативними, щоб захистити простір імен, не вдаючи, що знають кожне навантаження. Зависокий стандартний запит марнує квоту й робить малі поди дорогими на вигляд. Занизький стандартний запит відтворює проблему перевиділення під виглядом офіційнішої політики. Багато команд починають із малих стандартних значень для звичайних вебсервісів, вимагають явних ресурсів для важчих навантажень і розглядають винятки через звичайні платформні процеси. Ключ у тому, що простір імен тепер має передбачувану базову лінію замість покладання на те, що кожен автор маніфесту згадає однакові поля.
apiVersion: v1kind: LimitRangemetadata: name: cpu-memory-limits namespace: developmentspec: limits: - type: Container default: # Default limits if not specified cpu: "500m" memory: "256Mi" defaultRequest: # Default requests if not specified cpu: "100m" memory: "128Mi" min: # Minimum allowed cpu: "50m" memory: "64Mi" max: # Maximum allowed cpu: "1" memory: "1Gi"# Apply LimitRange to namespacekubectl apply -f limitrange.yaml
# Now create pod without resourceskubectl run test --image=nginx -n development
# Check - default resources were applied!kubectl get pod test -n development -o yaml | grep -A10 resourcesLimitRange може застосовуватися не лише до окремих контейнерів. Політика для контейнера найпоширеніша для стандартних значень CPU та пам’яті, але обмеження на рівні поду можуть обмежувати суму по контейнерах, а обмеження PVC можуть керувати запитами на сховище. Важлива деталь для іспиту в тому, що LimitRange — це політика часу допуску. Вона змінює чи відхиляє нові об’єкти; вона не змінює безперервно розмір уже запущених подів після того, як їх допущено.
| Тип | До чого застосовується |
|---|---|
Container | Окремі контейнери |
Pod | Сума всіх контейнерів у поді |
PersistentVolumeClaim | Запити на сховище PVC |
ResourceQuota — це сукупна сторона тієї самої історії керування. Замість казати, що один контейнер не може перевищити максимум, квота каже, що простір імен у цілому не може перевищити певну суму. Вона може обмежувати запитаний CPU, запитану пам’ять, ліміти, кількість подів, кількість PVC та інші лічильники об’єктів. Це дає кожній команді бюджет і не дає галасливому простору імен випадково спожити весь кластер.
Проєктування квоти має віддзеркалювати те, як ви хочете, щоб команди робили компроміси. Квота requests.cpu контролює зарезервований CPU й тому слід планування. Квота limits.memory контролює максимальний радіус ураження пам’яті, якщо кожен контейнер виросте до свого ліміту. Квота на кількість подів не дає простору імен створювати тисячі крихітних об’єктів, навіть коли кожен об’єкт має малі ресурси. Ці виміри відповідають на різні питання, тож корисна політика простору імен часто поєднує квоти на обчислення з квотами на кількість об’єктів замість покладання лише на одне жорстке значення.
apiVersion: v1kind: ResourceQuotametadata: name: compute-quota namespace: developmentspec: hard: requests.cpu: "4" # Total CPU requests requests.memory: "8Gi" # Total memory requests limits.cpu: "8" # Total CPU limits limits.memory: "16Gi" # Total memory limits pods: "10" # Total number of pods persistentvolumeclaims: "5" # Total PVCs# View quotakubectl get resourcequota -n development
# Detailed viewkubectl describe resourcequota compute-quota -n development# Name: compute-quota# Resource Used Hard# -------- ---- ----# limits.cpu 2 8# limits.memory 4Gi 16Gi# pods 5 10Збої квоти — це збої допуску, а не збої під час виконання. Якщо простір імен уже використав усі дозволені слоти подів, наступне створення поду відхиляється ще до того, як контейнер стартує. Така поведінка корисна, бо дає чітку помилку в точці зміни, але також означає, що розробникам потрібен достатній зворотний зв’язок, щоб зрозуміти, чи вони перевищили кількість об’єктів, запитаний CPU, запитану пам’ять чи інший вимір квоти.
# If quota exceededkubectl run new-pod --image=nginx -n development# Error: exceeded quota: compute-quota, requested: pods=1, used: pods=10, limited: pods=10Зупиніться й подумайте: ви створюєте ResourceQuota у просторі імен із pods: 10 та requests.cpu: 4. Розробник намагається створити под, не вказавши жодного запиту CPU. Под може бути відхилено, бо квоти на обчислення вимагають наявності відповідних запитів чи лімітів ресурсів, якщо тільки LimitRange не постачає стандартні значення під час допуску. Краще проєктування — поєднати квоту з LimitRange, щоб звичайні навантаження отримували розумні стандартні значення, а незвичайні навантаження мусили оголошувати свої потреби явно.
Правильний розмір, моніторинг та зміна розміру в Kubernetes 1.35
Розділ «Правильний розмір, моніторинг та зміна розміру в Kubernetes 1.35»Вибір значень ресурсів — це проблема вимірювання, перш ніж стати проблемою YAML. Якщо ви задаєте запити нижче за нормальне використання, планувальник перевиділяє вузол, і под може стати поганим кандидатом на виселення під час тиску. Якщо ви задаєте запити значно вище за нормальне використання, планувальник пакує менше подів на вузол, і кластер виглядає повним, тоді як реальне використання низьке. Практична мета — зарезервувати достатньо для стабільної роботи, дозволити доречні сплески й не дати лімітам приховувати проблеми проєктування.
Хороший перший прохід — поспостерігати за навантаженням під репрезентативним тестом, а потім відокремити базову лінію від сплеску. Базова лінія — це рівень, який вам було б некомфортно втратити під час нормальної роботи, тож він належить близько до запиту. Сплеск — це тимчасовий запас, який може бути корисним, але не має визначати потужність планування, якщо тільки він не постійний. Пам’ять заслуговує особливої обережності, бо вона часто липка: процес, що виділив пам’ять, може не повертати її швидко, а занизький ліміт може створювати цикли перезапусків, що виглядають як нестабільність застосунку.
# 1. Profile your application# Run locally or in test environment to measure actual usage
# 2. Set requests slightly above average usage# Ensures pod gets scheduled
# 3. Set limits to handle bursts# Allow headroom for spikes but protect the nodeСпіввідношення нижче — не універсальні правила; це відправні точки для розмови. Вебсервер із короткими сплесками може використовувати малий запит CPU та вищий ліміт CPU. База даних зазвичай потребує консервативнішої пам’яті, бо виселення чи поведінка OOMKilled дорого коштують, а відновлення зі станом може бути повільним. Кеш може більше дбати про розмір пам’яті, ніж про сплеск CPU. Завжди поєднуйте ці патерни з вимірами з тестового чи staging-середовища.
| Тип застосунку | Запит | Ліміт | Співвідношення |
|---|---|---|---|
| Вебсервер | 100m CPU, 128Mi | 500m CPU, 512Mi | 1:5, 1:4 |
| Фоновий обробник | 200m CPU, 256Mi | 1 CPU, 1Gi | 1:5, 1:4 |
| База даних | 500m CPU, 1Gi | 2 CPU, 4Gi | 1:4, 1:4 |
| Кеш | 100m CPU, 512Mi | 200m CPU, 1Gi | 1:2, 1:2 |
Ви можете задати ресурси напряму в маніфесті або використати kubectl set resources проти наявного навантаження. Для довговічних систем віддавайте перевагу декларативним маніфестам у системі контролю версій, бо контракт ресурсів є частиною проєктування навантаження. Для іспиту чи швидкого ремонту kubectl set resources корисний, бо він патчить контролер і дає контролеру перестворити поди з новим шаблоном.
# Create with resources using a manifestcat << 'EOF' | kubectl apply -f -apiVersion: v1kind: Podmetadata: name: nginxspec: containers: - name: nginx image: nginx resources: requests: cpu: "100m" memory: "128Mi" limits: cpu: "500m" memory: "256Mi"EOF# Update existing deploymentkubectl set resources deployment/nginx \ -c nginx \ --requests="cpu=100m,memory=128Mi" \ --limits="cpu=500m,memory=256Mi"
# Check resource usage (requires metrics-server)kubectl top podskubectl top nodesКоманди моніторингу допомагають порівняти задекларований намір зі спостережуваною поведінкою. kubectl describe node показує потужність, доступні для виділення ресурси та виділені запити й ліміти. kubectl top показує поточне використання, коли metrics-server встановлено й він справний. Жодна команда сама по собі не є достатньою. Задекларовані значення кажуть вам, у що вірить планувальник і система квот, тоді як метрики використання кажуть вам, чи навантаження насправді використовує контракт ресурсів, який ви йому дали.
Коли цифри розходяться, довіряйте кожному інструменту в тому, для повідомлення про що його призначено. kubectl top може сказати вам, що под наразі використовує набагато менше пам’яті, ніж його запит, що може вказувати на надмірне резервування. kubectl describe node може сказати вам, що запитана пам’ять уже достатньо висока, щоб блокувати нові розміщення, навіть якщо поточне використання зараз нижче. Вивід ResourceQuota може сказати вам, що бюджет простору імен вичерпано ще до появи будь-якої проблеми на рівні вузла. Хороша діагностика — це звичка вирівнювати ці погляди замість сприйняття однієї команди як авторитетної.
# Check node resource usagekubectl top nodes# NAME CPU(cores) CPU% MEMORY(bytes) MEMORY%# node1 250m 12% 1200Mi 60%
# Check pod resource usagekubectl top podskubectl top pods -n kube-systemkubectl top pod nginx --containers# Node capacity and allocatablekubectl describe node <node-name> | grep -A10 "Capacity"kubectl describe node <node-name> | grep -A10 "Allocatable"
# Node resource usage summarykubectl describe node <node-name> | grep -A10 "Allocated resources"Kubernetes 1.35 робить зміну розміру ресурсів поду на місці (in-place) для CPU й пам’яті загальнодоступною через підресурс /resize. Це не означає, що кожна зміна розміру невидима для застосунку. Специфікація поду, resizePolicy контейнера, ресурс, що змінюється, та підтримка з боку середовища виконання вирішують, чи контейнер можна оновити на місці, чи його треба перезапустити. Використовуйте це як інструмент для правильного розміру та операційного ремонту, але пам’ятайте про навантаження, керовані контролерами, та володіння в GitOps.
Зміна розміру на місці особливо корисна для закриття розриву між спостереженням і виправленням. До появи цієї можливості вертикальні зміни зазвичай означали заміну подів, що змішувало виправлення ресурсів із поведінкою розгортання. Зі зміною розміру оператор іноді може скоригувати запущений под, поспостерігати, чи зникає тиск, а потім підвищити нові значення до шаблону навантаження-власника. Цей робочий процес усе ще дисциплінований: пряма зміна — це доказ і пом’якшення, тоді як оновлення шаблону чи маніфесту — це довговічна конфігурація.
# Check current resourceskubectl get pod nginx -o jsonpath='{.spec.containers[0].resources}'
# Resize CPU and memory without replacing the Pod objectkubectl patch pod nginx --subresource resize --patch '{ "spec": { "containers": [{ "name": "nginx", "resources": { "requests": {"cpu": "200m", "memory": "256Mi"}, "limits": {"cpu": "500m", "memory": "512Mi"} } }] }}'
# Verify whether the resize is pending or in progresskubectl get pod nginx -o jsonpath='{.status.conditions[?(@.type=="PodResizePending")].status}'kubectl get pod nginx -o jsonpath='{.status.conditions[?(@.type=="PodResizeInProgress")].status}'# Empty output means the condition is not currently set.# If either condition is True, the resize is still pending or in progress.apiVersion: v1kind: Podmetadata: name: resize-demospec: containers: - name: app image: nginx resources: requests: cpu: 100m memory: 128Mi limits: cpu: 200m memory: 256Mi resizePolicy: - resourceName: cpu restartPolicy: NotRequired # CPU changes apply live - resourceName: memory restartPolicy: RestartContainer # Memory changes restart containerЯкий підхід ви б обрали тут і чому: запатчити один запущений под підресурсом /resize, запатчити Деплоймент-власник за допомогою kubectl set resources чи оновити маніфест у системі контролю версій і дати вашій системі доставки розгорнути його? Для некерованого налагоджувального поду пряма зміна розміру може бути доречною. Для Деплойменту довговічне виправлення зазвичай належить шаблону поду, бо наступне розгортання чи замінний под інакше дрейфуватиме назад до старого контракту.
Патерни та антипатерни
Розділ «Патерни та антипатерни»Використовуйте патерн «спершу запит» для більшості сервісів без стану. Почніть із вимірювання усталеного CPU й пам’яті, задайте запити близько до нормального використання плюс помірний запас, а потім вирішіть, чи потрібні ліміти для ізоляції. Це працює, бо планувальник має для міркувань лише запити. Якщо ці значення реалістичні, кластер може ефективно пакувати навантаження, не вдаючи, що кожен сплеск постійний.
Патерн «спершу запит» також робить комунікацію під час інцидентів чіткішою. Коли сервіс не можна запланувати, ви можете вказати на задеклароване резервування й порівняти його з доступними для виділення ресурсами. Коли простір імен досягає квоти, ви можете показати, які навантаження споживають бюджет через свої запити. Без запитів кожна розмова стає суперечкою про поточне використання, а поточне використання змінюється щохвилини. Стабільні контракти ресурсів дають командам спільну мову для потужності, володіння та справедливості.
Використовуйте запобіжники простору імен для спільних кластерів. LimitRange має надавати стандартні значення та розумні значення min чи max, тоді як ResourceQuota має обмежувати бюджет простору імен. Цей патерн масштабується краще, ніж покладання на те, що кожен розробник пам’ятатиме поля ресурсів вручну, і він дає чіткіші помилки допуску, коли простір імен перевищує свій бюджет. Міркування щодо масштабування — це володіння політикою: платформні команди мають публікувати стандартні значення й навчати команди застосунків, як запитувати винятки.
Використовуйте QoS свідомо для критичних навантажень. Деякі компоненти заслуговують на QoS Guaranteed, бо виселення було б дорожчим за нижчу утилізацію. Інші навантаження краще зробити Burstable, бо невеликий додатковий запас покращує ефективність витрат. Патерн полягає в тому, щоб обирати клас як рішення про надійність, а не як випадковий результат того, які поля випадково опустили в маніфесті.
Уникайте конфігурації лише з лімітами. Вона виглядає захисною, бо контейнер має максимум, але дає планувальнику замало інформації, якщо стандартні значення допуску не додають запит. Навантаження тоді може приземлитися на вузол, не зарезервувавши потужність, яка йому зазвичай потрібна, і під тиском із ним можуть повестися гірше, ніж очікує команда. Краща альтернатива — задати явні запити, а потім обирати ліміти лише там, де поведінка під час збою прийнятна.
Уникайте копіювання співвідношень між типами навантажень без вимірювання. Інтенсивний за CPU пакетний обробник, чутливий до затримки API, JVM-сервіс та кеш у пам’яті не мають однакового патерну сплесків чи ризику пам’яті. Команди потрапляють у цю пастку, бо один приклад маніфесту легко стандартизувати. Краща альтернатива — публікувати початкові профілі, а потім вимагати від команд підтверджувати їх метриками зі свого навантаження.
Уникайте сприйняття kubectl top як інструмента планування потужності самого по собі. Він показує нещодавнє використання, а не резервування планувальника, використання квоти чи лічильники тротлінгу. Под із низьким середнім CPU усе одно може тротлитися під час коротких сплесків, а вузол із низькою поточною пам’яттю усе одно може не мати змоги запланувати под, бо запити вже заповнюють доступну для виділення потужність. Краща альтернатива — порівняти kubectl top, kubectl describe node, події подів та вивід квоти, перш ніж вирішувати, що змінювати.
Ще один антипатерн — використання політики ресурсів як покарання замість контракту. Якщо платформні команди задають крихітні стандартні значення, щоб змусити розробників просити більше, навантаження падають заплутаними способами, і розробники вчаться обходити політику. Якщо команди застосунків задають роздуті запити, щоб уникнути майбутніх розмов, кластер стає штучно повним. Краще керування ресурсами — це перемовини з доказами: стандартні значення обробляють звичайний випадок, метрики обґрунтовують винятки, а квоти роблять спільну межу явною.
Рамка прийняття рішень
Розділ «Рамка прийняття рішень»Почніть із симптому, а потім оберіть важіль керування ресурсами, що відповідає точці збою. Якщо под у стані Pending і так і не стартує, спершу перевірте події планування та доступні для виділення ресурси вузла. Якщо под стартує й перезапускається з кодом виходу 137, перевірте ліміти пам’яті, спостережуване використання пам’яті та чи є в застосунку витік. Якщо под працює, але затримка стрибає під навантаженням, перевірте ліміти CPU та ризик тротлінгу, перш ніж припускати, що застосунок нездоровий.
Для проєктування простору імен вирішіть, чи проблема в тому, що бракує стандартних значень, чи в надмірному розмірі окремого поду, чи в надмірному сукупному споживанні. Брак стандартних значень указує на LimitRange із defaultRequest та default. Завеликі окремі поди вказують на значення max у LimitRange або політику огляду. Сукупне перевикористання вказує на ResourceQuota. Коли потрібні і стандартні значення, і сукупні стелі, спершу створіть LimitRange, щоб звичайні поди отримували поля ресурсів, перш ніж допуск квоти їх оцінить.
Для усунення несправностей читайте збої в порядку, у якому їх зустрічає Kubernetes. Помилки допуску трапляються перед плануванням, тож повідомлення ResourceQuota та LimitRange з’являються, коли API-сервер відхиляє об’єкт. Помилки планування трапляються після допуску, тож вони з’являються як події поду, поки под лишається у стані Pending. Примус під час виконання трапляється після того, як под прив’язано й контейнери стартували, тож симптоми OOMKilled, перезапусків та тротлінгу з’являються у статусі, подіях і метриках. Ця хронологія не дає вам налагоджувати неправильний рівень.
Для правильного розміру відокремте екстрений ремонт від довговічної конфігурації. У Kubernetes 1.35+ підресурс /resize може бути корисним, коли одному запущеному поду потрібне негайне коригування CPU чи пам’яті й політика це дозволяє. Зміна на рівні Деплойменту краща, коли кожна репліка має поділяти новий контракт. Оновлення маніфесту в системі контролю версій — це довговічна відповідь, коли зміна має пережити розгортання, огляди та майбутні узгодження.
Для надійності порівняйте вартість виселення з вартістю нижчої утилізації. QoS Guaranteed може бути доречним для критичних сервісів, суміжних із площиною управління, чи навантажень зі станом, де виселення має високий вплив. Burstable часто доречний для сервісів без стану, які виграють від ефективного пакування й можуть толерувати зрідка поведінку під тиском. BestEffort зазвичай має обмежуватися експериментами, одноразовими задачами чи просторами імен, де виселення прийнятне.
Для виконання іспиту перетворіть цю рамку на швидку послідовність команд. Якщо створення не вдається, прочитайте помилку й перевірте політику простору імен. Якщо под існує, але у стані Pending, опишіть под і перевірте події планування. Якщо под працює й перезапускається, опишіть його та перевірте останній стан, код виходу й ліміти. Якщо под працює, але повільний, порівняйте ліміти CPU, запити та спостережуване використання. Ця послідовність швидша за здогадки, бо кожен крок іде за життєвим циклом Kubernetes.
Чи знали ви?
Розділ «Чи знали ви?»- Зміна розміру поду на місці тепер GA: У Kubernetes 1.35 запити та ліміти CPU й пам’яті можна оновлювати для запущених Подів через підресурс
/resize, аresizePolicyвпливає на те, чи потрібен перезапуск контейнера. - Ліміти CPU та пам’яті збоять по-різному: Перевищення CPU тротлиться, тоді як перевищення пам’яті може завершити контейнер зі станом
OOMKilledі зазвичай з’являється з кодом виходу 137. - Guaranteed QoS — це все або нічого: Кожен контейнер у поді потребує відповідних запитів і лімітів CPU та пам’яті; один неповний sidecar може зробити весь под Burstable.
- Metrics-server живить
kubectl top: Поширений конвеєр метрик кластера збирає нещодавнє використання ресурсів для команд на кшталтkubectl top, але планування все одно використовує задекларовані запити, а не поточне використання.
Типові помилки
Розділ «Типові помилки»| Помилка | Чому вона трапляється | Як її виправити |
|---|---|---|
| Не задано запитів | Маніфест працює в тихому тестовому просторі імен, тож команда забуває, що в планувальника немає резервування для використання | Задайте реалістичні запити CPU та пам’яті або використайте LimitRange для застосування стандартних значень |
| Занизькі ліміти | Скопійоване значення з прикладу стає продакшен-межею без профілювання навантаження | Виміряйте реальне використання, підніміть ліміт пам’яті, коли це легітимно, і виправляйте витоки, коли використання росте без меж |
| Запити скрізь дорівнюють лімітам | Команди женуться за Guaranteed QoS, не зважаючи на толерантність до сплесків чи утилізацію | Резервуйте Guaranteed QoS для навантажень, яким він потрібен, і використовуйте контракти Burstable для сервісів, що можуть безпечно давати сплеск |
Використання M замість Mi | Десяткові та двійкові суфікси виглядають схоже під час швидкого перегляду | Стандартизуйте Mi та Gi для маніфестів пам’яті, якщо десяткові одиниці не задумані навмисно |
| Квота без стандартних значень LimitRange | ResourceQuota вимагає полів, які звичайні маніфести подів опускають | Створіть стандартні значення LimitRange перед квотами на обчислення або разом із ними, щоб допуск міг заповнити відсутні запити |
Погляд лише на kubectl top | Нещодавнє використання читати легше, ніж резервування планувальника та події | Порівняйте використання з describe node, подіями подів, виводом ResourceQuota та специфікаціями ресурсів |
| Патчинг одного поду, що належить Деплойменту | Прямі правки поду здаються швидкими під час інциденту, але шаблон контролера досі має старі ресурси | Патчте контролер чи маніфест у системі контролю версій для довговічних змін, а зміну розміру поду використовуйте лише коли прямий ремонт задуманий навмисно |
Тест
Розділ «Тест»Питання 1: Под у стані Pending після деплою. Образ існує, простір імен правильний, а `kubectl describe pod` показує FailedScheduling із нестачею пам'яті. Що слід перевірити й змінити першим?
Перевірте запит пам’яті поду й порівняйте його з доступними для виділення ресурсами вузла та вже запитаною пам’яттю. Планувальник використовує запити, а не поточне використання, тож под може лишатися у стані Pending, навіть коли вузол виглядає тихим у kubectl top. Якщо запит випадково завеликий, зменшіть його до виміряного значення; якщо запит легітимний, додайте потужність або перемістіть навантаження з нижчим пріоритетом. Зміна лише ліміту пам’яті не вирішить розміщення, якщо запит усе одно не вміщається.
Питання 2: Контейнер перезапускається з кодом виходу 137, без помилки застосунку, із `Last State: Terminated, Reason: OOMKilled`. Як ви діагностуєте, чи піднімати ліміт, чи виправляти застосунок?
Почніть із підтвердження ліміту пам’яті та перевірки спостережуваного зростання пам’яті за доступними метриками. Якщо використання неухильно зростає, не повертаючись до стабільного рівня, підозрюйте витік і ставтеся до вищого ліміту як до тимчасового стримування, а не остаточного виправлення. Якщо використання дає сплеск лише під час валідного трафіку, а потім стабілізується, ліміт може бути занизьким для нормальної роботи. Також перевірте запит, бо низький запит може зробити под вразливішим під час тиску на вузол, навіть якщо ліміт запобігає шкоді на рівні вузла.
Питання 3: Ваш API має запит CPU `100m`, ліміт CPU `200m`, і затримка стрибає під піковим трафіком, навіть попри те, що середній CPU у `kubectl top` виглядає низьким. Яка проблема ресурсів може пояснити цей симптом?
Под може тротлитися за CPU під час коротких сплесків, навіть попри те, що середнє за вікном метрик низьке. Ліміти CPU застосовуються протягом малих періодів планування, тож сплеск обробки запиту може досягати межі 200m і сповільнюватися, тоді як довше середнє все одно виглядає помірним. Ви можете перевірити це, піднявши чи прибравши ліміт CPU в контрольованому середовищі й порівнявши затримку. Запиту все одно слід задати реалістичний розмір, щоб планування лишалося чесним.
Питання 4: Простір імен має ResourceQuota для `requests.cpu` та `pods`, але розробники часто створюють поди без полів ресурсів і отримують помилки допуску. Який об'єкт слід додати й чому?
Додайте LimitRange, що постачає значення defaultRequest для CPU та пам’яті, а зазвичай і стандартні ліміти. Квоти на обчислення потребують відповідних полів запиту чи ліміту, щоб Kubernetes міг обліковувати використання простору імен під час допуску. LimitRange заповнює відсутні значення перед оцінкою квоти, що дає змогу допустити звичайні поди, водночас зараховуючи їх до бюджету простору імен. Вам також варто задати розумні значення min і max, якщо розмір окремого поду потребує запобіжників.
Питання 5: Під час тиску на пам'ять вузла ви бачите под Guaranteed, под Burstable, що використовує набагато більше за свій запит, та под BestEffort. Який под найімовірніше буде виселено першим і який урок проєктування з цього випливає?
Под BestEffort — перший імовірний кандидат на виселення, бо він не зробив жодного резервування CPU чи пам’яті. Якщо тиск триває, поди Burstable, що використовують більше за свої запити, стають сильнішими кандидатами перед подами Guaranteed. Урок проєктування в тому, що запити — не паперова формальність; вони впливають і на планування, і на те, як kubelet оцінює справедливість під тиском. Критичні навантаження не мають випадково працювати як BestEffort через те, що було опущено секцію ресурсів.
Питання 6: Ви змінюєте розмір запущеного поду в Kubernetes 1.35 за допомогою підресурсу `/resize`, і зміна спрацьовує. Чому це все одно може бути неправильним довгостроковим виправленням для Деплойменту?
Пряма зміна розміру поду змінює запущений об’єкт Pod, але Деплоймент створює поди зі свого шаблону. Якщо шаблон досі містить старі ресурси, майбутні замінні поди можуть повернутися до старих значень запиту й ліміту. Для довговічних навантажень, керованих контролерами, патчте Деплоймент або оновлюйте маніфест у системі контролю версій, що ним володіє. Зміна розміру на місці корисна для негайного ремонту та правильного розміру, але володіння й узгодження все одно мають значення.
Практична вправа
Розділ «Практична вправа»Ця вправа використовує звичайні поди та політику простору імен, щоб пов’язати планування, QoS і поведінку допуску. Виконуйте її в одноразовому кластері чи лабораторному середовищі. Мета — не запам’ятати кожну команду, а передбачити результат перед кожною перевіркою, а потім порівняти це передбачення зі статусом, подіями та YAML об’єктів Kubernetes.
Завдання 1: Створіть под Burstable із явними запитами та лімітами
Розділ «Завдання 1: Створіть под Burstable із явними запитами та лімітами»Створіть под, де запити й ліміти відрізняються, потім перевірте клас QoS. Зверніть увагу, що под не є Guaranteed, навіть попри те, що має і значення CPU, і значення пам’яті, бо значення запиту й ліміту не рівні.
cat << 'EOF' | kubectl apply -f -apiVersion: v1kind: Podmetadata: name: resource-testspec: containers: - name: nginx image: nginx resources: requests: cpu: "100m" memory: "128Mi" limits: cpu: "200m" memory: "256Mi"EOF
kubectl get pod resource-test -o jsonpath='{.status.qosClass}'# Burstable (because requests are not equal to limits)Завдання 2: Створіть под Guaranteed
Розділ «Завдання 2: Створіть под Guaranteed»Тепер створіть под, де запити CPU та пам’яті дорівнюють лімітам. Це суворий патерн, якого Kubernetes вимагає для QoS Guaranteed.
cat << 'EOF' | kubectl apply -f -apiVersion: v1kind: Podmetadata: name: guaranteedspec: containers: - name: nginx image: nginx resources: requests: memory: "128Mi" cpu: "100m" limits: memory: "128Mi" cpu: "100m"EOF
kubectl get pod guaranteed -o jsonpath='{.status.qosClass}'# GuaranteedЗавдання 3: Створіть под BestEffort
Розділ «Завдання 3: Створіть под BestEffort»Створіть под без налаштувань ресурсів і підтвердьте, що Kubernetes класифікує його як BestEffort. У спільному кластері це зазвичай сигнал про те, що бракує стандартних значень простору імен.
kubectl run besteffort --image=nginxkubectl get pod besteffort -o jsonpath='{.status.qosClass}'# BestEffortЗавдання 4: Застосуйте LimitRange і поспостерігайте за стандартними значеннями
Розділ «Завдання 4: Застосуйте LimitRange і поспостерігайте за стандартними значеннями»Створіть простір імен, додайте LimitRange, а потім створіть под без явних ресурсів. Маніфест поду, який ви надіслали, не містив ресурсів, але допущений под має показати стандартні значення, надані політикою простору імен.
kubectl create namespace limits-test
cat << 'EOF' | kubectl apply -f -apiVersion: v1kind: LimitRangemetadata: name: default-limits namespace: limits-testspec: limits: - type: Container default: cpu: "200m" memory: "128Mi" defaultRequest: cpu: "100m" memory: "64Mi"EOF
# Create pod without resourceskubectl run test-defaults --image=nginx -n limits-test
# Check - defaults applied!kubectl get pod test-defaults -n limits-test -o yaml | grep -A8 resourcesЗавдання 5: Перевірте допуск ResourceQuota
Розділ «Завдання 5: Перевірте допуск ResourceQuota»Додайте квоту на кількість подів і створюйте поди, доки бюджет простору імен не вичерпається. test-defaults із Завдання 4 уже займає один із двох дозволених слотів подів, тож pod1 доводить простір імен до 2/2, і другий новий под відхиляється.
cat << 'EOF' | kubectl apply -f -apiVersion: v1kind: ResourceQuotametadata: name: pod-quota namespace: limits-testspec: hard: pods: "2"EOF
# Create pods until quota exceededkubectl run pod1 --image=nginx -n limits-testkubectl run pod2 --image=nginx -n limits-test # Should fail: test-defaults + pod1 already reach 2/2
kubectl describe resourcequota pod-quota -n limits-testЗавдання 6: Прибирання
Розділ «Завдання 6: Прибирання»Видаліть окремі поди й тестовий простір імен, щоб лабораторія залишила кластер чистим для наступного модуля.
kubectl delete pod resource-test guaranteed besteffortkubectl delete namespace limits-testКритерії успіху
Розділ «Критерії успіху»- Налаштувати запити та ліміти CPU й пам’яті на поді, а потім перевірити допущену специфікацію ресурсів.
- Діагностувати, чому под є Burstable, Guaranteed чи BestEffort, за його полями ресурсів.
- Впровадити LimitRange, що постачає стандартні значення простору імен для подів без явних ресурсів.
- Впровадити ResourceQuota й перевірити збій допуску, коли простір імен перевищує свій бюджет.
- Спроєктувати потік усунення несправностей ресурсів, що перевіряє події, QoS, квоту, метрики використання та володіння контролером.
Додаткові тренувальні вправи
Розділ «Додаткові тренувальні вправи»Вправи нижче зберігають командні патерни з оригінального тренувального набору. Сприймайте їх як вправи на час після того, як ви зрозуміли концепції, а не як заміну поясненням вище.
# Drill 1: Resource Creationcat << 'EOF' | kubectl apply -f -apiVersion: v1kind: Podmetadata: name: webspec: containers: - name: nginx image: nginx resources: requests: cpu: "100m" memory: "128Mi" limits: cpu: "500m" memory: "512Mi"EOF
kubectl get pod web -o jsonpath='{.spec.containers[0].resources}'kubectl get pod web -o jsonpath='{.status.qosClass}'kubectl delete pod web# Drill 2: QoS Class Identificationcat << 'EOF' | kubectl apply -f -apiVersion: v1kind: Podmetadata: name: qos-guaranteedspec: containers: - name: app image: nginx resources: requests: cpu: "100m" memory: "100Mi" limits: cpu: "100m" memory: "100Mi"EOF
cat << 'EOF' | kubectl apply -f -apiVersion: v1kind: Podmetadata: name: qos-burstablespec: containers: - name: app image: nginx resources: requests: cpu: "100m"EOF
kubectl run qos-besteffort --image=nginx
for pod in qos-guaranteed qos-burstable qos-besteffort; do echo "$pod: $(kubectl get pod $pod -o jsonpath='{.status.qosClass}')"done
kubectl delete pod qos-guaranteed qos-burstable qos-besteffort# Drill 3: LimitRangekubectl create namespace lr-test
cat << 'EOF' | kubectl apply -f -apiVersion: v1kind: LimitRangemetadata: name: mem-limit namespace: lr-testspec: limits: - type: Container default: memory: "256Mi" defaultRequest: memory: "128Mi" min: memory: "64Mi" max: memory: "1Gi"EOF
kubectl run default-test --image=nginx -n lr-testkubectl get pod default-test -n lr-test -o jsonpath='{.spec.containers[0].resources}'
cat << 'EOF' | kubectl apply -f -apiVersion: v1kind: Podmetadata: name: too-big namespace: lr-testspec: containers: - name: app image: nginx resources: limits: memory: "2Gi"EOF
kubectl delete namespace lr-test# Drill 4: ResourceQuotakubectl create namespace quota-test
cat << 'EOF' | kubectl apply -f -apiVersion: v1kind: ResourceQuotametadata: name: compute-quota namespace: quota-testspec: hard: requests.cpu: "1" requests.memory: "1Gi" limits.cpu: "2" limits.memory: "2Gi" pods: "3"EOF
kubectl describe resourcequota compute-quota -n quota-test
cat << 'EOF' | kubectl apply -f -apiVersion: v1kind: Podmetadata: name: pod1 namespace: quota-testspec: containers: - name: nginx image: nginx resources: requests: cpu: "200m" memory: "256Mi" limits: cpu: "400m" memory: "512Mi"---apiVersion: v1kind: Podmetadata: name: pod2 namespace: quota-testspec: containers: - name: nginx image: nginx resources: requests: cpu: "200m" memory: "256Mi" limits: cpu: "400m" memory: "512Mi"---apiVersion: v1kind: Podmetadata: name: pod3 namespace: quota-testspec: containers: - name: nginx image: nginx resources: requests: cpu: "200m" memory: "256Mi" limits: cpu: "400m" memory: "512Mi"EOF
cat << 'EOF' | kubectl apply -f -apiVersion: v1kind: Podmetadata: name: pod4 namespace: quota-testspec: containers: - name: nginx image: nginx resources: requests: cpu: "200m" memory: "256Mi" limits: cpu: "400m" memory: "512Mi"EOF
kubectl describe resourcequota compute-quota -n quota-testkubectl delete namespace quota-test# Drill 5: Resource Troubleshootingcat << 'EOF' | kubectl apply -f -apiVersion: v1kind: Podmetadata: name: pending-podspec: containers: - name: nginx image: nginx resources: requests: cpu: "100" memory: "100Gi"EOF
kubectl get pod pending-podkubectl describe pod pending-pod | grep -A5 "Events"
kubectl delete pod pending-podcat << 'EOF' | kubectl apply -f -apiVersion: v1kind: Podmetadata: name: pending-podspec: containers: - name: nginx image: nginx resources: requests: cpu: "100m" memory: "128Mi"EOF
kubectl get pod pending-podkubectl delete pod pending-pod# Drill 6: Update Resourceskubectl create deployment resource-update --image=nginx --replicas=2
kubectl set resources deployment/resource-update \ --requests="cpu=100m,memory=128Mi" \ --limits="cpu=200m,memory=256Mi"
kubectl get pods -l app=resource-update -w &sleep 10kill %1 2>/dev/null
kubectl describe deployment resource-update | grep -A10 "Resources"kubectl delete deployment resource-updateРозв'язок Drill 7: повне налаштування ресурсів простору імен
kubectl create namespace challenge
cat << 'EOF' | kubectl apply -f -apiVersion: v1kind: LimitRangemetadata: name: limits namespace: challengespec: limits: - type: Container default: cpu: "200m" memory: "256Mi" defaultRequest: cpu: "100m" memory: "128Mi" max: cpu: "1" memory: "1Gi"EOF
cat << 'EOF' | kubectl apply -f -apiVersion: v1kind: ResourceQuotametadata: name: quota namespace: challengespec: hard: pods: "4" requests.cpu: "2" requests.memory: "4Gi"EOF
kubectl create deployment app --image=nginx --replicas=2 -n challengekubectl get all -n challengekubectl describe resourcequota quota -n challengekubectl delete namespace challengeДжерела
Розділ «Джерела»- Resource Management for Pods and Containers
- Pod Quality of Service Classes
- Limit Ranges
- Resource Quotas
- Kubernetes Metrics Server
- Resize CPU and Memory Resources assigned to Containers
- Vertical Pod Autoscaling
- Node-pressure Eviction
- kubectl set resources reference
- kubectl top reference
Наступний модуль
Розділ «Наступний модуль»Модуль 2.6: Планування — вибір вузлів, affinity, taint-и та toleration-и.