Планирование подов для узлов Kubernetes: развёрнутое руководство

Kate

Administrator
Команда форума
В статье — варианты использования расширенного планирования подов в Kubernetes, а также передовые методы его применения на практике. Информация пригодится разработчикам приложений и администраторам K8s, чтобы реализовать расширенные шаблоны развёртывания приложений, в которых есть локальность данных, совместное размещение модулей, высокая доступность и эффективное использование ресурсов кластеров K8s.

В Kubernetes задача планирования подов для определённых узлов в кластере выполняется с помощью компонента kube-scheduler. По умолчанию он фильтрует узлы на основе запросов ресурсов и ограничений каждого контейнера в созданном модуле. Затем возможные узлы оцениваются для поиска лучшей возможности для размещения пода.

Во многих сценариях планирование подов на основе ограничений ресурсов — лучший из возможных вариантов. Однако иногда администраторы Kubernetes планируют поды для опредёленных узлов в соответствии с другими вариантами ограничений. В таких случаях стоит рассматривать возможность расширенного планирования.

Сценарии использования для ручного планирования доставки от пода к узлу​

В производственном применении Kubernetes всегда необходима настройка распределения подов по узлам. Перечислим некоторые распространённые сценарии, при которых расширенное планирование — оптимальный вариант.

  • Запуск подов на узлах с выделенным оборудованием. У некоторых приложений Kubernetes особые требования к оборудованию. Поды, выполняющие задания по машинному обучению, требуют высокопроизводительных графических процессоров вместо ЦП, в то время как модули Elasticsearch более эффективны на твердотельных накопителях, чем на жёстких дисках. Лучшая практика для любого управления кластером K8s с учётом ресурсов — это назначать поды узлам с правильным оборудованием.
  • Совместное размещение подов и кодовая зависимость. В настройке микросервисов или тесно связанном стеке приложений определённые поды должны быть размещены на одном компьютере, чтобы повысить производительность, избежать проблем с задержкой в сети и сбоев подключения. Рекомендуется запускать веб-сервер на том же компьютере, что и службу кэширования в памяти или базу данных.
  • Локальность данных. Требования к локальности данных для приложений, интенсивно использующих данные, аналогичны предыдущему варианту использования. Для более быстрого чтения и лучшей пропускной способности записи этим приложениям требуется развёртывание баз данных на одном компьютере с клиентским приложением.

Ресурсы Kubernetes для расширенного планирования подов​

Kubernetes располагает множеством ресурсов и стратегий API. Рассмотрим некоторые из них: концепции nodeSelector, сродства узлов и сродства между модулями — и покажем наглядно, как реализовать их в вашем кластере K8s.

Ручное планирование подов с помощью nodeSelector​

В более ранних версиях K8s пользователи могли планировать поды вручную, используя поле nodeSelector в файле PodSpec. NodeSelector — метод планирования от пода к узлу на основе меток, при котором пользователи назначают определённые метки узлам и следят за тем, чтобы поле nodeSelector соответствовало этим меткам.

Допустим, что одна из меток узла — «storage = ssd», указывающая тип хранилища на узле.

kubectl describe node “host01”
Name: host01
Roles: node
Labels: storage=ssd,
Для планирования подов на узле с этой меткой указываем поле nodeSelector с ней в манифесте для пода.

apiVersion: v1
kind: Pod
metadata:
name: pod
labels:
env: dev
spec:
containers:
- name: your-container
image: your-container-image
imagePullPolicy: IfNorPresent
nodeSelector:
storage: ssd
Селекторы узлов — наиболее доступный метод расширенного планирования модулей. Однако они неэффективны в случаях, когда при планировании подов приходится учитывать дополнительные правила и условия.

Сродство узлов​

Функция привязки узлов более эффективна по сравнению с ручным размещением подов. Она включает в себя функциональный язык сродства с использованием логических операторов и ограничений, обеспечивающий точный контроль над размещением подов. Язык поддерживает мягкие и жёсткие правила планирования, позволяющие контролировать строгость ограничений привязки узлов в зависимости от требований пользователя.

Рассмотрим привязку узлов для размещения подов на узлах в определённых зонах доступности.

