Topology Spread Constraints в Kubernetes: надёжность

Обложка статьи об ограничениях распределения топологии в Kubernetes

Если вы похожи на большинство пользователей Kubernetes, то, скорее всего, мало задумываетесь о том, где и как Kubernetes размещает ваши поды. Пока они работают — какая разница, куда их задеплоили? Наверняка Kubernetes сам использует какой-то сложный алгоритм, чтобы максимально надёжно распределить поды по кластеру… не так ли?

На самом деле распределение подов влияет на надёжность куда сильнее, чем кажется. Хорошая новость: управлять тем, когда, куда и как Kubernetes размещает поды, совсем несложно. Достаточно добавить несколько строк в манифест — и ваши развёртывания станут отказоустойчивыми по зонам и равномерно масштабируемыми. Эта возможность называется ограничениями распределения топологии (topology spread constraints), и в данной статье мы разберём её во всех подробностях.

Почему ограничения распределения топологии важны для надёжности?

Ограничения распределения топологии определяют, как Kubernetes распределяет поды между доменами отказов (failure domains): регионами, зонами и узлами. Это позволяет гарантировать, что ваши рабочие нагрузки действительно распределены не просто по кластеру, а по всей операционной среде. Ограничения можно задать на уровне кластера — как значения по умолчанию — или для каждой рабочей нагрузки отдельно.

Как настроить ограничения распределения топологии

Ограничения распределения топологии задаются через поле spec.topologySpreadConstraints. Их можно применять к отдельному поду или к кластеру в целом. Ограничение включает следующие поля:

maxSkew

Допустимая степень неравномерного распределения подов. Поведение зависит от значения whenUnsatisfiable:

  • Если whenUnsatisfiable: DoNotSchedule, параметр определяет максимально допустимую разницу между минимальным числом подов в домене и числом совпадающих подов в целевой топологии. Иными словами, насколько домен может отклониться от минимума.

  • Если whenUnsatisfiable: ScheduleAnyway, Kubernetes отдаёт предпочтение топологиям, которые помогают уменьшить перекос.

minDomains

Минимальное число подходящих доменов (например, зон доступности или регионов).

topologyKey

Метка узла, по которой определяются узлы для данного ограничения. Все узлы с этой меткой группируются в домены топологии в соответствии со значениями метки. Например, использование topology.kubernetes.io/zone в качестве ключа создаёт домены на основе зон доступности, которые охватывают ваши хосты.

whenUnsatisfiable

Поведение при невозможности удовлетворить ограничение распределения. По умолчанию под не будет запланирован (DoNotSchedule). Если задать ScheduleAnyway, под будет запланирован в любом случае с приоритетом для узлов, минимизирующих maxSkew.

labelSelector

Метка пода, по которой отбираются совпадающие поды.

matchLabelKeys

Список ключей меток пода для вычисления перекоса распределения.

nodeAffinityPolicy

Определяет, как учитываются настройки nodeAffinity и nodeSelector каждого пода. Значение Honor (по умолчанию) ограничивает расчёт топологии только этими узлами, Ignore — использует все узлы.

nodeTaintsPolicy

Определяет, учитывать ли метки-отталкивания (taints) узлов при расчёте топологии.

Обратите внимание: для одной пары topologyKey и whenUnsatisfiable можно задать только одно topologySpreadConstraint.

Как добавить ограничение распределения топологии в манифест Kubernetes

Представим, что у нас есть кластер Kubernetes, распределённый по трём зонам доступности: us-east-1a, us-east-1b и us-west-2a. Также есть под, который нужно задеплоить и реплицировать для обеспечения избыточности. Начнём со следующего манифеста:

Если задеплоить четыре реплики пода по алгоритму round-robin, получится один узел с двумя подами и два узла с одним подом:

Равномерное распределение подов по зонам доступности

Однако планировщик Kubernetes может разместить два пода на двух узлах, оставив один пустым; или задеплоить три пода в us-east-1a и один в us-east-1b — и тогда при отказе региона us-east мы окажемся в опасной ситуации. В худшем случае все четыре пода могут оказаться на одном узле, что создаст единую точку отказа.

Неравномерное распределение подов, создающее единую точку отказа
  • Сначала ограничим дисбаланс подов, задав maxSkew равным 1. Это гарантирует, что ни один узел не будет иметь более чем на одну реплику пода больше, чем любой другой узел.

  • Затем установим topologyKey в значение topology.kubernetes.io/zone, поскольку мы хотим ограничить распределение по зонам, даже если зоны охватывают несколько регионов.

  • Мы хотим, чтобы Kubernetes запускал под, даже если не может выполнить ограничения топологии, поэтому установим whenUnsatisfiable в значение ScheduleAnyway.

  • Нам нужно отобрать все поды Nginx (по крайней мере в данном развёртывании), поэтому добавим labelSelector с условием app: nginx.

После всех изменений манифест выглядит так:

Как найти поды с отсутствующими ограничениями распределения топологии

