Модуль 2.1: Планування
Складність:
[СЕРЕДНЯ]— концепції оркестраціїЧас на проходження: 60-75 хвилин
Передумови: Частина 1 (Основи Kubernetes), базові YAML для Pod та Deployment, а також доступ до кластера Kubernetes 1.35 або новішої версії
Результати навчання
Розділ «Результати навчання»Після завершення цього модуля ви зможете виконувати ці завдання з планування на основі доказів, а не інтуїції, що означає: кожен результат пов’язаний із конкретною подією, вибором маніфесту, сценарієм тесту або дією в лабораторній роботі:
- Діагностувати Поди у стані Pending, інтерпретуючи події
FailedScheduling, місткість нод та запити ресурсів. - Спроєктувати репліки з високою доступністю за допомогою анти-афінності Подів та обмежень рівномірного розподілу за топологією.
- Порівняти афінність до нод із taint’ами та толерантностями для виділених пулів нод зі спеціалізованим обладнанням.
- Впровадити запити та ліміти ресурсів, які забезпечують передбачуване планування та поведінку під час виконання.
- Оцінити вибір планувальника щодо розміщення з огляду на затримку, утилізацію, доступність та операційне відновлення.
Чому цей модуль важливий
Розділ «Чому цей модуль важливий»Гіпотетичний сценарій: під час пікового періоду покупок роздрібний продавець стикається зі збоєм, який спочатку виглядав як звичайний тиск трафіку. Трафік оформлення замовлень зростав, Horizontal Pod Autoscaler створював більше реплік, і команда платформи очікувала, що кластер поглине цей сплеск. Натомість сотні Подів оформлення замовлень залишалися у стані Pending, тоді як наявні репліки дедалі дужче нагрівалися, перезапускалися під тиском пам’яті й не давали клієнтам завершити покупки. Перерва коштувала значної втрати доходу та ручної роботи з відновлення, але самі машини фізично не були вичерпані.
Першопричиною стало нерозуміння планування. Команди застосунків встановили запити CPU рівними щедрим лімітам CPU, бо так здавалося безпечніше, і планувальник трактував ці запити як резервування проти доступної місткості ноди. Ноди часто використовували лише невелику частку свого реального CPU, але на папері вони були заповнені, тож kube-scheduler цілком слушно відмовлявся прив’язувати нові Поди. Нічого не зламалося ні в планувальнику, ні в kubelet, ні у хмарного провайдера; система виконувала контракт, який команди написали, не розуміючи операційних наслідків.
Планування — це рішення площини управління, яке перетворює запис про Под на робоче навантаження, що працює на реальній ноді. Воно визначає, чи зможе репліка запуститися під час інциденту, чи переживе база даних відмову ноди, чи залишаться дорогі ноди з GPU зарезервованими за командою, якій вони потрібні, і чи масштабується кластер ефективно, чи марнує місткість. У цьому модулі ви дізнаєтеся, як kube-scheduler фільтрує, оцінює та прив’язує Поди, а потім попрактикуєтеся читати збої планування як докази, а не вгадувати причини.
Для прикладів команд цей модуль використовує k як локальний псевдонім для kubectl, що відповідає короткій формі, якою багато операторів кластерів послуговуються під час усунення несправностей. Ви можете створити його у своїй оболонці командою alias k=kubectl, і кожна команда k get, k describe, k apply чи k taint, показана тут, — це просто стандартна команда kubectl через цей псевдонім. Концепції стосуються Kubernetes 1.35 та новіших версій, включно зі стабільними примітивами планування, які кандидати на KCNA повинні вміти розпізнавати.
Анатомія kube-scheduler
Розділ «Анатомія kube-scheduler»Под — це лише бажаний запис, доки він не прив’язаний до ноди. Коли ви створюєте Deployment, контролер Deployment створює або оновлює ReplicaSet, а контролер ReplicaSet створює об’єкти Pod, які зазвичай починаються з порожнього spec.nodeName. Ці об’єкти Pod існують у сервері API, але жоден контейнер ще не запущено, жоден образ не завантажено, і жоден kubelet не взяв на себе відповідальність за них. Планування — це момент, коли Kubernetes обирає ноду, яка має зробити бажаний стан реальним.
Kube-scheduler — це компонент площини управління, який стежить за Подами без призначеної ноди. Для кожного незапланованого Пода він запускає цикл планування, що оцінює кластер відносно вимог, переваг та обмежень політики цього Пода. Цей цикл не є єдиною операцією на кшталт «знайти найменш завантажену ноду». Це багатошарове рішення, яке спочатку прибирає неможливі ноди, потім ранжує придатні ноди, а потім записує рішення про прив’язку назад через сервер API, щоб обраний kubelet міг розпочати роботу середовища виконання.
Фаза фільтрації — це жорсткий бар’єр. Нода може мати багато вільного CPU на дашборді й усе одно не пройти фільтрацію, якщо Под просить мітку, якої ноді бракує, taint, який Под не толерує, порт хоста, що вже використовується, або більше запитаної пам’яті, ніж нода має доступно на папері. Фільтрація навмисно сувора, бо розміщення Пода на ноді, яка не може задовольнити його контракт, створило б пізніший збій, який важче діагностувати. Под у стані Pending часто безпечніший за Под, який стартує на неправильному обладнанні й падає під навантаженням.
Під час фільтрації планувальник ставить практичну послідовність запитань. Чи має нода достатньо незарезервованого CPU та пам’яті для запитів Пода? Чи відповідають мітки ноди будь-яким обов’язковим правилам афінності до нод чи nodeSelector? Чи сумісні потрібні томи з топологією ноди, наприклад та сама зона доступності, що й зональний персистентний диск? Чи толерує Под кожен релевантний taint на ноді? Якщо нода не проходить будь-яку жорстку вимогу, планувальник виключає її з решти цього циклу.
Щойно після фільтрації залишається придатний набір, оцінювання обирає найкращого кандидата серед тих, що вціліли. Оцінювання — це там, де мають значення м’які переваги: ноди з кращим балансом ресурсів можуть отримати вищу оцінку, ноди, що вже мають потрібний образ, можуть запустити Под швидше, а ноди, що відповідають бажаним правилам афінності, можуть отримати додаткову вагу. Саме тому правила планування існують у жорсткій та м’якій формах. Жорсткі правила захищають коректність, тоді як м’які правила виражають переваги, які мають покращити розміщення, не перетворюючи звичайний тиск на місткість на збій.
Завершальний крок прив’язки легко проґавити, бо це не там, де стартують контейнери. Планувальник подає рішення про прив’язку через сервер API, який встановлює spec.nodeName Пода на ноду-переможця. Kubelet на цій ноді стежить за сервером API, бачить призначений собі Под, а потім завантажує образи, налаштовує мережу, монтує томи та просить середовище виконання контейнерів запустити контейнери. Якщо ви пам’ятаєте цей поділ, усунення несправностей стає зрозумілішим: планувальник вирішує розміщення, тоді як події kubelet та середовища виконання пояснюють, що відбувається після розміщення.
+-----------------------------------------------------------------------+| АЛГОРИТМ ПЛАНУВАННЯ |+-----------------------------------------------------------------------+| || 1. СПОСТЕРЕЖЕННЯ: kube-scheduler бачить Под без nodeName || || 2. ФІЛЬТРАЦІЯ (жорсткі обмеження) || Нода A: не проходить (недостатньо пам'яті) || Нода B: проходить || Нода C: не проходить (бракує обов'язкової мітки) || Нода D: проходить || || 3. ОЦІНЮВАННЯ (м'які переваги на придатних нодах) || Нода B: || - Образ уже в кеші: +20 балів || - Багато доступного CPU: +50 балів || Сумарна оцінка: 70 || || Нода D: || - Образ не в кеші: 0 балів || - Дуже багато доступн. CPU: +90 балів || Сумарна оцінка: 90 <--- ПЕРЕМОЖЕЦЬ || || 4. ПРИВ'ЯЗКА: планувальник каже серверу API призначити Под Ноді D || || 5. ВИКОНАННЯ: kubelet на Ноді D бачить призначення й стартує Под || |+-----------------------------------------------------------------------+Зупиніться та спрогнозуйте: якщо кластер має сотні нод, чи має планувальник витрачати однакові зусилля на оцінювання кожної можливої ноди для кожного нового Пода, чи має він зупинятися, щойно набере достатньо придатних кандидатів для ухвалення доброго рішення? Реальний планувальник балансує між якістю розміщення та затримкою планування, що має значення, коли контролер створює багато замінних Подів під час інциденту. Ідеальне рішення, яке приходить запізно, може бути операційно гіршим за добре рішення, яке приходить швидко й тримає розгортання в русі.
Корисна ментальна модель — призначення воріт в аеропорту. Фільтрація прибирає ворота, які фізично не можуть прийняти літак, бо ворота замалі, закриті чи призначені для обмеженої операції іншої авіакомпанії. Оцінювання обирає серед решти воріт за зручністю, часом руління та операційною перевагою. Прив’язка — це офіційне призначення, яке каже наземній команді, де готуватися, але саме призначення не вивантажує пасажирів; воно лише передає виконання команді, відповідальній за ці ворота.
Гіпотетичний сценарій: команда платіжного сервісу якось звинуватила «повільний Kubernetes», бо замінні Поди з’являлися кілька хвилин під час навчання з відмовлення бази даних. Фактичні події FailedScheduling показали, що кожна нода, яка відповідала правилу афінності бази даних, також несла maintenance-taint, який замінні Поди не толерували. Щойно команда побачила планування як конвеєр «фільтрувати-та-оцінювати», вона перестала перезапускати контролери й натомість виправила суперечливий контракт розміщення. Кластер увесь час давав їм відповідь.
Є ще один практичний наслідок для операторів: планування достатньо повторюване, щоб його діагностувати, але достатньо динамічне, щоб час усе ще мав значення. Под може не пройти планування опівдні, бо кожна відповідна нода заповнена, а потім успішно заплануватися за дві хвилини, коли інше розгортання завершує старі репліки й вивільняє запитану місткість. Це не робить попередню подію хибною. Це означає, що планувальник ухвалив рішення зі знімка кластера, доступного в тому циклі, а потім спробував знову, коли стан кластера змінився. Сприймайте події як докази з часовою позначкою, а не як остаточні вироки.
Запити та ліміти ресурсів: основа розміщення
Розділ «Запити та ліміти ресурсів: основа розміщення»Запити ресурсів — це валюта планувальника. Запит CPU каже: «зарезервуй принаймні стільки місткості CPU для розміщення», а запит пам’яті каже те саме для пам’яті. Планувальник порівнює ці запити з доступною місткістю ноди, тобто місткістю ноди після вилучення резервувань Kubernetes та операційної системи. Потім він віднімає запити Подів, уже призначених на ноду, а не використання в реальному часі, показане на дашборді моніторингу.
Ця модель паперового резервування — причина того, чому розмір ресурсів так сильно впливає на надійність і вартість. Якщо нода має чотири доступні ядра CPU й чотири наявні Поди, кожен з яких запросив одне повне ядро, планувальник трактує ноду як заповнену за CPU, навіть якщо всі чотири Поди простоюють. Така поведінка не є помилкою. Kubernetes дав обіцянку Подам, що працюють, коли запланував їх, а порушення цієї обіцянки лише тому, що графік зараз тихий, зробило б перевантаження непередбачуваними.
Запити також впливають на класи Quality of Service та поведінку витіснення, але урок планування рівня KCNA простий: запити вирішують, чи можна розмістити Под. Якщо ви пропускаєте запити, ви можете отримати Под, який легко планується, але стає першою жертвою під час тиску на ноду. Якщо ви завищуєте запити, ви можете отримати Под, захищений на папері, але неможливий для розміщення в масштабі. Добрі запити походять від вимірювання, навантажувального тестування та ітерацій, а не від копіювання найвищого значення, що зробило збій менш імовірним на відчуття.
Ліміти служать іншій меті. Планувальник не використовує ліміт CPU як резервування, якщо тільки стандартні значення допуску чи політики простору імен не копіюють ліміти у запити. Під час виконання kubelet та Linux застосовують ліміти після того, як Под уже розміщено. CPU є стискуваним, тож перевищення ліміту CPU зазвичай дроселює процес і робить його повільнішим. Пам’ять є нестискуваною, тож перевищення ліміту пам’яті може спричинити завершення через Out Of Memory, перезапуск контейнера й, можливо, CrashLoopBackOff.
apiVersion: v1kind: Podmetadata: name: resource-demonstration-podspec: containers: - name: heavy-processing-app image: custom-processor:v2 resources: requests: memory: "1Gi" cpu: "500m" limits: memory: "2Gi" cpu: "1000m"У цьому Поді планувальнику потрібна лише нода з принаймні одним гібібайтом незарезервованої пам’яті та половиною ядра CPU, доступними в придатній місткості. Після того як Под стартує, контейнер може зрости до лімітів пам’яті та CPU, якщо нода може їх надати, але наслідки відрізняються, коли він перетинає ці межі. Тиск на CPU спричиняє дроселювання та затримку. Перевищення пам’яті спричиняє завершення, тому ліміти пам’яті, які надто низькі, можуть перетворити придатний для планування Под на цикл збоїв під час виконання.
Перш ніж запускати це в лабораторії, спрогнозуйте, який збій ви побачили б першим, якби Под запросив 500m CPU, але мав ліміт пам’яті 50Mi, тоді як процесу для ініціалізації потрібно 500Mi. Планувальник, імовірно, розмістив би Под, бо запит скромний, але kubelet згодом повідомив би про повторні перезапуски після того, як процес перевищив ліміт пам’яті. Успіх планування й справність під час виконання пов’язані, але це не той самий сигнал.
Для операторів практичний діагностичний крок — порівняти три числа: доступну місткість ноди, сумарну запитану місткість, уже призначену на цю ноду, та запити нового Пода. k describe node <node-name> дає значення доступної місткості та зведення виділених запитів, тоді як k describe pod <pod-name> показує точну подію планування, якщо розміщення не вдалося. Коли Под у стані Pending каже «Insufficient cpu» попри низьке реальне використання CPU, планувальник повідомляє вам, що резервування, а не використання, вичерпали бюджет розміщення.
Розмір ресурсів також впливає на автомасштабування. Автомасштабувальник кластера може додавати ноди, коли Поди неможливо запланувати через нестачу ресурсів, але надмірні запити змушують його купувати більше місткості, ніж застосунок насправді потребує. Занижені запити мають протилежний ризик: Поди пакуються щільно, виглядають дешевими, а потім конфліктують під час виконання, коли надходить трафік. Планувальник настільки чесний, наскільки чесні запити, які ви йому даєте, тож точні запити — це водночас практика надійності й практика контролю витрат.
Політика простору імен часто формує цю поведінку, перш ніж команди застосунків це помічають. LimitRanges можуть установлювати стандартні запити чи ліміти, а ResourceQuotas можуть відхиляти Поди, що пропускають обов’язкові поля ресурсів. Ці політики є корисними запобіжниками, але їх слід документувати, бо вони змінюють те, що бачить планувальник. Коли маніфест не містить запиту, не припускайте, що планувальник бачить нуль. Перевірте допущений Под після створення, бо контролери допуску могли підставити стандартні значення, які змінюють розміщення.
Спрямування трафіку: селектори нод та афінність
Розділ «Спрямування трафіку: селектори нод та афінність»Місткості ресурсів рідко достатньо, щоб розмістити продакшн-навантаження. Деяким Подам потрібні пристрої GPU, диски з високим IOPS, можливості ядра, зони відповідності вимогам або мережева близькість до залежного сервісу. Kubernetes представляє ці факти про ноди як мітки, а правила планування дозволяють Поду вимагати ноди з відповідними мітками або надавати їм перевагу. Важлива відмінність полягає в тому, чи є правило жорсткою вимогою, яка фільтрує ноди, чи м’якою перевагою, яка змінює оцінювання.
Найпростіший механізм — nodeSelector, який зіставляє точні мітки «ключ-значення». Якщо Под має nodeSelector: {disktype: ssd}, кожна придатна нода повинна мати саме цю мітку. Простота корисна для демонстрацій та дуже стабільних середовищ, але вона дає вам лише зіставлення за рівністю та логічне І. Вона не може виразити «зона A або зона B», «надавати перевагу NVMe, але відкочуватися до стандартних дисків» чи «уникати нод із цією міткою». Коли nodeSelector надто суворий, Под залишається у стані Pending, навіть якщо поблизу існує прийнятна альтернатива.
Афінність до нод — це виразніша форма. requiredDuringSchedulingIgnoredDuringExecution — це жорсткий фільтр, схожий за наслідками на nodeSelector, але набагато гнучкіший, бо підтримує оператори на кшталт In, NotIn, Exists, DoesNotExist, Gt та Lt. preferredDuringSchedulingIgnoredDuringExecution — це перевага в оцінюванні з вагами, що означає: нода може стати привабливішою, не стаючи обов’язковою. Цей поділ дозволяє вам резервувати жорсткі правила для коректності та використовувати переваги для оптимізації.
apiVersion: v1kind: Podmetadata: name: advanced-affinity-podspec: affinity: nodeAffinity: # HARD CONSTRAINT: Must be in us-east-1a or us-east-1b requiredDuringSchedulingIgnoredDuringExecution: nodeSelectorTerms: - matchExpressions: - key: topology.kubernetes.io/zone operator: In values: - us-east-1a - us-east-1b # SOFT PREFERENCE: Strongly prefer nodes with dedicated SSDs preferredDuringSchedulingIgnoredDuringExecution: - weight: 80 preference: matchExpressions: - key: hardware-profile/disk operator: In values: - dedicated-nvme containers: - name: data-processor image: data-processor:latestЦей Под може працювати лише у двох перелічених зонах, тож нода в іншій зоні не проходить фільтрацію, навіть якщо має достатньо CPU та пам’яті. У межах придатних зон нода з міткою hardware-profile/disk=dedicated-nvme отримує додаткову оцінку, бо перевага має вагу вісімдесят. Якщо ноди NVMe заповнені, Под усе одно може працювати на ноді зі стандартним диском у дозволених зонах. Така поведінка відкочування — причина того, чому бажана афінність безпечніша для підказок щодо продуктивності, ніж жорсткі селектори.
Фраза IgnoredDuringExecution має значення, бо планувальник ухвалює рішення про розміщення в один момент часу. Якщо Под був запланований на ноду, бо нода мала environment=production, а адміністратор пізніше прибрав цю мітку, наявний Под продовжує працювати. Kubernetes не переоцінює безперервно звичайну афінність і не витісняє запущені Поди через дрейф. Якщо вам потрібна активна корекція після розміщення, ви застосовуєте операційні дії, як-от видалення Подів, спорожнення нод, використання taint’ів NoExecute або розгортання політики descheduler.
Який підхід ви обрали б тут і чому: жорстке правило афінності до нод для accelerator=nvidia на завданні тренування машинного навчання чи бажане правило афінності до нод для hardware-profile/disk=dedicated-nvme на завданні пакетної аналітики? Правило GPU, ймовірно, жорстке, бо навантаження не може працювати без пристрою. Правило диска, ймовірно, бажане, бо повільніші диски можуть бути прийнятними, коли швидший пул зайнятий. Проєктування планування починається з розділення коректності та оптимізації.
Гіпотетичний сценарій: команда платформи даних якось вимагала, щоб кожен Под аналітики працював на нодах NVMe, бо ранні бенчмарки виглядали там краще. Під час квартального звітного прогону ці ноди заповнилися, нові завдання залишалися у стані Pending, і команді довелося вручну редагувати маніфести під тиском. Після перетворення переваги щодо сховища на зважену афінність до нод завдання все ще надавали перевагу NVMe, але продовжували працювати на стандартних нодах, коли місткості бракувало. Звіт зайняв більше часу, але завершився без втручання людини.
Якість міток — тиха залежність за кожним правилом афінності. Якщо мітки накладаються вручну й непослідовно, планувальник сумлінно застосує мапу, яка не відповідає дійсності. Команди платформ повинні трактувати мітки нод для зон, обладнання, відповідності вимогам та стану життєвого циклу як керовані інфраструктурні дані, бажано встановлені автоматизацією чи політикою допуску. Чим критичніше для бізнесу правило розміщення, тим менш прийнятно, щоб мітка, яка його підкріплює, залежала від разової команди, яку хтось може забути під час заміни ноди.
Афінність Подів, анти-афінність та рівномірний розподіл за топологією
Розділ «Афінність Подів, анти-афінність та рівномірний розподіл за топологією»Афінність до нод описує, як Под пов’язаний з обладнанням. Афінність та анти-афінність Подів описують, як Под пов’язаний з іншими Подами, що вже існують. Ця відмінність є вирішальною для розподілених систем, бо репліки не є незалежними прикрасами на мапі кластера. Веб-фронтенди можуть потребувати близькості до кешів задля затримки, тоді як репліки одного критичного сервісу часто мають триматися нарізно, щоб відмова однієї ноди чи зони не прибрала всю місткість одразу.
Афінність Подів притягує робочі навантаження до відповідних Подів у домені топології. Веб-сервіс із високим трафіком, який багаторазово читає з Redis, може отримати вигоду від роботи в тій самій зоні або, в рідкісних випадках, на тій самій ноді, що й кеш. Планувальник оцінює наявні Поди, що відповідають селектору міток, і запитує, чи поділяє кандидатна нода запитаний ключ топології з тими Подами. Це потужно, але також може створити тісне зчеплення, тож ви використовуєте це, коли затримка чи локальність даних чітко виправдовують обмеження розміщення.
Анти-афінність Подів відштовхує робочі навантаження від відповідних Подів. Якщо Deployment має три репліки й усі три потрапляють на одну ноду, втрата єдиної ноди може стерти весь сервіс. Анти-афінність може вимагати, щоб репліки розподілялися по іменах хостів чи зонах, відхиляючи ноди, чий домен топології вже містить відповідний Под. Компроміс — це доступність проти можливості планування: суворе розподілення чудове, доки кластеру не забракне доменів, після чого додаткові репліки залишаються у стані Pending замість порушення правила.
topologyKey визначає значення «поруч» чи «нарізно». З kubernetes.io/hostname доменом є окрема нода, тож анти-афінність не дає двом відповідним реплікам спільно використовувати фізичну чи віртуальну машину. З topology.kubernetes.io/zone доменом є зона доступності, тож анти-афінність може тримати репліки в окремих доменах відмов. Якщо ви оберете неправильний ключ топології, YAML може бути валідним, тоді як результат щодо доступності буде хибним.
apiVersion: apps/v1kind: Deploymentmetadata: name: highly-available-webspec: replicas: 3 selector: matchLabels: app: web-frontend template: metadata: labels: app: web-frontend spec: affinity: podAntiAffinity: # Force the scheduler to place every replica in a DIFFERENT availability zone requiredDuringSchedulingIgnoredDuringExecution: - labelSelector: matchLabels: app: web-frontend topologyKey: "topology.kubernetes.io/zone" containers: - name: web-server image: nginx:alpineЦей Deployment дає сильну обіцянку: жодні дві репліки web-frontend не повинні працювати в тій самій зоні. Ця обіцянка спрацьовує лише тоді, коли кластер має достатньо позначених зон для розміщення запитаної кількості реплік. Якщо ви просите п’ять реплік у кластері з трьома зонами з обов’язковою зональною анти-афінністю, планувальник може розмістити три й мусить залишити решту реплік у стані Pending. Kubernetes не порушить жорстке правило лише тому, що Deployment хоче більше реплік.
Обмеження рівномірного розподілу за топологією надають гнучкішу альтернативу для багатьох проєктів доступності. Замість казати «ніколи не розміщуй ці Поди разом», ви визначаєте максимальний перекіс між доменами топології й обираєте, чи планувальник повинен застосовувати правило, чи має лише надавати йому перевагу. Наприклад, сервіс може вимагати, щоб зони залишалися в межах однієї репліки одна від одної, що запобігає випадковим скупченням, уникаючи водночас частини жорсткої поведінки «все або нічого» суворої анти-афінності. Це особливо корисно для більшої кількості реплік, де точне розміщення «один на зону» не є метою.
Зупиніться та спрогнозуйте: у кластері з трьома зонами та шістьма репліками фронтенду, яку операційну різницю ви очікували б між суворою зональною анти-афінністю та обмеженням рівномірного розподілу за топологією з низьким перекосом? Сувора анти-афінність може заблокуватися після трьох реплік, бо кожна зона вже містить відповідний Под. Обмеження розподілу може дозволити дві репліки на зону, водночас запобігаючи нерівномірному розміщенню на кшталт п’яти реплік в одній зоні й однієї в іншій.
Гіпотетичний сценарій: команда, що працювала зі status API, вважала себе високодоступною, бо кожна репліка використовувала анти-афінність kubernetes.io/hostname. Відмова хмарної зони все одно прибрала більшу частину сервісу, бо всі репліки потрапили на різні ноди всередині тієї самої зони. Після інциденту вони перейшли від анти-афінності лише за хостом до обмежень рівномірного розподілу за топологією з урахуванням зон, а потім перевіряли розміщення командою k get pods -o wide під час кожної репетиції релізу. Правило, якого вони потребували, було не «різні машини»; це було «різні домени відмов».
Для міркувань рівня KCNA головна звичка — назвати домен відмови, перш ніж обирати примітив. Якщо очікувана подія — перезавантаження ноди, релевантне розподілення за хостами. Якщо очікувана подія — відмова зони, релевантне розподілення за зонами. Якщо очікувана подія — перевантаження від усіх реплік, що спільно використовують ту саму залежність кешу, замість анти-афінності може бути релевантною афінність. YAML повинен слідувати за моделлю відмов, а не навпаки.
Taint’и та толерантності: виділені ноди та витіснення
Розділ «Taint’и та толерантності: виділені ноди та витіснення»Афінність — це механізм притягування, тоді як taint’и — механізм відштовхування. Афінність до нод дозволяє Подам шукати ноди з певними властивостями, але вона не зупиняє непов’язані Поди від потрапляння на ці самі ноди. Taint’и розв’язують цю другу проблему, позначаючи ноду так, щоб планувальник відхиляв Поди, яким бракує відповідної толерантності. На практиці виділені пули обладнання майже завжди потребують обох інструментів: афінності, щоб затягнути потрібні навантаження, та taint’ів, щоб тримати небажані навантаження ззовні.
Уявіть кластер з дорогими нодами GPU для тренування моделей. Якщо ви лише позначаєте ці ноди й даєте Подам машинного навчання афінність до нод, завдання ML можуть їх знайти, але звичайні веб-Поди все одно можуть використовувати вільний там CPU та пам’ять, коли планувальник вважає розміщення вдалим. Це марнує спеціалізоване обладнання й може заблокувати наступне завдання тренування. Taint на кшталт dedicated=machine-learning:NoSchedule перетворює ноди GPU на обмежений пул.
Наявна форма команди проста, і з локальним псевдонімом вона стає k taint nodes gpu-node-01 dedicated=machine-learning:NoSchedule. Taint має ключ, значення та ефект. Толерантність Пода повинна відповідати цим полям, залежно від його оператора, перш ніж планувальник зможе розглянути ноду. Толерантність — це не запит на роботу на ноді; це дозвіл пройти повз taint. Зазвичай ви поєднуєте її з афінністю до нод чи селектором, коли Под має активно цілитися в пул.
Ефекти taint’ів визначають, наскільки сильним є відштовхування. NoSchedule блокує нові Поди без толерантності, але залишає наявні Поди в спокої. PreferNoSchedule — це м’яка перевага, яка перешкоджає розміщенню, але може дозволити його, коли в кластера немає кращих варіантів. NoExecute — це агресивна форма: вона блокує нові Поди без толерантності й витісняє наявні Поди без толерантності з ноди. Kubernetes використовує поведінку у стилі NoExecute всередині для серйозних станів нод, як-от недосяжні ноди.
apiVersion: v1kind: Podmetadata: name: data-science-jobspec: # The VIP pass to bypass the node's defensive taint tolerations: - key: "dedicated" operator: "Equal" value: "machine-learning" effect: "NoSchedule" # Optional: Also add nodeSelector so it ONLY goes to these nodes nodeSelector: accelerator: nvidia-tesla-v100 containers: - name: ml-processor image: tensor-flow-custom:v4Цей Под має і дозвіл, і спрямування. Толерантність дозволяє йому пройти повз taint dedicated=machine-learning:NoSchedule, тоді як nodeSelector гарантує, що він цілиться в ноди, позначені прискорювачем GPU. Якщо ви приберете толерантність, Под може відповідати мітці ноди, але не пройти фільтр taint. Якщо ви приберете селектор, Под може толерувати пул GPU, але все одно запланується деінде, якщо інша нода отримає кращу оцінку.
Операційна пастка — припускати, що толерантності є ексклюзивними резервуваннями. Вони не такі. Толерантність каже: «мені дозволено працювати там», а не «я мушу працювати там» чи «лише я можу там працювати». Ексклюзивність походить від поєднання taint’ів ноди, толерантностей навантаження та правил вибору навантаження. Для виділених пулів вам також потрібні угоди про іменування та мітки, які люди можуть легко аудитувати під час інцидентів.
Гіпотетичний сценарій: команда платформи створила пул нод бази даних і дала Подам бази даних толерантності, але забула додати обов’язкову афінність до нод. У спокійний період планувальник розмістив деякі репліки бази даних на звичайних нодах, бо ті ноди отримали добру оцінку, а Поди не були зобов’язані обирати пул бази даних. Виправленням був не ще один taint. Виправленням було виразити обидві половини контракту: відштовхувати загальні навантаження від нод бази даних та вимагати, щоб навантаження бази даних обирали їх.
Taint’и також цінні під час операцій, бо вони можуть повідомляти тимчасовий намір. Maintenance-taint NoSchedule може тримати нову роботу подалі від ноди перед спорожненням, даючи команді платформи час оглянути те, що вже працює. Taint NoExecute набагато сильніший, і його слід використовувати обережно, бо він змінює стан навантаження, що вже працює. Ця відмінність має значення під час реагування на інциденти: один ефект запобігає новому ризику, тоді як інший активно переміщує роботу й може створити навантаження деінде.
Діагностика Подів у стані Pending: мислення оператора
Розділ «Діагностика Подів у стані Pending: мислення оператора»Под у стані Pending — це не загадка; це незадоволений контракт планування. Планувальник оцінив Под відносно кластера й не зміг знайти ноду, яка пройшла кожен жорсткий фільтр. Ваш перший діагностичний крок — це не перезапустити Deployment, видалити випадкові Поди чи наосліп додати більшу ноду. Ваш перший крок — прочитати подію, записану планувальником, і перекласти її на конкретний перелік невиконаних обмежень.
Найважливіша команда — k describe pod <pod-name>, потім розділ Events: унизу. Типова подія може казати: 0/15 nodes are available: 5 node(s) didn't match Pod's node affinity/selector, 6 Insufficient memory, 4 node(s) had taint {dedicated: database}, that the pod didn't tolerate. Це речення — стиснутий звіт про інцидент. Воно каже вам загальний розмір кластера, які фільтри відхилили ноди, і чи проблема в мітках, ресурсах, taint’ах, портах, топології чи сховищі.
Вам слід читати подію як розбиття кластера на частини. У тому прикладі п’ять нод не пройшли вимоги щодо мітки чи афінності, шість нод не пройшли запити пам’яті, а чотири ноди не пройшли толерування taint. Немає потреби гадати, чи планувальник забув про ноду. Кожна нода не пройшла принаймні одну жорстку умову. Виправлення повинно змінити одну з цих умов: скоригувати запити, додати місткість, виправити мітки, змінити афінність, додати толерантність або перемістити навантаження до краще пристосованого пулу.
Подія також допомагає вам уникати хибних висновків з дашбордів. Низьке спостережуване використання CPU не суперечить Insufficient cpu, бо планувальник працює із запитів. Багато вільного дискового простору не виправляє відсутню відповідність топології тому. Справний статус ноди не скасовує taint. Добра діагностика планування означає поважання словника планувальника, а потім перевірку конкретних полів об’єктів, що породжують цей словник.
Коли ви усуваєте несправності, рухайтеся від Пода назовні. Почніть зі специфікації Пода та подій, потім огляньте мітки кандидатних нод за допомогою k get nodes --show-labels, taint’и нод за допомогою k describe node, доступну місткість за допомогою k describe node та наявну запитану місткість у розділі виділених ресурсів. Для проблем з топологією перевірте, що мітки нод, як-от topology.kubernetes.io/zone, існують і є послідовними. Для проблем зі сховищем перевірте, чи живуть PersistentVolume та ноди в сумісних зонах.
Розв’язаний приклад робить метод конкретним. Припустимо, платіжний Под каже 0/8 nodes are available: 3 node(s) didn't match Pod's node affinity/selector, 5 Insufficient cpu. Неправильна реакція — сказати: «CPU використовується лише на десять відсотків, тож Kubernetes зламаний». Краща реакція — оглянути запитаний CPU, уже призначений на ці п’ять нод, а потім вирішити, чи запит Пода чесний, чи старі навантаження перезапитані, чи кластеру потрібно більше місткості.
Перш ніж щось змінювати, спрогнозуйте, яке виправлення було б найменш ризикованим у тому платіжному прикладі: зниження запиту CPU нового Пода, додавання міток до трьох відхилених нод чи додавання нод. Якщо запит роздутий порівняно з виміряним навантаженням, його зниження може бути правильним. Якщо мітки неправильні, бо ноди насправді придатні, виправлення міток може бути правильним. Якщо запити точні, а навантаження критичне для бізнесу, додавання місткості є чесним рішенням. Подія каже вам, куди дивитися, але інженерне судження обирає засіб.
Добрі команди перетворюють цей діагностичний процес на runbook. Runbook повинен казати відповідальним зафіксувати подію FailedScheduling, оглянути запити та правила розміщення Пода, оглянути мітки та taint’и кандидатних нод і записати обране виправлення. Цей запис стає навчальними даними для майбутніх оглядів розмірів, політик та архітектури. Інциденти планування повторюються, коли команди виправляють симптом, не зберігаючи доказів, що пояснювали невідповідність контракту.
Патерни та антипатерни
Розділ «Патерни та антипатерни»Патерни планування — це багаторазові контракти між командами застосунків та платформою. Вони працюють найкраще, коли їх легко пояснити під час інциденту, коли вони спостережувані з об’єктів Kubernetes і перевірені навчаннями з відмов. Мета — не зробити кожну специфікацію Пода хитромудрою. Мета — закодувати ті кілька правил розміщення, які насправді захищають надійність, продуктивність чи вартість, залишаючи планувальнику достатньо свободи, щоб тримати кластер придатним до використання.
| Патерн | Коли його використовувати | Чому це працює | Міркування щодо масштабування |
|---|---|---|---|
| Запити з виміряних базових рівнів | Ви маєте продакшн- або навантажувальні дані щодо поведінки CPU та пам’яті. | Планувальник отримує реалістичні резервування, тож розміщення відображає реальний попит без купівлі місткості, яка простоює. | Перегляньте запити після великих релізів, бо ініціалізація, кеші та форма трафіку можуть змінитися. |
| Обов’язкова афінність для коректності | Навантаження не може функціонувати без властивості обладнання, відповідності чи топології. | Фільтрація не дає Поду стартувати там, де він пізніше впав би чи порушив політику. | Тримайте набір обов’язкових міток малим і послідовно застосованим, інакше розгортання можуть стати крихкими. |
| Бажана афінність для оптимізації | Навантаження отримує вигоду від властивості ноди, але може толерувати відкочування. | Оцінювання покращує розміщення, не перетворюючи тимчасову нестачу на репліки у стані Pending. | Використовуйте ваги навмисно й стежте, чи переваги насправді задовольняються під навантаженням. |
| Taint плюс афінність для виділених пулів | Ви запускаєте GPU, ноди бази даних, регульовані зони чи пули ізоляції. | Taint’и відштовхують загальні навантаження, тоді як афінність затягує потрібні навантаження в пул. | Аудитуйте обидві сторони разом; сама лише толерантність не вимагає розміщення в пулі. |
| Рівномірний розподіл за топологією для багатьох реплік | Вам потрібні збалансовані репліки по зонах чи нодах, особливо понад одну репліку на домен. | Обмеження перекосу запобігають скупченням, зберігаючи більше гнучкості планування, ніж сувора анти-афінність. | Обирайте whenUnsatisfiable обережно, бо суворе застосування все ще може блокувати розгортання. |
Антипатерни зазвичай походять від трактування одного примітиву планування так, ніби він розв’язував кожну проблему розміщення. nodeSelector виглядає просто, тож команди використовують його для переваг і випадково створюють жорсткі збої. Толерантність звучить як резервування, тож команди забувають про афінність і дивуються, чому навантаження потрапляють деінде. Ліміт пам’яті виглядає захисним, тож команди встановлюють його надто низько й створюють перезапуски. Примітиви надійні, але вони роблять точно те, що кажуть, а не те, на що команда сподівалася, що вони мають на увазі.
| Антипатерн | Що йде не так | Краща альтернатива |
|---|---|---|
| Жорсткі селектори для бажаного, але необов’язкового обладнання | Поди залишаються у стані Pending, коли бажані ноди заповнені, хоча прийнятні ноди існують. | Використовуйте бажану афінність до нод із чітким очікуванням відкочування. |
| Запити, скопійовані з лімітів без вимірювання | Планувальник бачить кластер як заповнений, тоді як реальний CPU може простоювати. | Встановлюйте запити з спостережуваного усталеного стану та потреб запуску, потім коригуйте за доказами. |
| Толерантності без вибору ноди | Навантаження можуть працювати будь-де, бо толерантність — це дозвіл, а не притягання. | Поєднуйте толерантності з обов’язковою афінністю чи селектором, коли пул обов’язковий. |
| Зональна анти-афінність із надто багатьма репліками | Додаткові репліки не можна запланувати, щойно кожна зона вже має відповідний Под. | Використовуйте обмеження рівномірного розподілу за топологією, коли збалансований розподіл кращий за ексклюзивність «один на зону». |
| Трактування дрейфу міток як автоматичного витіснення | Наявні Поди продовжують працювати після зміни звичайних міток афінності. | Використовуйте контрольовані розгортання, спорожнення, taint’и NoExecute чи політики descheduler, коли потрібна корекція. |
Мова патернів повинна з’являтися в оглядах. Коли хтось пропонує правило планування, запитайте, чи захищає воно коректність, покращує продуктивність, ізолює вартість чи підтримує відновлення. Якщо відповідь незрозуміла, правило може бути випадковою складністю. Якщо відповідь зрозуміла, оберіть найменш суворий примітив, який усе ще захищає потрібний результат, бо надмірна суворість — один з найшвидших способів виготовити Поди, що не піддаються плануванню.
Патерни також потребують власників. Команда, що володіє застосунком, зазвичай може налаштовувати запити й вирішувати, чи швидший диск є необов’язковим. Команда платформи зазвичай володіє мітками нод, taint’ами, іменуванням топології та поведінкою автомасштабувальника кластера. Якщо ці межі відповідальності розмиті, помилки планування стають політичними суперечками під час інцидентів. Зробіть контракт явним: маніфести застосунків декларують потреби навантаження, тоді як автоматизація платформи тримає факти про ноди точними, а політики місткості зрозумілими.
Фреймворк ухвалення рішень
Розділ «Фреймворк ухвалення рішень»Використовуйте обмеження планування, починаючи з відмови, якій ви намагаєтеся запобігти. Навантаження, якому потрібен GPU, має вимогу коректності, тож йому потрібне жорстке правило ноди й, імовірно, виділений пул. Навантаження, яке працює швидше на NVMe, має перевагу щодо продуктивності, тож йому, ймовірно, потрібна бажана афінність. Сервіс, який має пережити відмову зони, потребує розподілення з урахуванням зон, а не просто розподілення за хостами. Нода, яка має відхиляти загальні навантаження, потребує taint, а не лише мітки.
Потрібне правило розміщення? | +-- Чи має Под мати властивість ноди, щоб функціонувати? | | | +-- так --> обов'язкова афінність до нод або nodeSelector | | | +-- ні --> бажана афінність до нод | +-- Чи мають звичайні Поди триматися подалі від пулу нод? | | | +-- так --> taint'и на ноди та толерантності лише дозволеним Подам | +-- Чи мають репліки пережити відмову ноди чи зони? | | | +-- мало реплік, сувора ізоляція --> анти-афінність Подів | | | +-- багато реплік, збалансовані домени --> обмеження розподілу за топологією | +-- Чи Под у стані Pending? | +-- читайте події FailedScheduling, перш ніж змінювати YAML| Рішення | Надавайте перевагу цьому | Уникайте цього | Причина |
|---|---|---|---|
| Правильне обладнання обов’язкове | Обов’язкова афінність до нод | Лише бажана афінність | Планувальник мусить відфільтрувати ноди, які не можуть запустити навантаження. |
| Обладнання швидше, але необов’язкове | Бажана афінність до нод | nodeSelector | Под має продовжувати працювати на резервних нодах, коли бажаної місткості немає. |
| Виділений пул потребує захисту | Taint плюс толерантність плюс афінність | Лише мітка | Мітки притягують обрані Поди, але не відштовхують непов’язані Поди. |
| Репліки мають уникати однієї ноди | Анти-афінність Подів на kubernetes.io/hostname | Припущення лише про зони | Розподілення за хостами захищає від відмови єдиної ноди. |
| Репліки мають пережити втрату зони | Розподіл за топологією чи анти-афінність на topology.kubernetes.io/zone | Розподілення лише за хостами | Різні ноди в одній зоні все одно поділяють домен відмови зони. |
| Розміщення не вдається | k describe pod та огляд нод | Випадкові перезапуски | Події планувальника визначають фільтри, які відхилили кожну ноду. |
Добрий огляд проєкту може використати три запитання. Перше: яка мінімальна гарантія розміщення потрібна навантаженню, щоб бути коректним? Друге: яка перевага розміщення покращила б продуктивність, вартість чи відновлення, якщо місткість дозволяє? Третє: які докази оператори оглядатимуть, коли Под у стані Pending? Якщо YAML відповідає на ці запитання, контракт планування, ймовірно, буде зрозумілим під час реального інциденту.
Фреймворк навмисно консервативний, бо планувальники працюють найкраще, коли мають простір для оптимізації. Кожне жорстке правило прибирає ноди з розгляду, що може бути цілком правильним для коректності й цілком неправильним для переваги. Кожне м’яке правило впливає на ранжування, але може бути проігноровано, коли тиск на місткість звужує придатний набір. Звичка старшого оператора — запитати: «Що станеться, коли ідеальна нода недоступна?» Якщо відповідь — прийнятне відкочування, зробіть правило м’яким. Якщо відповідь — зламане чи небезпечне навантаження, зробіть його жорстким і переконайтеся, що місткість існує.
Чи знали ви?
Розділ «Чи знали ви?»- Масштаб планувальника навмисно обмежений: у великих кластерах kube-scheduler може припинити пошук після того, як знайде налаштований відсоток придатних нод, зі стандартними значеннями, які жертвують ідеальним глобальним оцінюванням заради нижчої затримки планування.
- Кастомні планувальники можуть співіснувати: Kubernetes дозволяє Поду вказати
schedulerName, тож кластери можуть запускати спеціалізовані планувальники поруч зі стандартним планувальником для незвичних політик розміщення. - Стандартний планувальник не перерозподіляє запущені Поди: щойно Под прив’язано, звичайна логіка планувальника не переміщує його пізніше лише тому, що кластер став нерівномірним чи мітки дрейфують.
- Рівномірний розподіл за топологією став стабільним у Kubernetes 1.19: ця можливість дає операторам розподілення на основі перекосу, яке часто гнучкіше за сувору анти-афінність «один Под на домен».
Типові помилки
Розділ «Типові помилки»| Помилка | Чому це стається | Як це виправити |
|---|---|---|
Використання nodeSelector для м’яких переваг | nodeSelector легко читати, тож команди використовують його для бажаного, але необов’язкового обладнання й випадково створюють жорстку вимогу планування. | Використовуйте бажану афінність до нод, коли резервні ноди прийнятні, а жорсткі селектори резервуйте для коректності. |
| Встановлення лімітів без продуманих запитів | Команди зосереджуються на стримуванні під час виконання й забувають, що саме запити планувальник використовує для місткості розміщення. | Визначте явні запити зі спостережуваних базових рівнів, потім встановлюйте ліміти лише там, де примус під час виконання корисний. |
Нерозуміння topologyKey | YAML приймає багато ключів міток, тож оператор може обрати ключ, який не представляє очікуваний домен відмови. | Використовуйте kubernetes.io/hostname для розділення нод та topology.kubernetes.io/zone для розділення зон. |
Покладання на IgnoredDuringExecution для очищення | Фраза звучить як політика, але вона означає, що звичайна афінність перевіряється під час планування, а не примусово застосовується безперервно пізніше. | Перезапускайте чи спорожнюйте Поди навмисно, використовуйте taint’и NoExecute або descheduler, коли потрібна активна корекція. |
| Застосування лише афінності до виділеного обладнання | Афінність притягує потрібні Поди, але не тримає звичайні Поди подалі від дорогих чи обмежених нод. | Додайте taint’и до виділених нод і відповідні толерантності плюс правила вибору до потрібних навантажень. |
| Встановлення надмірно суворих лімітів CPU | Команди намагаються запобігти галасливим сусідам, але спричиняють дроселювання під час запуску, компіляції чи сплесків запитів. | Використовуйте реалістичні запити CPU для розміщення й застосовуйте ліміти CPU обережно після вимірювання впливу на затримку. |
| Додавання толерантності без відповідного селектора | Толерантність плутають із правилом призначення, тож Поди все одно можуть запланувати поза виділеним пулом. | Поєднуйте толерантності з обов’язковою афінністю до нод чи селекторами, коли навантаження має потрапити в цей пул. |
Тест: операційні сценарії
Розділ «Тест: операційні сценарії»Запитання 1: Платіжний Под у стані `Pending`, і `k describe pod` повідомляє `0/8 nodes are available: 3 node(s) didn't match Pod's node affinity/selector, 5 Insufficient cpu`. Дашборди показують, що п'ять нод, які не пройшли за CPU, завантажені лише на десять відсотків. Що слід перевірити, перш ніж змінювати Deployment?
Перевірте запитаний CPU, уже призначений на ці ноди, а не лише використання CPU в реальному часі. Планувальник фільтрує за доступною місткістю мінус наявні запити, тож нода може виглядати тихою, будучи заповненою на папері. Вам слід порівняти запит CPU нового Пода з виділеними ресурсами ноди й вирішити, чи наявні запити роздуті, чи новий запит роздутий, чи потрібно більше місткості. Подія також каже, що три ноди не пройшли правила афінності чи селектора, тож мітки та обов’язкову афінність слід оглянути окремо.
Запитання 2: Ваша команда хоче, щоб три репліки API, орієнтованого на клієнтів, пережили відмову зони, але поточне правило анти-афінності використовує `kubernetes.io/hostname`. Розгортання виглядає справним. Який ризик залишається?
Репліки можуть бути на різних нодах, але все одно поділяти ту саму зону доступності. Анти-афінність за хостом захищає від відмови єдиної ноди, але не гарантує різноманітності зон. Щоб захиститися від втрати зони, використовуйте topology.kubernetes.io/zone у відповідній анти-афінності чи правилі рівномірного розподілу за топологією. Вам слід перевірити мітки нод та фактичне розміщення Подів за допомогою k get pods -o wide під час розгортання.
Запитання 3: Завдання машинного навчання використовує обов'язкову афінність до нод для `accelerator=nvidia`, але звичайні веб-Поди також працюють на нодах GPU й блокують нові завдання. Якого механізму планування бракує?
Нодам GPU потрібен taint, а Подам ML — відповідна толерантність. Афінність до нод притягує навантаження ML до нод GPU, але не відштовхує непов’язані навантаження. Taint на кшталт dedicated=machine-learning:NoSchedule тримає звичайні Поди подалі, якщо вони його не толерують. Поди ML також повинні зберігати своє правило вибору ноди, щоб і толерувати, і цілитися у виділений пул.
Запитання 4: Deployment встановлює запит CPU `4000m` та ліміт пам'яті `50Mi`, тоді як застосунок зазвичай потребує `200m` CPU та `500Mi` пам'яті. Що стається під час планування та виконання?
Під час планування Под вимагає чотири повні ядра CPU незарезервованої місткості, тож він може залишатися у стані Pending, хоча застосунок зазвичай потребує набагато менше CPU. Якщо він усе ж запланується, ліміт пам’яті значно нижчий за фактичну потребу застосунку під час запуску. Процес перевищить нестискувану межу пам’яті й може бути завершений через OOMKilled ядром. Це поєднаний збій розміщення й розміру під час виконання, а не єдина проблема планувальника.
Запитання 5: Адміністратор застосовує `disk-failure=true:NoExecute` до ноди з п'ятьма запущеними Подами, і жоден з цих Подів не має відповідної толерантності. Чого слід очікувати контролерам та операторам?
NoExecute впливає на запущені Поди, а також на майбутнє планування, тож Поди без толерантності витісняються з ноди. Якщо ці Поди керовані контролерами на кшталт Deployment чи ReplicaSet, контролери створюють заміни. Планувальник потім розміщує заміни на нодах, що проходять їхні обмеження. Оператори все одно повинні підтвердити, що існує достатньо справної місткості, бо витіснення лише розпочинає відновлення; воно не гарантує успішного розміщення заміни.
Запитання 6: Поди аналітики повинні надавати перевагу високошвидкісним нодам NVMe, але все одно працювати на стандартних дисках, коли швидкий пул заповнений. Яке правило розміщення відповідає цій меті?
Використовуйте бажану афінність до нод із вагою для мітки NVMe. Це правило бере участь в оцінюванні, тож ноди NVMe стають привабливішими, коли вони придатні. Якщо ці ноди не проходять фільтрацію, бо заповнені чи недоступні, Под усе одно може запланувати на стандартній ноді. Жорсткий nodeSelector чи правило обов’язкової афінності заблокувало б відкочування й перетворило перевагу щодо продуктивності на проблему доступності.
Запитання 7: Команда хоче шість реплік, збалансованих по трьох зонах, і не вимагає рівно однієї репліки на зону. Чому обмеження рівномірного розподілу за топологією можуть бути кращими за сувору зональну анти-афінність?
Сувора зональна анти-афінність може заблокувати планування після того, як кожна зона вже містить одну відповідну репліку, залежно від точного селектора та топології. Обмеження рівномірного розподілу за топологією дозволяють виразити збалансований перекіс, як-от тримати зони в межах однієї репліки одна від одної. Це дозволяє дві репліки на зону, водночас запобігаючи несиметричному розміщенню. Зазвичай це краще пасує для більшої кількості реплік, де баланс важливіший за повне розділення.
Практична вправа: інженерія доступності
Розділ «Практична вправа: інженерія доступності»У цій вправі ви побудуєте невеликий сценарій планування, що оголює різницю між розміщенням, розподіленням та ізоляцією на основі taint’ів. Використовуйте одноразовий лабораторний кластер Kubernetes 1.35 або новіший із принаймні трьома робочими нодами, якщо це можливо. Якщо ваш локальний кластер має менше нод, поведінка Pending усе одно корисна, бо вона показує, як суворі правила зазнають невдачі, коли кластеру бракує достатньо доменів топології.
Почніть зі створення локального псевдоніма команди, якщо ваша оболонка ще його не має. Решта вправи використовує k, і команди навмисно короткі, бо реальні інциденти планування часто включають повторний огляд. Ви створите Deployment Redis, додасте сувору анти-афінність, taint’нете ноду, потім створите адміністративний Под, якому дозволено обійти taint.
alias k=kubectlk create deployment ha-cache --image=redis:alpine --replicas=3 --dry-run=client -o yaml > ha-cache.yamlЗавдання 1: Створіть маніфест Deployment ha-cache з трьома репліками, потім огляньте згенерований YAML перед його застосуванням. Важливе спостереження: звичайний Deployment не гарантує розділення нод. Без правила афінності чи розподілення планувальник вільний розміщувати репліки там, куди ведуть ресурси, оцінювання та наявний стан кластера.
Розв'язання Завдання 1
Команда вище записує ha-cache.yaml. Перегляньте його, потім застосуйте командою k apply -f ha-cache.yaml й огляньте розміщення за допомогою k get pods -o wide. Якщо всі репліки випадково потрапляють на окремі ноди, пам’ятайте, що це результат, а не гарантія. Видаліть і відтворіть достатньо разів у завантаженому кластері, і ви зрештою можете побачити спільне розміщення.
Завдання 2: Змініть ha-cache.yaml так, щоб жодні дві репліки Redis не могли працювати на тій самій фізичній ноді, потім застосуйте файл. Це завдання прямо відображає проєктний результат: ви використовуєте анти-афінність Подів, щоб закодувати високу доступність проти відмови єдиної ноди.
spec: affinity: podAntiAffinity: requiredDuringSchedulingIgnoredDuringExecution: - labelSelector: matchLabels: app: ha-cache topologyKey: kubernetes.io/hostname containers: - image: redis:alpineРозв'язання Завдання 2
Розмістіть блок affinity під spec.template.spec, а не під верхньорівневим spec Deployment. Застосуйте оновлений маніфест командою k apply -f ha-cache.yaml, потім запустіть k get pods -o wide, щоб перевірити, що репліки на різних нодах. Якщо у вас менше трьох придатних для планування нод, одна чи більше реплік повинні залишитися у стані Pending, що є правильним результатом для суворого правила, яке не може бути задоволене.
Завдання 3: Оберіть одну робочу ноду й застосуйте taint NoSchedule із ключем maintenance та значенням true. Це завдання демонструє, що taint змінює придатність для майбутніх Подів, але не витісняє запущені Поди, якщо ефект не NoExecute.
# Get your node namesk get nodes
# Apply the taint to one node, replacing the placeholder with a real node namek taint nodes <node-2-name> maintenance=true:NoScheduleРозв'язання Завдання 3
Після застосування taint запустіть k describe node <node-2-name> і знайдіть рядок Taints:. Наявні Поди на цій ноді повинні продовжувати працювати, бо NoSchedule впливає на нові розміщення. Якщо ви випадково taint’нули неправильну ноду, приберіть його командою k taint nodes <node-name> maintenance=true:NoSchedule- і повторіть завдання на потрібній ноді.
Завдання 4: Масштабуйте ha-cache до п’яти реплік і діагностуйте будь-які Поди у стані Pending за допомогою подій планувальника. Це завдання відображає діагностичний результат: вам слід прочитати деталі FailedScheduling і пояснити, чи спричинили блокування анти-афінність, taint’и чи місткість.
k scale deployment ha-cache --replicas=5k get podsРозв'язання Завдання 4
Якщо ваша лабораторія має лише три чи чотири придатні ноди, принаймні один новий Под може залишитися у стані Pending. Запустіть k describe pod <pending-pod-name> і огляньте подію FailedScheduling. Ви повинні побачити причини, пов’язані з анти-афінністю, maintenance-taint, недостатніми ресурсами чи їх поєднанням. Суть не в тому, щоб змусити всі п’ять реплік працювати; суть у тому, щоб пояснити точно, чому планувальник відхилив кожну кандидатну ноду.
Завдання 5: Створіть окремий Под admin-toolkit, який може працювати на taint’нутій ноді, поєднавши селектор ноди з відповідною толерантністю. Це завдання відображає результат порівняння, бо селектор притягує Под до ноди, тоді як толерантність дозволяє йому пройти повз taint.
apiVersion: v1kind: Podmetadata: name: admin-toolkitspec: nodeSelector: kubernetes.io/hostname: <node-2-name> tolerations: - key: "maintenance" operator: "Equal" value: "true" effect: "NoSchedule" containers: - name: shell image: busybox command: ["sleep", "3600"]Розв'язання Завдання 5
Збережіть маніфест Пода як admin-pod.yaml, замініть <node-2-name> точним значенням мітки імені хоста ноди, потім запустіть k apply -f admin-pod.yaml. Перевірте розміщення за допомогою k get pod admin-toolkit -o wide. Якщо Под усе ще у стані Pending, опишіть його й порівняйте значення селектора ноди, ключ taint, значення taint, оператор толерантності та ефект.
Завдання 6: Очистіть лабораторію та запишіть докази планування, які ви спостерігали. Очищення є частиною вправи, бо taint’и, залишені у спільній лабораторії, можуть заплутати наступне розгортання й створити оманливі збої.
Розв'язання Завдання 6
Запустіть k delete deployment ha-cache, k delete pod admin-toolkit і приберіть taint командою k taint nodes <node-2-name> maintenance=true:NoSchedule-. Потім запишіть один приклад повідомлення FailedScheduling і точне виправлення, яке ви обрали б у продакшн-інциденті. Ця фінальна нотатка повинна згадувати, чи була першопричиною запити ресурсів, анти-афінність, taint’и, мітки чи топологія.
Використовуйте ці критерії успіху як фінальну перевірку відповідності результатам навчання й не позначайте вправу завершеною, доки кожен пункт не підкріплений виводом команди, повідомленням про подію чи рішенням про розміщення, яке ви можете пояснити:
- Діагностуйте Поди у стані Pending, цитуючи одну подію
FailedSchedulingта зіставляючи кожну фразу з невдалим фільтром планувальника. - Спроєктуйте репліки з високою доступністю, застосовуючи анти-афінність Подів та перевіряючи розміщення реплік за допомогою
k get pods -o wide. - Порівняйте афінність до нод із taint’ами та толерантностями, пояснюючи, чому адмін-Поду потрібні і вибір, і дозвіл.
- Впровадьте перевірки планування з урахуванням ресурсів, оглядаючи доступну місткість ноди чи виділені запити перед зміною кількості реплік.
- Оцініть вибір планувальника щодо розміщення, вирішуючи, чи сувора анти-афінність, чи рівномірний розподіл за топологією були б кращими для вашого лабораторного кластера.
Джерела
Розділ «Джерела»- Документація Kubernetes: Scheduling, Preemption and Eviction
- Документація Kubernetes: Assign Pods to Nodes
- Документація Kubernetes: Resource Management for Pods and Containers
- Документація Kubernetes: Taints and Tolerations
- Документація Kubernetes: Pod Topology Spread Constraints
- Документація Kubernetes: Pod Priority and Preemption
- Документація Kubernetes: Node Pressure Eviction
- Документація Kubernetes: kube-scheduler
- Документація Kubernetes: Scheduler Configuration
- Документація Kubernetes: Well-Known Labels, Annotations and Taints
Наступний модуль
Розділ «Наступний модуль»Модуль 2.2: Масштабування — Дізнайтеся, як Kubernetes переходить від статичного розміщення до автоматизованого зростання, коригуючи репліки за попитом навантаження та сигналами кластера.