Обмеження поширення топології Podʼів
Ви можете використовувати обмеження поширення топології для контролю того, як Podʼи розподіляються по вашому кластеру серед доменів відмов, таких як регіони, зони, вузли та інші користувацькі топологічні домени. Це може допомогти забезпечити високу доступність, а також ефективне використання ресурсів.
Ви можете встановлювати типові обмеження на рівні кластера або налаштовувати обмеження поширення топології для окремих навантажень.
Мотивація
Уявіть, що у вас є кластер, в якому до двадцяти вузлів, і ви хочете запустити навантаження, яке автоматично масштабує кількість реплік, які використовує. Тут може бути як два Podʼи, так і пʼятнадцять. Коли є лише два Podʼи, ви б хотіли, щоб обидва ці Podʼи не працювали на одному вузлі: ви ризикуєте, що відмова одного вузла призведе до зникнення доступу до вашого навантаження.
Крім цього основного використання, є деякі приклади використання, які дозволяють вашим навантаженням отримувати переваги високої доступності та використання кластера.
При масштабуванні та запуску більшої кількості Podʼів важливим стає інша проблема. Припустімо, що у вас є три вузли, на яких працюють по пʼять Podʼів кожен. У вузлах достатньо потужності для запуску такої кількості реплік; проте клієнти, які взаємодіють з цим навантаженням, розподілені по трьох різних центрах обробки даних (або зонах інфраструктури). Тепер ви менше хвилюєтеся про відмову одного вузла, але ви помічаєте, що затримка вища, ніж ви хотіли б, і ви платите за мережеві витрати, повʼязані з передачею мережевого трафіку між різними зонами.
Ви вирішуєте, що при нормальній роботі ви б хотіли, щоб у кожній інфраструктурній зоні була приблизно однакова кількість реплік, і ви хотіли б, щоб кластер самостійно відновлювався у разі виникнення проблеми.
Обмеження поширення топології Podʼів пропонують вам декларативний спосіб налаштування цього.
Поле topologySpreadConstraints
У API Pod є поле spec.topologySpreadConstraints
. Використання цього поля виглядає так:
---
apiVersion: v1
kind: Pod
metadata:
name: example-pod
spec:
# Налаштувати обмеження поширення топології
topologySpreadConstraints:
- maxSkew: <ціле число>
minDomains: <ціле число> # необовʼязково
topologyKey: <рядок>
whenUnsatisfiable: <рядок>
labelSelector: <обʼєкт>
matchLabelKeys: <список> # необовʼязково; бета з v1.27
nodeAffinityPolicy: [Honor|Ignore] # необовязково; бета з v1.26
nodeTaintsPolicy: [Honor|Ignore] # необовязково; бета з v1.26
### інші поля Pod тут
Додаткову інформацію про це поле можна отримати, запустивши команду kubectl explain Pod.spec.topologySpreadConstraints
або звернувшись до розділу планування довідки API для Pod.
Визначення обмежень поширення топології
Ви можете визначити один або кілька записів topologySpreadConstraints
, щоб вказати kube-scheduler, як розмістити кожний вхідний Pod у відповідно до наявних Podʼів у всьому кластері. Ці поля включають:
maxSkew описує ступінь нерівномірного поширення Pod. Ви повинні вказати це поле, і число повинно бути більше нуля. Його семантика відрізняється залежно від значення
whenUnsatisfiable
:- якщо ви виберете
whenUnsatisfiable: DoNotSchedule
, тодіmaxSkew
визначає максимально допустиму різницю між кількістю відповідних Podʼів у цільовій топології та глобальним мінімумом (мінімальна кількість відповідних Podʼів у прийнятній області або нуль, якщо кількість прийнятних областей менше, ніж MinDomains). Наприклад, якщо у вас є 3 зони з 2, 2 та 1 відповідно відповідних Podʼів,MaxSkew
встановлено на 1, тоді глобальний мінімум дорівнює 1. - якщо ви виберете
whenUnsatisfiable: ScheduleAnyway
, планувальник надає вищий пріоритет топологіям, які допомагають зменшити розрив.
- якщо ви виберете
minDomains вказує мінімальну кількість прийнятних областей. Це поле є необовʼязковим. Домен — це певний екземпляр топології. Прийнятний домен — це домен, чиї вузли відповідають селектору вузлів.
Примітка:
До Kubernetes v1.30 полеminDomains
було доступним, якщо функціональну можливістьMinDomainsInPodTopologySpread
було увімкнено (типово увімкнено починаючи з v1.28). В старіших кластерах Kubernetes воно може бути явно відключеним або поле може бути недоступним.- Значення
minDomains
повинно бути більше ніж 0, коли вказано. Ви можете вказатиminDomains
лише разом зwhenUnsatisfiable: DoNotSchedule
. - Коли кількість прийнятних доменів з відповідними ключами топології менше
minDomains
, розподіл топології Pod розглядає глобальний мінімум як 0, а потім виконується розрахунокskew
. Глобальний мінімум — це мінімальна кількість відповідних Podʼів у прийнятному домені, або нуль, якщо кількість прийнятних доменів менше, ніжminDomains
. - Коли кількість прийнятних доменів з відповідними ключами топології дорівнює або більше
minDomains
, це значення не впливає на планування. - Якщо ви не вказуєте
minDomains
, обмеження поводиться так, як якбиminDomains
дорівнював 1.
- Значення
topologyKey — ключ міток вузла. Вузли, які мають мітку з цим ключем і ідентичними значеннями, вважаються присутніми в тій самій топології. Кожен екземпляр топології (іншими словами, пара <ключ, значення>) називається доменом. Планувальник спробує помістити вирівняну кількість Podʼів в кожен домен. Також ми визначаємо прийнятний домен як домен, вузли якої відповідають вимогам nodeAffinityPolicy та nodeTaintsPolicy.
whenUnsatisfiable вказує, як розвʼязувати проблему з Pod, якщо він не відповідає обмеженню поширення:
DoNotSchedule
(типово) вказує планувальнику не планувати його.ScheduleAnyway
вказує планувальнику все одно його планувати, проте з пріоритетом вибору вузлів, що мінімізують розрив.
labelSelector використовується для знаходження відповідних Podʼів. Podʼи, які відповідають цьому селектору міток, враховуються для визначення кількості Podʼів у відповідному домені топології. Дивіться селектори міток для отримання додаткових відомостей.
matchLabelKeys — це список ключів міток Podʼа для вибору Podʼів, відносно яких буде розраховано поширення. Ключі використовуються для вибору значень з міток Podʼів, ці ключі-значення міток AND
labelSelector
вибирають групу наявних Podʼів, відносно яких буде розраховано поширення для вхідного Podʼа. Існування однакового ключа заборонене як уmatchLabelKeys
, так і вlabelSelector
.matchLabelKeys
не може бути встановлено, колиlabelSelector
не встановлено. Ключі, яких не існує в мітках Podʼа, будуть проігноровані. Порожній або нульовий список означає, що збіг буде відповідати лишеlabelSelector
.З
matchLabelKeys
вам не потрібно оновлюватиpod.spec
між різними версіями. Контролер/оператор просто повинен встановити різні значення для того самого ключа мітки для різних версій. Планувальник автоматично припускає значення на основіmatchLabelKeys
. Наприклад, якщо ви налаштовуєте Deployment, ви можете використовувати мітку за ключем pod-template-hash, яка додається автоматично контролером Deployment, для розрізнення різних версій в одному Deployment.topologySpreadConstraints: - maxSkew: 1 topologyKey: kubernetes.io/hostname whenUnsatisfiable: DoNotSchedule labelSelector: matchLabels: app: foo matchLabelKeys: - pod-template-hash
Примітка:
ПолеmatchLabelKeys
є полем на рівні бета-версії та включено стандартно у 1.27. Ви можете відключити його, вимкнувши функціональну можливістьMatchLabelKeysInPodTopologySpread
.nodeAffinityPolicy вказує, як ми будемо обробляти nodeAffinity/nodeSelector Pod, коли розраховуємо розрив поширення топології Podʼів. Опції:
- Honor: до розрахунків включаються лише вузли, які відповідають nodeAffinity/nodeSelector.
- Ignore: nodeAffinity/nodeSelector ігноруються. Включаються всі вузли.
Якщо це значення є null, поведінка еквівалентна політиці Honor.
Примітка:
ПолеnodeAffinityPolicy
є полем на рівні бета-версії та включено стандартно у 1.26. Ви можете відключити його, вимкнувши функціональну можливістьNodeInclusionPolicyInPodTopologySpread
.nodeTaintsPolicy вказує, як ми будемо обробляти заплямованість вузлів при розрахунку розриву поширення топології Podʼів. Опції:
- Honor: включаються вузли без заплямованості, разом з заплямованими вузлами, для яких вхідний Pod має толерантність.
- Ignore: заплямованість вузла ігноруються. Включаються всі вузли.
Якщо це значення є null, поведінка еквівалентна політиці Ignore.
Примітка:
ПолеnodeTaintsPolicy
є полем на рівні бета-версії та включено стандартно у 1.26. Ви можете відключити його, вимкнувши функціональну можливістьNodeInclusionPolicyInPodTopologySpread
.
Коли Pod визначає більше одного topologySpreadConstraint
, ці обмеження комбінуються за допомогою операції AND: kube-scheduler шукає вузол для вхідного Podʼа, який задовольняє всі налаштовані обмеження.
Мітки вузлів
Обмеження поширення топології ґрунтуються на мітках вузлів для ідентифікації доменів топології, в яких знаходиться кожен вузол. Наприклад, вузол може мати такі мітки:
region: us-east-1
zone: us-east-1a
Примітка:
У цьому прикладі не використовуються відомі ключі міток topology.kubernetes.io/zone
та topology.kubernetes.io/region
. Однак, рекомендується використовувати саме ці зареєстровані ключі міток, а не приватні (непідтверджені) ключі міток region
та zone
, які використовуються тут.
Не можна надійно припускати про наявність значення приватного ключа мітки в різних контекстах.
Припустимо, у вас є кластер з 4 вузлами з наступними мітками:
NAME STATUS ROLES AGE VERSION LABELS
node1 Ready <none> 4m26s v1.16.0 node=node1,zone=zoneA
node2 Ready <none> 3m58s v1.16.0 node=node2,zone=zoneA
node3 Ready <none> 3m17s v1.16.0 node=node3,zone=zoneB
node4 Ready <none> 2m43s v1.16.0 node=node4,zone=zoneB
Тоді кластер логічно виглядає так:
Узгодженість
Вам слід встановити однакові обмеження поширення топології Podʼів для всіх Podʼів у групі.
Зазвичай, якщо ви використовуєте контролер робочого навантаження, такий як Deployment, шаблон Podʼа забезпечує це за вас. Якщо ви комбінуєте різні обмеження поширення, то Kubernetes дотримується визначення API поля; однак, це більш ймовірно призведе до плутанини в поведінці, а усунення несправностей буде менш прямолінійним.
Вам потрібен механізм для забезпечення того, що всі вузли в домені топології (наприклад, регіон хмарного постачальника) мають однакові мітки. Щоб уникнути необхідності ручного маркування вузлів, більшість кластерів автоматично заповнюють відомі мітки, такі як kubernetes.io/hostname
. Перевірте, чи підтримує ваш кластер це.
Приклад обмеження розподілу топології
Приклад: одне обмеження розподілу топології
Припустимо, у вас є кластер із чотирма вузлами, де 3 Podʼа з міткою foo: bar
знаходяться на вузлах node1, node2 та node3 відповідно:
Якщо ви хочете, щоб новий Pod рівномірно розподілявся з наявними Podʼами по зонах, ви можете використовувати маніфест Podʼів, схожий на такий:
kind: Pod
apiVersion: v1
metadata:
name: mypod
labels:
foo: bar
spec:
topologySpreadConstraints:
- maxSkew: 1
topologyKey: zone
whenUnsatisfiable: DoNotSchedule
labelSelector:
matchLabels:
foo: bar
containers:
- name: pause
image: registry.k8s.io/pause:3.1
У цьому маніфесті topologyKey: zone
означає, що рівномірне поширення буде застосовуватися лише до вузлів, які мають мітку zone: <будь-яке значення>
(вузли, які не мають мітки zone
, будуть пропущені). Поле whenUnsatisfiable: DoNotSchedule
повідомляє планувальнику, що потрібно залишити новий Pod у стані очікування, якщо планувальник не може знайти спосіб задовольнити обмеження.
Якщо планувальник розмістить цей новий Pod у зоні A
, розподіл Podʼів стане [3, 1]
. Це означає, що фактичне відхилення складає 2 (розраховане як 3 - 1
), що порушує maxSkew: 1
. Щоб задовольнити умови обмеження та контекст для цього прикладу, новий Pod може бути розміщений лише на вузлі в зоні B
.
або
Ви можете змінити специфікацію Podʼа, щоб вона відповідала різним вимогам:
- Змініть
maxSkew
на більше значення, наприклад2
, щоб новий Pod також можна було розмістити в зоніA
. - Змініть
topologyKey
наnode
, щоб рівномірно розподілити Podʼи по вузлах, а не зонам. У вищезазначеному прикладі, якщоmaxSkew
залишиться1
, новий Pod може бути розміщений лише на вузліnode4
. - Змініть
whenUnsatisfiable: DoNotSchedule
наwhenUnsatisfiable: ScheduleAnyway
, щоб гарантувати, що новий Pod завжди можна розмістити (якщо інші API планування задовольняються). Однак перевага надається розміщенню в області топології, яка має менше відповідних Podʼів. (Памʼятайте, що ця перевага спільно нормалізується з іншими внутрішніми пріоритетами планування, такими як відношення використання ресурсів).
Приклад: декілька обмежень поширення топології
Цей приклад будується на попередньому. Припустимо, у вашому кластері з 4 вузлами є 3 Podʼа, позначених як foo: bar
, що знаходяться на вузлі node1, node2 і node3 відповідно:
Ви можете поєднати два обмеження поширення топології, щоб контролювати розподіл Podʼів як за вузлами, так і за зонами:
kind: Pod
apiVersion: v1
metadata:
name: mypod
labels:
foo: bar
spec:
topologySpreadConstraints:
- maxSkew: 1
topologyKey: zone
whenUnsatisfiable: DoNotSchedule
labelSelector:
matchLabels:
foo: bar
- maxSkew: 1
topologyKey: node
whenUnsatisfiable: DoNotSchedule
labelSelector:
matchLabels:
foo: bar
containers:
- name: pause
image: registry.k8s.io/pause:3.1
У цьому випадку для збігу з першим обмеженням новий Pod може бути розміщений лише на вузлах у зоні B
; тоді як для відповідності другому обмеженню новий Pod може бути розміщений лише на вузлі node4
. Планувальник розглядає лише варіанти, які задовольняють всі визначені обмеження, тому єдине допустиме розташування — це на вузлі node4
.
Приклад: конфліктуючі обмеження розподілу топології
Кілька обмежень може призвести до конфліктів. Припустимо, у вас є кластер з 3 вузлами у 2 зонах:
Якщо ви застосуєте two-constraints.yaml
(файл маніфесту з попереднього прикладу) до цього кластера, ви побачите, що Pod mypod
залишається у стані Pending
. Це трапляється тому, що для задоволення першого обмеження Pod mypod
може бути розміщений лише у зоні B
; тоді як для відповідності другому обмеженню Pod mypod
може бути розміщений лише на вузлі node2
. Перетин двох обмежень повертає порожній набір, і планувальник не може розмістити Pod.
Щоб подолати цю ситуацію, ви можете або збільшити значення maxSkew
, або змінити одне з обмежень, щоб використовувати whenUnsatisfiable: ScheduleAnyway
. Залежно від обставин, ви також можете вирішити видалити наявний Pod вручну — наприклад, якщо ви розвʼязуєте проблему, чому розгортання виправлення помилки не виконується.
Взаємодія з селектором вузла та спорідненістю вузла
Планувальник пропустить вузли, що не відповідають, з обчислень нерівності, якщо у вхідного Podʼа визначено spec.nodeSelector
або spec.affinity.nodeAffinity
.
Приклад: обмеження поширення топології зі спорідненістю вузла
Припустимо, у вас є 5-вузловий кластер, розташований у зонах A до C:
і ви знаєте, що зону C
потрібно виключити. У цьому випадку ви можете скласти маніфест, як наведено нижче, щоб Pod mypod
був розміщений у зоні B
, а не у зоні C
. Так само Kubernetes також враховує spec.nodeSelector
.
kind: Pod
apiVersion: v1
metadata:
name: mypod
labels:
foo: bar
spec:
topologySpreadConstraints:
- maxSkew: 1
topologyKey: zone
whenUnsatisfiable: DoNotSchedule
labelSelector:
matchLabels:
foo: bar
affinity:
nodeAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
nodeSelectorTerms:
- matchExpressions:
- key: zone
operator: NotIn
values:
- zoneC
containers:
- name: pause
image: registry.k8s.io/pause:3.1
Неявні домовленості
Тут є кілька неявних домовленостей, на які варто звернути увагу:
Відповідними кандидатами можуть бути лише ті Podʼи, що мають той самий простір імен, що й вхідний Pod.
Планувальник розглядає лише ті вузли, у яких одночасно присутні всі
topologySpreadConstraints[*].topologyKey
. Вузли, у яких відсутній будь-який з цихtopologyKey
, обминаються. Це означає, що:- будь-які Podʼи, що розташовані на цих обхідних вузлах, не впливають на обчислення
maxSkew
— в прикладі вище, припустимо, що вузолnode1
не має мітки "zone", тоді 2 Podʼи будуть проігноровані, тому вхідний Pod буде заплановано в зонуA
. - вхідний Pod не має шансів бути запланованим на такі вузли — у вищенаведеному прикладі, припустимо, що вузол
node5
має невірно введену міткуzone-typo: zoneC
(і не має жодної встановленої міткиzone
). Після приєднання вузлаnode5
до кластера, він буде обходитися, і Podʼи для цього робочого навантаження не будуть плануватися туди.
- будь-які Podʼи, що розташовані на цих обхідних вузлах, не впливають на обчислення
Будьте уважні, якщо
topologySpreadConstraints[*].labelSelector
вхідного Podʼа не відповідає його власним міткам. У вищенаведеному прикладі, якщо ви видалите мітки вхідного Podʼа, він все ще може бути розміщений на вузлах у зоніB
, оскільки обмеження все ще виконуються. Проте, після цього розміщення ступінь незбалансованості кластера залишається без змін — зонаA
все ще має 2 Podʼи з міткамиfoo: bar
, а зонаB
має 1 Pod з міткоюfoo: bar
. Якщо це не те, що ви очікуєте, оновітьtopologySpreadConstraints[*].labelSelector
робочого навантаження, щоб відповідати міткам в шаблоні Podʼа.
Типові обмеження на рівні кластера
Можливо встановити типові обмеження поширення топології для кластера. Типово обмеження поширення топології застосовуються до Podʼа лише в тому випадку, якщо:
- Він не визначає жодних обмежень у своєму
.spec.topologySpreadConstraints
. - Він належить до Service, ReplicaSet, StatefulSet або ReplicationController.
Типові обмеження можна встановити як частину аргументів втулка PodTopologySpread
в профілі планувальника. Обмеження вказуються з тими ж API вище, за винятком того, що labelSelector
повинен бути пустим. Селектори обчислюються з Service, ReplicaSet, StatefulSet або ReplicationControllers, до яких належить Pod.
Приклад конфігурації може виглядати наступним чином:
apiVersion: kubescheduler.config.k8s.io/v1beta3
kind: KubeSchedulerConfiguration
profiles:
- schedulerName: default-scheduler
pluginConfig:
- name: PodTopologySpread
args:
defaultConstraints:
- maxSkew: 1
topologyKey: topology.kubernetes.io/zone
whenUnsatisfiable: ScheduleAnyway
defaultingType: List
Вбудовані типові обмеження
Kubernetes v1.24 [stable]
Якщо ви не налаштовуєте жодних типових обмежень для поширення топології Podʼа на рівні кластера, то kube-scheduler діє так, ніби ви вказали наступні обмеження:
defaultConstraints:
- maxSkew: 3
topologyKey: "kubernetes.io/hostname"
whenUnsatisfiable: ScheduleAnyway
- maxSkew: 5
topologyKey: "topology.kubernetes.io/zone"
whenUnsatisfiable: ScheduleAnyway
Також, типово відключений застарілий втулок SelectorSpread
, який забезпечує еквівалентну поведінку.
Примітка:
Втулок PodTopologySpread
не оцінює вузли, які не мають вказаних ключів топології в обмеженнях поширення. Це може призвести до іншої типової поведінки порівняно з застарілим втулком SelectorSpread
, коли використовуються типові обмеження поширення топології.
Якщо ви не очікуєте, що ваші вузли матимуть обидві мітки kubernetes.io/hostname
та topology.kubernetes.io/zone
встановлені, визначте свої власні обмеження замість використання стандартних значень Kubernetes.
Якщо ви не хочете використовувати типові обмеження поширення топології Podʼа для вашого кластера, ви можете відключити ці типові значення, встановивши defaultingType
у List
і залишивши порожніми defaultConstraints
у конфігурації втулка PodTopologySpread
:
apiVersion: kubescheduler.config.k8s.io/v1beta3
kind: KubeSchedulerConfiguration
profiles:
- schedulerName: default-scheduler
pluginConfig:
- name: PodTopologySpread
args:
defaultConstraints: []
defaultingType: List
Порівняння з podAffinity та podAntiAffinity
У Kubernetes, між-Podʼова (анти)спорідненість контролює те, як Podʼи розміщуються відносно один одного — чи ущільнені, чи розріджені.
podAffinity
- притягує Podʼи; ви можете намагатися упакувати будь-яку кількість Podʼів в кваліфікуючі топологічні домени.
podAntiAffinity
- відштовхує Podʼи. Якщо ви встановите це у режим
requiredDuringSchedulingIgnoredDuringExecution
, тоді тільки один Pod може бути запланований в один топологічний домен; якщо ви виберетеpreferredDuringSchedulingIgnoredDuringExecution
, то ви втратите можливість змусити виконання обмеження.
Для більш точного контролю ви можете вказати обмеження поширення топології для розподілу podʼів по різним топологічним доменам — для досягнення як високої доступності, так і економії коштів. Це також може допомогти в роботі з оновленнями без відмов та плавному масштабуванні реплік.
Для отримання більш детальної інформації, див. розділ Motivation пропозиції щодо покращення про обмеження поширення топології Podʼів.
Відомі обмеження
Немає гарантії, що обмеження залишаться задоволеними при видаленні Podʼів. Наприклад, зменшення масштабування Deployment може призвести до нерівномірного розподілу Podʼів.
Ви можете використовувати інструменти, такі як Descheduler, для перебалансування розподілу Podʼів.
Podʼи, що відповідають заплямованим вузлам, враховуються. Див. Issue 80921.
Планувальник не має попереднього знання всіх зон або інших топологічних доменів, які має кластер. Вони визначаються на основі наявних вузлів у кластері. Це може призвести до проблем у автоматизованих кластерах, коли вузол (або група вузлів) масштабується до нуля вузлів, і ви очікуєте масштабування кластера, оскільки в цьому випадку ці топологічні домени не будуть враховуватися, поки в них є хоча б один вузол.
Ви можете обійти це, використовуючи інструменти автоматичного масштабування кластера, які враховують обмеження розподілу топології Podʼів та також знають загальний набір топологічних доменів.
Що далі
- У статті блогу Introducing PodTopologySpread докладно пояснюється
maxSkew
, а також розглядаються деякі приклади використання. - Прочитайте розділ scheduling з довідки API для Pod.