В статье — варианты использования расширенного планирования подов в 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, которые необходимо освоить каждому администратору кластера. 

Комментарии (3)


  1. Ar0x13
    30.09.2021 19:13
    +2

    сродство? может родственность?


  1. a-postx
    01.10.2021 07:03
    +2

    Спасибо за отличный ввод отраслевых терминов в русский язык


  1. AlexGluck
    01.10.2021 18:09

    Чтение вызвало хруст в голове, будто зубья шестерёнок поломаны. +1 за родственность, +10 за афинити.