Kubernetes: планирование и ресурсы
Как планировщик размещает Pod, requests и limits, классы QoS и вытеснение, health-пробы, affinity и распределение по топологии.
9 вопросов
JuniorТеорияОчень частоВ чём разница между requests и limits ресурсов у Pod?
В чём разница между requests и limits ресурсов у Pod?
requests — это то, что резервирует планировщик: Pod встаёт только туда, где allocatable ещё вмещает сумму requests. limits ограничивают потребление в рантайме: сверх CPU-лимита контейнер троттлится, сверх лимита памяти его OOMKill'ит. Pod без requests встанет куда угодно и обделит соседей.
Типичные ошибки
- ✗Менять роли местами — считать, что планирование ведут
limits, а неrequests - ✗Ждать, что CPU-лимит вызовет OOMKill, а не троттлинг контейнера
- ✗Не задавать
requests, позволяя Pod встать где угодно и обделить других
Уточняющие вопросы
- →Какой класс QoS получит Pod, где requests равны limits?
- →Почему завышенные
limitsпри заниженныхrequestsведут к OOM узла?
JuniorТеорияЧастоЧто делает планировщик Kubernetes и что решает, на какой узел попадёт Pod?
Что делает планировщик Kubernetes и что решает, на какой узел попадёт Pod?
Планировщик назначает незапланированному Pod узел в две фазы. Фильтрация оставляет только узлы, которые вмещают requests Pod и проходят его taint, nodeSelector и affinity; скоринг ранжирует оставшиеся и выбирает лучший. Если ни один узел не прошёл фильтрацию, Pod остаётся в Pending.
Типичные ошибки
- ✗Думать, что планировщик запускает Pod, а не только выбирает ему узел
- ✗Считать, что фильтрация проверяет
limits, а неrequests - ✗Полагать, что Pod всегда размещается, забывая про
Pending
Уточняющие вопросы
- →Как размещаются Pod из DaemonSet в отличие от планируемых обычно?
- →Какие состояния узла заставляют фильтрацию отклонить даже пустой узел?
MiddleДебаггингЧастоPod завис в Pending — прочитайте события планировщика и поставьте диагноз.
Pod завис в Pending — прочитайте события планировщика и поставьте диагноз.
Узла нет по двум причинам. 2 обычных узла отсеяны по Insufficient cpu — Pod просит 4 CPU. 3 GPU-узла закрыты нетерпимым taint dedicated=gpu; preemption не поможет — не даст CPU и не снимет taint. Решение: уменьшить request или добавить узлы; для GPU-узлов — toleration и nodeAffinity.
Типичные ошибки
- ✗Винить только CPU, игнорируя половину сообщения про untolerated taint
- ✗Ждать, что preemption разместит Pod, который физически не влезает
- ✗Думать, что один toleration заставит Pod предпочесть GPU-узлы
Уточняющие вопросы
- →Какое одно изменение позволит этому Pod встать на обычный узел?
- →Почему для GPU-узлов здесь стоит
Preemption not helpful?
MiddleТеорияЧастоКак выводятся классы QoS Guaranteed, Burstable и BestEffort и как они влияют на вытеснение?
Как выводятся классы QoS Guaranteed, Burstable и BestEffort и как они влияют на вытеснение?
QoS выводится из requests и limits: Guaranteed — requests равны limits, Burstable — хотя бы один request задан, но не равен, BestEffort — не задано ничего. При нехватке памяти на узле kubelet вытесняет сначала BestEffort, затем Burstable сверх requests, а Guaranteed последним.
Типичные ошибки
- ✗Думать, что QoS берётся из priority, а не из requests и limits
- ✗Переворачивать порядок — вытеснять Guaranteed раньше BestEffort
- ✗Считать
BestEffortбезопасным, ведь у него нет лимита для превышения
Уточняющие вопросы
- →Почему
BurstablePod под своими requests переживает нехватку памяти? - →Чем вытеснение по QoS отличается от вытеснения по priority (preemption)?
MiddleТеорияЧастоПритягивают ли taint и toleration Pod к узлу? Чем это отличается от nodeAffinity?
Притягивают ли taint и toleration Pod к узлу? Чем это отличается от nodeAffinity?
Нет — taint ОТТАЛКИВАЕТ. Taint не пускает Pod на узел, если у Pod нет подходящего toleration, а toleration лишь СНИМАЕТ этот барьер — он не притягивает Pod к узлу. Притягивают как раз nodeAffinity и nodeSelector. Поэтому чтобы посадить Pod на выделенный узел с taint, нужны И toleration, И affinity.
Типичные ошибки
- ✗Считать, что toleration притягивает Pod, а не просто разрешает его
- ✗Пропускать nodeAffinity, ожидая, что один toleration закрепит Pod
- ✗Считать taint и affinity одним механизмом, а не ортогональными
Уточняющие вопросы
- →Как выделить пул GPU-узлов только под GPU-нагрузки?
- →Что даёт
podAntiAffinity, чего не выразить через taint узла?
MiddleТеорияИногдаЧто делают topologySpreadConstraints и как maxSkew и topologyKey управляют распределением?
Что делают topologySpreadConstraints и как maxSkew и topologyKey управляют распределением?
Они равномерно распределяют подходящие Pod по доменам топологии из topologyKey — зонам или узлам. maxSkew ограничивает, насколько неравномерными станут счётчики по доменам; whenUnsatisfiable задаёт, останется ли нарушающий Pod в Pending (DoNotSchedule) или встанет всё равно (ScheduleAnyway).
Типичные ошибки
- ✗Понимать
maxSkewкак лимит Pod на узел, а не как допустимый перекос - ✗Считать равномерное распределение автоматическим без constraint
- ✗Путать смысл
DoNotScheduleиScheduleAnyway
Уточняющие вопросы
- →Чем этот
topologyKeyотличается от topologyKey вpodAntiAffinity? - →Когда выбрать
ScheduleAnywayвместоDoNotSchedule?
SeniorТеорияИногдаКак работают PriorityClass и preemption и как PodDisruptionBudget ограничивают вытеснение?
Как работают PriorityClass и preemption и как PodDisruptionBudget ограничивают вытеснение?
PriorityClass задаёт Pod приоритет. Когда высокоприоритетный Pod не может встать, планировщик выполняет preemption — вытесняет менее приоритетные Pod, освобождая их requests, чтобы он влез. PodDisruptionBudget ограничивает только ДОБРОВОЛЬНОЕ нарушение (drain, выкат), но не preemption и не отказ узла.
Типичные ошибки
- ✗Считать, что PDB блокирует preemption, а не только добровольное нарушение
- ✗Думать, что preemption выбирает жертв по QoS, а не по приоритету
- ✗Читать
PriorityClassкак резерв ресурсов, а не как ранг
Уточняющие вопросы
- →Может ли preemption каскадировать и как применяется срок мягкого завершения?
- →Что будет, если соблюдение PDB блокирует нужный drain узла?
SeniorКодИногдаДопишите Deployment так, чтобы его Pod шли только в выделенный пул GPU-узлов.
Допишите Deployment так, чтобы его Pod шли только в выделенный пул GPU-узлов.
Добавьте И то, И другое: toleration под taint, чтобы GPU-узлы перестали отталкивать Pod, и required nodeAffinity по accelerator=gpu, чтобы его притягивало только туда. Один toleration лишь разрешает GPU-узлы; без affinity Pod всё равно может встать на любой узел без taint.
Типичные ошибки
- ✗Добавить только toleration и ждать, что он притянет Pod
- ✗Добавить только nodeAffinity и упереться в нетерпимый taint
NoSchedule - ✗Считать, что affinity перекрывает taint и toleration не нужен
Уточняющие вопросы
- →Как одновременно не пускать обычные Pod на GPU-узлы?
- →Когда здесь лучше
preferredDuringSchedulingaffinity?
SeniorДизайнИногдаУ вас sharded-кэш из 6 реплик (stateful) плюс чувствительный к задержке API, который активно к нему обращается, в одном кластере на 3 зоны доступности. Требования: реплики кэша разнести так, чтобы потеря любой зоны убирала не более трети; Pod API должны стоять на тех же узлах, что и кэш, — или очень близко — ради низкой задержки; а плавающее обновление не должно снимать больше одной реплики кэша разом. Спроектируйте политику планирования — какие из nodeAffinity, podAffinity, podAntiAffinity, topologySpreadConstraints и PodDisruptionBudget вы возьмёте под каждое требование и как зададите ключевые параметры. Объясните компромисс между разносом ради доступности и совмещением ради задержки.
У вас sharded-кэш из 6 реплик (stateful) плюс чувствительный к задержке API, который активно к нему обращается, в одном кластере на 3 зоны доступности. Требования: реплики кэша разнести так, чтобы потеря любой зоны убирала не более трети; Pod API должны стоять на тех же узлах, что и кэш, — или очень близко — ради низкой задержки; а плавающее обновление не должно снимать больше одной реплики кэша разом. Спроектируйте политику планирования — какие из nodeAffinity, podAffinity, podAntiAffinity, topologySpreadConstraints и PodDisruptionBudget вы возьмёте под каждое требование и как зададите ключевые параметры. Объясните компромисс между разносом ради доступности и совмещением ради задержки.
Разнесите кэш через зональный topologySpreadConstraint (maxSkew: 1, DoNotSchedule): в зоне не больше трети. Совместите API через podAffinity по метке кэша: topologyKey hostname или зона. Ограничьте выкат PDB maxUnavailable: 1. Компромисс: разнос повышает доступность, но добавляет хоп.
Типичные ошибки
- ✗Брать
podAntiAffinityдля совмещения — он, наоборот, разносит Pod - ✗Надеяться, что планировщик разнесёт по зонам без constraint
- ✗Ждать, что tolerations совместят API с узлами кэша
Уточняющие вопросы
- →Как
whenUnsatisfiable: ScheduleAnywayизменит гарантию доступности? - →Что сломается, если зона целиком отпадёт во время выката?