Привет, Хабр! Сегодня рассмотрим такой компонент в Kubernetes, как kube‑scheduler — это компонент control plane в Kubernetes, отвечающий за назначение подов на узлы.

Полный процесс включает четыре этапа:

  1. Queueing — под попадает в очередь планировщика.

  2. Filtering — отсеиваются неподходящие ноды.

  3. Scoring — оцениваются оставшиеся ноды.

  4. Binding — под назначается на лучшую ноду.

Как kube-scheduler принимает решения?

Сначала kube‑scheduler убирает ноды, которые не соответствуют требованиям пода. Если нода не подходит, она сразу исключается.

Критерии фильтрации:

  • Node Unschedulable — если у ноды стоит spec.unschedulable: true, на неё нельзя посадить под.

  • NodeSelector — поды могут размещаться только на нодах с нужными labels.

  • Node Affinity — предпочтения, какие ноды нам нравятся.

  • Pod Affinity / Anti‑Affinity — можно ли размещать поды рядом или нужно их разнести?

  • Volume Binding — если под запрашивает PVC, он должен попасть на ноду с доступным storage.

  • Taints & Tolerations — ограничения, которые накладывают админы.

Пример пода, который хочет жить только на нодах с SSD:

apiVersion: v1
kind: Pod
metadata:
  name: my-app
spec:
  containers:
    - name: my-app
      image: nginx
  nodeSelector:
    disktype: ssd

Если подходящих нод нет, под будет висеть в Pending.

После фильтрации kube‑scheduler оценивает оставшиеся ноды и выбирает лучшую.

Основные стратегии:

  • Least Requested Priority — чем меньше загружена нода, тем лучше.

  • Balanced Resource Allocation — баланс CPU/RAM.

  • Image Locality — нода, у которой уже есть нужный образ, получает приоритет.

  • Node Affinity Priority — предпочтения, заданные через nodeAffinity.

  • Taint Toleration Priority — обработка taints и tolerations.

Пример кастомного приоритета, который группирует поды на одной ноде:

apiVersion: kubescheduler.config.k8s.io/v1
kind: KubeSchedulerConfiguration
profiles:
  - schedulerName: my-scheduler
    plugins:
      score:
        enabled:
          - name: PodTopologySpread
            weight: 10

Так указываем планировщику: старайся селить мои поды ближе друг к другу.

Если ни одна нода не подходит, kube‑scheduler пытается освободить место. Он ищет менее приоритетные поды и может их вытеснить.

Механика:

  1. Под запрашивает priorityClassName: high-priority.

  2. kube‑scheduler не находит свободных нод.

  3. Он ищет слабые поды и выселяет их.

  4. Выселенные поды перезапускаются в другом месте.

apiVersion: scheduling.k8s.io/v1
kind: PriorityClass
metadata:
  name: high-priority
value: 1000000
preemptionPolicy: PreemptLowerPriority

Так создается некий вип класс подов, которые будут вытеснять слабые.

Как запустить кастомный планировщик?

Можно написать свой планировщик, если стандартный не подходит. Например, если нужно учитывать нестандартные метрики: доступные GPU, сетевую нагрузку или latency между нодами.

Пример кастомного планировщика на Go, который размещает поды только на ноды с минимум 50% свободного CPU.

package main

import (
	"context"
	"fmt"
	"k8s.io/client-go/kubernetes"
	"k8s.io/client-go/rest"
	"k8s.io/kubernetes/pkg/scheduler/framework"
)

type CustomScheduler struct{}

func (cs *CustomScheduler) Name() string {
	return "CustomCPUScheduler"
}

func (cs *CustomScheduler) Score(ctx context.Context, state *framework.CycleState, pod *framework.PodInfo, nodeName string) (int64, *framework.Status) {
	clientset, err := kubernetes.NewForConfig(rest.InClusterConfig())
	if err != nil {
		return 0, framework.AsStatus(err)
	}

	node, err := clientset.CoreV1().Nodes().Get(ctx, nodeName, metav1.GetOptions{})
	if err != nil {
		return 0, framework.AsStatus(err)
	}

	allocatableCPU := node.Status.Allocatable["cpu"]
	capacityCPU := node.Status.Capacity["cpu"]

	// Подсчёт свободного CPU
	freeCPU := allocatableCPU.Value() * 100 / capacityCPU.Value()
	if freeCPU > 50 {
		return int64(freeCPU), framework.NewStatus(framework.Success)
	}

	return 0, framework.NewStatus(framework.Unschedulable, "Not enough free CPU")
}

func main() {
	fmt.Println("Запуск кастомного планировщика...")
}

Подключаемся к API Kubernetes, после получаем список нод. Далее считаем, сколько CPU занято, если свободно меньше 50% CPU, не размещаем под, аесли больше 50%, ставим под и возвращаем успех.

Подробнее с kube‑scheduler можно ознакомиться здесь.


В заключение рекомендую всем желающим к посещению открытый урок «Kubernetes + CI/CD + GitOps — как сделать стабильный деплой без выхода из кластера». Если интересно, записаться можно по ссылке.

Также список всех остальных онлайн-уроков от практикующих преподавателей можно посмотреть в календаре.

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