Как вы уже знаете, в своей домашней лаборатории я использую кластер Kubernetes, развернутый на Talos Linux. Периодически у меня отключают электроэнергию, ИБП не спасают при длительном отключении. Я настроил правила, которые завершают работу всех нод Proxmox при отключении более 6 минут. После восстановления узлы кластера запускаются, но запускаются в разное время, что приводит к неравномерному распределению нагрузки: некоторые узлы перегружены, в то время как другие простаивают.

Scheduling в Kubernetes — это процесс назначения ожидающих подов к узлам, выполняемый компонентом под названием kube-scheduler. Решения планировщика о том, можно ли и где разместить под, определяются его настраиваемой политикой, состоящей из набора правил, называемых предикатами и приоритетами. Эти решения зависят от состояния кластера Kubernetes в момент появления нового пода для планирования.

Поскольку кластеры Kubernetes очень динамичны и их состояние меняется со временем, может возникнуть необходимость переместить уже работающие поды на другие узлы по различным причинам:

  • Некоторые узлы простаивают или перегружены
  • Первоначальное решение о размещении пода больше не актуально, так как на узлы были добавлены или удалены метки или "taints", и требования affinity пода к узлу больше не выполняются
  • Некоторые узлы вышли из строя, и их поды были перемещены на другие узлы
  • В кластер добавлены новые узлы

В результате в кластере могут оказаться поды, размещенные не там, где хотелось. Такую балансировку Kubernetes сам делать не умеет и мне приходилось вручную балансировать нагрузку. Как вы понимаете — это не наш метод, я хочу, чтобы Kubernetes все делал сам.

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

Установка Descheduler с помощью Helm

Для установки Descheduler я использовал Helm-чарт, доступный в официальном репозитории.

helm repo add descheduler https://kubernetes-sigs.github.io/descheduler/
helm upgrade descheduler descheduler/descheduler \
  --install \
  --kubeconfig ./kubeconfig \
  --namespace kube-system \
  --version 0.31.0

По умолчанию Descheduler устанавливается в виде CronJob, который запускается каждые две минуты для анализа и эвакуации подов. Перед установкой можно настроить параметры, такие как расписание запуска и политики вытеснения подов, в файле values.yaml, но я оставил значения по умолчанию.

Настройка политик Descheduler

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

  1. Удаление дублирующихся подов (RemoveDuplicates): эта политика выявляет и удаляет поды, которые были случайно созданы в нескольких экземплярах, что помогает освободить ресурсы и предотвратить избыточное использование.
  2. Удаление подов с чрезмерным количеством перезапусков (RemovePodsHavingTooManyRestarts): эта политика нацелена на поды, которые перезапускались более 100 раз, включая перезапуски инициализационных контейнеров. Такие поды могут указывать на проблемы в приложении, и их удаление способствует стабильности кластера.
  3. Удаление подов, нарушающих привязку к узлам (RemovePodsViolatingNodeAffinity): эта политика удаляет поды, которые больше не соответствуют требованиям привязки к узлам, например, если узлы были помечены или изменены, и поды больше не удовлетворяют этим условиям.
  4. Удаление подов, нарушающих ограничения (taints) узлов (RemovePodsViolatingNodeTaints): эта политика нацелена на поды, размещённые на узлах с ограничениями (taints), которые несовместимы с толерантностями подов, что может привести к неправильному функционированию приложений.
  5. Удаление подов, нарушающих правила раздельного существования (affinity) между подами (RemovePodsViolatingInterPodAntiAffinity): эта политика удаляет поды, которые нарушают правила раздельного существования между подами, обеспечивая, чтобы поды, которые не должны размещаться вместе, были разделены.
  6. Удаление подов, нарушающих ограничения распределения по топологии (RemovePodsViolatingTopologySpreadConstraint): эта политика удаляет поды, которые не соответствуют ограничениям распределения по топологии, помогая равномерно распределить поды по различным зонам отказа или другим топологическим доменам.
  7. Низкая загрузка узлов (LowNodeUtilization): эта политика определяет узлы с использованием ресурсов ниже 20% по CPU, памяти и количеству подов и перемещает поды на более загруженные узлы, чтобы освободить недогруженные узлы для возможного масштабирования вниз.

Мониторинг и обновление Descheduler

После установки рекомендуется последить за логами CronJob Descheduler для оценки его работы. У меня все прошло хорошо, проблем не возникло.

Итоги

Я очень доволен! Descheduler спас меня от ручной работы, теперь мой кластер сам себя чинит и балансирует и мне не нужно проверять все ли в порядке. А больше ничего и не требуется.

Источники

GitHub - kubernetes-sigs/descheduler: Descheduler for Kubernetes
Descheduler for Kubernetes. Contribute to kubernetes-sigs/descheduler development by creating an account on GitHub.
descheduler 0.32.1 · helm/descheduler
Descheduler for Kubernetes is used to rebalance clusters by evicting pods that can potentially be scheduled on better nodes. In the current implementation, descheduler does not schedule replacement of evicted pods but relies on the default scheduler for that.
Вытеснение, инициированное через API
Вытеснение, инициированное через API, — процесс, при котором с помощью Eviction API создается объект Eviction, который запускает корректное завершение работы пода. Вытеснение можно инициировать напрямую с помощью Eviction API или программно, используя клиент API-сервера (например, командой kubectl drain). В результате будет создан объект Eviction, который запустит процесс контролируемого завершения работы Pod’а. Вытеснения, инициированные через API, учитывают настройки PodDisruptionBudget и terminationGracePeriodSeconds. Создание с помощью API объекта Eviction для Pod’а аналогично выполнению операции DELETE для этого Pod’а, которая контролируется политикой.