apiVersion: v1
kind: Pod
metadata:
name: node-affinity
spec:
affinity:
nodeAffinity
requiredDuringSchedulingIgnoreDuringExecution:
nodeSelectorTerms:
- matchExpressions:
- key: kubernetes.io/cp-az-name
operator: In
values:
- cp-1a
- cp-1b
preferredDuringSchedulingIgnoreDuringExecution:
- weight: 7
preference:
matchExpressions:
- key: custom-key
operator: In
values:
- custom-value
containers:
- name: node-affinity
image: your-container-image
Жёсткие правила сродства указываются в поле “required during scheduling ignored during execution”(«Требуется во время планирования, игнорируется во время выполнения») раздела nodeAffinity в манифесте для пода. В примере планировщику было дано задание разместить модуль только на узлах с меткой, имеющей ключ kubernetes.io/cp-az-name и значения cp-1a или cp-1b.

Для этого использован логический оператор In, фильтрующий массив существующих значений меток. Другие возможные в подобных случаях операторы: NotIn, Exists, DoesNotExist, Gt и Lt.

Мягкое правило указывается в поле спецификации «Предпочитается во время планирования, игнорируется во время выполнения» (“preferred during scheduling ignored during execution”). В примере среди узлов, отвечающих жёстким критериям, используются узлы с меткой, имеющей ключ с именем custom-key и значением custom-value. Если таких узлов нет, возможно добавление подов другим кандидатам, если они соответствуют жёстким критериям.

Эффективная практика — создание правил привязки узлов таким образом, чтобы они включали как жёсткие, так и мягкие правила. Следование этому подходу максимальных усилий (использовать по возможности ту или иную опцию, но не отклонять планирование, если опция недоступна) помогает гибко и предсказуемо планировать развёртывание.

Межподовое сродство​

Сродство в Kubernetes — это функция, служащая для планирования подов на основе их отношения к другим подам. Сродство позволяет использовать различные интересные варианты, такие как совместное размещение подов в рамках созависимой службы (служб) или реализация локальности данных, при которой поды данных работают на том же компьютере, что и основной под службы.

Сродство между пакетами определяется аналогично сродству узлов. В данном случае мы будем использовать поле podAffinity внутри спецификации.

apiVersion: v1
kind: Pod
metadata:
name: example-pod-affinity
spec:
affinity:
nodeAffinity
requiredDuringSchedulingIgnoreDuringExecution:
- labelSelector:
matchExpressions:
- key: security
operator: In
values:
- S1
topologyKey: failure-domain.beta.kubernetes.io/zone
containers:
- name: node-affinity
image: your-container-image
Как и сродство узла, сродство пода определяется с помощью выражений соответствия и логических операторов. Но в этом случае они применяются к селектору меток подов, работающих на определённом узле. Если указанное выражение совпадает с меткой целевого пода, новый под размещается вместе с целевым на том же компьютере.

Анти-сродство​

Иногда предпочтительнее использовать метод чёрного списка для планирования пакетов. Такой подход препятствует планированию подов на определённых узлах при невыполнении опредёленных условий. Такая концепция называется анти-сродством Kubernetes.

В основном анти-сродство от пода к узлу применяется для выделенных узлов. Для управления использованием ресурсов в кластере администраторы K8s могут выделить определённые узлы для опредёленных типов подов и приложений.

Есть и другие интересные варианты использования анти-сродства между подами. Во-первых, это предотвращение единой точки отказа. Это достигается путём распределения подов в рамках одного и того же сервиса по разным машинам, что требует предотвращения совмещения подов с другими подами того же типа. Во-вторых, это предотвращение конкуренции между службами за ресурсы. Для повышения производительности некоторых сервисов их не стоит размещать вместе с другими сервисами, которые потребляют много ресурсов.

Анти-сродство от пода к узлу в Kubernetes может быть достигнуто с помощью порчи и толерантности.

Порча и терпимость​

Ошибки, условия и допуски помогают пользователю контролировать планирование подов для определённых узлов без изменения уже существующих.

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

Реализовать порчи и терпимости довольно просто. Для начала — добавить к узлу искажение, для которого необходимо применить нестандартное поведение при планировании:

kubectl taint nodes host2 storage=ssd:NoSchedule
node “host1” tainted
Формат порчи выглядит так: <taintKey> = <taintValue>: <taintEffect>. Эффект порчи в примере предотвращает включение любого модуля без соответствующих допусков в расписание для этого узла.

Другие поддерживаемые эффекты порчи — NoExecute и PreferNoSchedule (мягкая версия NoSchedule). При использовании эффекта PreferNoSchedule kube-scheduler попытается не размещать под модуль без требуемого допуска на испорченный узел.

Эффект NoExecute вызывает мгновенное удаление всех подов без определённого разрешения узла. Он может быть полезен, если у вас уже есть поды, запущенные на узле, которые вам больше не нужны.