Можно воспользоваться утилитой командной строки kubectl, чтобы получить список подов, а затем с помощью jq отфильтровать те, у которых не задан topologySpreadConstraints. Например:

Также можно использовать встроенную функцию Detected Risks в Gremlin, которая автоматически сканирует поды Kubernetes на предмет отсутствующих ограничений распределения топологии.

После добавления ограничений повторно запустите команду, чтобы убедиться, что поды больше не появляются в выводе. Если вы используете Gremlin, статус риска «отсутствие ограничений распределения топологии» автоматически изменится с «под угрозой» на «устранено», а показатель надёжности сервиса вырастет.

Совместное использование ограничений топологии, правил привязки к узлам и меток-отталкиваний с допусками

Как мы уже видели, ограничения распределения топологии могут взаимодействовать с другими возможностями Kubernetes — в частности, с правилами привязки к узлам (node affinity rules) и метками-отталкиваниями и допусками (taints and tolerations). Между ними есть тонкие различия.

Правила привязки задают конкретные критерии для планирования пода на узел. Например, под, запускающий большую языковую модель (LLM), может иметь правило привязки, требующее узла с GPU. Таким образом, комбинируя правила привязки с ограничениями топологии, можно сузить круг узлов, доступных для пода. Главное — убедиться, что задано nodeAffinityPolicy: Honor (это значение по умолчанию).

Метки-отталкивания, напротив, указывают, куда не нужно планировать под — если только у него нет соответствующего допуска (toleration). Если GPU на узле выходит из строя из-за проблемы с драйвером, Kubernetes не должен планировать LLM на этот узел. Можно применить taint, который запрещает Kubernetes размещать поды на данном узле и одновременно инициирует миграцию уже запущенных подов на другие узлы. Как и правила привязки, метки-отталкивания работают совместно с ограничениями топологии, сужая размер домена, — при условии что задано nodeTaintsPolicy: Honor.

Другие риски Kubernetes, на которые стоит обратить внимание

Ограничения распределения топологии — лишь один элемент надёжного развёртывания в Kubernetes. Если вы хотите узнать, как защититься от других рисков — отсутствующих проб живучести (liveness probes), незаданных запросов ресурсов, неправильно настроенных кластеров высокой доступности, — ознакомьтесь с нашей подробной электронной книгой «Kubernetes Reliability at Scale».

А если вы хотите уже сейчас за несколько минут получить бесплатный отчёт о рисках надёжности, вы можете зарегистрироваться на бесплатную 30-дневную пробную версию Gremlin или воспользоваться функцией Detected Risks в Gremlin для автоматического сканирования ваших подов Kubernetes на предмет отсутствующих ограничений распределения топологии.

K8s Reliability at Scale

Чтобы узнать больше о режимах отказов Kubernetes и о том, как предотвращать их в масштабе, скачайте нашу подробную электронную книгу.

Автор статьи Андре Ньюман

Как устранять неустанавливаемые поды в Kubernetes

Есть под Kubernetes, который никак не запускается? Вы не одиноки: неустанавливаемые поды (unschedulable Pods) — распространённая проблема. Узнайте, как вернуть зависшие сервисы к жизни, из этой статьи.

Руководство по устранению неустанавливаемых подов в Kubernetes
Автор Андре Ньюман

Kubernetes создан для масштабирования, и с управляемыми сервисами Kubernetes вы можете задеплоить под, не задумываясь об…

/blog/how-to-fix-kubernetes-unschedulable-pods[Читать далее]

Как обеспечить единообразие версий контейнеров в Kubernetes

Узнайте о сложностях поддержания единообразия версий в развёртываниях контейнеров Kubernetes и о рисках одновременного запуска нескольких версий. Разберитесь, как обеспечить согласованность версий контейнеров с помощью тегов, дайджестов, проверки файлов манифестов и альтернативных стратегий развёртывания.

Руководство по обеспечению единообразия версий контейнеров в Kubernetes
Автор Андре Ньюман

Одна из ключевых возможностей Kubernetes — бесшовное обновление приложений независимо от масштаба развёртывания. Разработчик внёс изменение в код, и теперь нужно обновить тысячу запущенных контейнеров? Просто выполните kubectl apply -f manifest.yaml и наблюдайте, как Kubernetes заменяет каждый устаревший под новой версией.

/blog/kubernetes-container-image-version-uniformity[Читать далее]

Управление медленным запуском контейнеров с помощью проб готовности Kubernetes

Поды без проб готовности (readiness probes) — как инженеры без кофе. Узнайте, как работают пробы готовности, почему они важны и как их правильно настроить.

Руководство по управлению медленным запуском контейнеров с пробами готовности Kubernetes
Автор Андре Ньюман

Поды без проб готовности — как инженеры без кофе. Узнайте, как работают пробы готовности, почему они важны и как их правильно настроить.

/blog/managing-slow-container-starts-kubernetes-readiness-probes[Читать далее]

© 2026 meganuke