Создание порчи — лишь первая часть настройки. Чтобы поды могли быть запланированы на испорченном узле, нужно добавить терпимость:

apiVersion: v1
metadata:
name: esearch
spec:
containers:
- name: esearch
image: your-es-container
resources:
requests:
cpu: 0.8
memory: 4Gi
limits:
cpu: 3.0
memory: 22Gi
tolerations:
- key: “storage”
operator: “Equal”
value: “ssd”
effect: “NoSchedule”
Терпимость для порчи добавлена с помощью оператора «Равно». Можно использовать оператор Exists, который применяет допуск к любому узлу, соответствующему ключу порчи. При этом точное значение указывать не нужно.

В этом случае используем taint storage = ssd: NoSchedule, чтобы запланировать под, определённый выше, для узла.

Анти-сродство подов​

Также можно отделять поды друг от друга с помощью функции анти-сродства. Одна из лучших практик, доступных Kubernetes — избегание единой точки отказа с распределением подов по разным зонам доступности. Можно настроить аналогичное поведение в анти-сродстве для части спецификации пода. Для анти-сродства нам понадобятся два пода.

Первый под:

apiVersion: v1
kind: Pod
metadata:
name: s1
labels:
security: s1
spec:
containers:
- name: c1
image: first-image
Для первого пода использована метка “security: s1.”

Второй под:

apiVersion: v1
kind: Pod
metadata:
name: s2
spec:
affinity:
podAntiAffinity:
requiredDuringSchedulingIgnoreDuringExecution:
- labelSelector:
matchExpressions:
- key: security
operator: In
values:
- s1
topologyKey: kubernetes.io/hostname
containers:
- name: pod-anti-affinity
image: second-image
Второй под относится к селектору меток security: s1 в spec.affinity.podAntiAffinity. Соответственно, он не будет назначен узлу, на котором уже размещены какие-либо поды с меткой security: s1.

Заключение​

Расширенное планирование подов позволяет использовать множество интересных вариантов и продвинутых практик для развёртывания сложных приложений и микросервисов в Kubernetes. С помощью привязки подов реализуется их размещение и локализация данных для тесно связанных стеков приложений и микросервисов.

В таблице — ключевая информация для каждого типа ресурсов.

Обзор возможностей Kubernetes для расширенного планирования подов

Тип ресурсаКогда используетсяДостоинстваНедостаткиОптимальные сферы применения
nodeSelectorНазначение подов узлам с определёнными меткамиПростота использова-ния, незначитель-ные изменения в PodSpecОтсутствие поддержки логических операторов, ограниченные возможности расширения с помощью сложных правил планированияТолько в ранних версиях K8s, не поддержива-ющих сродство узлов
Сродство узловРеализация локальности данных, запуск модулей на узлах с помощью специального программного обеспечения.Функциональ-ный синтаксис с поддержкой логических операторов, детальный контроль над правилами размещения подов, поддержка жёстких и мягких правил размещения подовНеобходи-мость модификации существующих подов для изменения поведенияСочетание жёстких и мягких правил для охвата различных вариантов использова-ния и сценариев
Сродство подовРазмещение модулей в зависимой службе, обеспечива-ющей локальность данныхАналогично сродству узловНеобходи-мость модификации существующих подов для изменения поведенияПравильное управление метками в подах и документи-рование используемых меток
Анти-сродство подовОбеспечение высокой доступности через распростране-ние пакетов, предотвраще-ние конкуренции между сервисами за ресурсыРазвёрнутый контроль над отторжением подов, поддержка жёстких и мягких правил анти-сродства подовНеобходи-мость модификации существующих подов для изменения поведенияАналогично сродству узлов
Порча и терпимостьУзлы с выделенным программным обеспечением, разделение ресурсов командыОтсутствие необходи-мости модификации существующих подов, поддержка автоматического удаления подов без требуемой терпимости, поддержка различных эффектов порчиОтсутствие поддержки синтаксиса с логическими операторамиНеобходимо быть осторожными при нанесении нескольких порч на узел. Важно убедиться, что нужные поды имеют требуемые уровни терпимости
Используя анти-сродство узлов и порчу, можно запускать узлы с оборудованием, выделенным для определённых приложений и сервисов, обеспечивая эффективное использование ресурсов в кластере. При помощи анти-сродства пода и анти-сродства узла возможна высокая доступность приложений с избежанием единой точки отказа: в таком случае разные компоненты будут работать на разных узлах.

Сродство и анти-сродство — две мощные концепции в Kubernetes, которые необходимо освоить каждому администратору кластера.


 
Сверху