Планирование Pod Kubernetes на определённых узлах

KubernetesBeginner
Практиковаться сейчас

Введение

Обычно планировщик выбирает любой подходящий узел. Метки и аффинность задают предпочтения рабочей нагрузки, а taint позволяют узлам отклонять Pod, у которых нет соответствующих toleration.

В ходе этого эксперимента вы последовательно примените эти механизмы к подготовленному узлу и с помощью событий Pod отличите обязательное правило планирования от необязательного предпочтения.

Используйте следующую модель: метки описывают узлы, селекторы и аффинность выражают требования Pod, taint указывают, что узел отклоняет, а toleration позволяют Pod пройти соответствующее правило отклонения. Ни один из этих механизмов напрямую не «перемещает» уже запущенный Pod.

Добавьте метки узлу для размещения

На этом шаге добавьте описательные метки и выведите их отдельными столбцами.

Метки намеренно описывают размещение рабочей нагрузки, а не идентичность приложения. workload-zone=local обозначает расположение, а disk-class=fast — характеристику узла. Kubernetes не проверяет смысл этих значений с точки зрения бизнеса: именно правила планирования определяют, как они используются.

cd /home/labex/project/pod-scheduling
kubectl create namespace practice-scheduling
kubectl label node labex-v135 workload-zone=local disk-class=fast
kubectl get nodes -L workload-zone,disk-class

Метки служат исходными данными для селекторов и аффинности. Они не перемещают уже существующие Pod автоматически, если только контроллер не создаст им замену.

Параметр -L добавляет по одному столбцу для каждого ключа метки. Значения local и fast в строке узла подтверждают, что планировщик сможет обработать правила, используемые на следующих шагах.

NAME         STATUS   ROLES           ...   VERSION   WORKLOAD-ZONE   DISK-CLASS
labex-v135   Ready    control-plane   ...   v1.35.5   local           fast

Это также подтверждает, что обновлённая среда работает на Kubernetes v1.35.5. Два пользовательских столбца появились благодаря только что добавленным меткам.

Требуйте узел с помощью nodeSelector

На этом шаге создайте Pod с самым простым правилом размещения — точным совпадением меток.

nodeSelector представляет собой набор обязательных совпадений «ключ — значение». Pod может быть размещён только на узле, где для каждого ключа указано требуемое значение. Такой подход подходит для простых обязательных ограничений размещения.

cd /home/labex/project/pod-scheduling
cat <<'EOF' > selected-pod.yaml
apiVersion: v1
kind: Pod
metadata: {name: selected-pod, namespace: practice-scheduling}
spec:
  nodeSelector: {disk-class: fast}
  containers:
    - {name: worker, image: busybox:1.36, imagePullPolicy: IfNotPresent, command: ["sh", "-c", "while true; do sleep 30; done"]}
EOF

Примените манифест Pod, дождитесь его готовности и проверьте назначенный узел в столбце NODE:

kubectl apply -f selected-pod.yaml
kubectl wait --for=condition=Ready pod/selected-pod -n practice-scheduling --timeout=120s
kubectl get pod selected-pod -n practice-scheduling -o wide

nodeSelector является обязательным правилом: если ни один узел не содержит всех требуемых меток, Pod останется в состоянии Pending.

Выбранным узлом должен стать labex-v135. Успешное планирование подтверждает, что метка disk-class=fast на узле соответствует требованию Pod.

pod/selected-pod condition met
NAME           READY   STATUS    ...   NODE
selected-pod   1/1     Running   ...   labex-v135

Столбец NODE показывает результат планирования, а nodeSelector в манифесте объясняет, почему этот узел оказался подходящим.

Сравните обязательную и предпочтительную аффинность

На этом шаге создайте Pod, которому требуется локальная зона, но предпочтителен быстрый диск.

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

Длинные имена полей описывают момент действия правила: оно учитывается во время планирования и не применяется во время выполнения. Если позднее метка изменится, Kubernetes не станет выселять работающий Pod только из-за этого правила аффинности.

cd /home/labex/project/pod-scheduling
cat <<'EOF' > affinity-pod.yaml
apiVersion: v1
kind: Pod
metadata: {name: affinity-pod, namespace: practice-scheduling}
spec:
  affinity:
    nodeAffinity:
      requiredDuringSchedulingIgnoredDuringExecution:
        nodeSelectorTerms:
          - matchExpressions: [{key: workload-zone, operator: In, values: [local]}]
      preferredDuringSchedulingIgnoredDuringExecution:
        - weight: 100
          preference:
            matchExpressions: [{key: disk-class, operator: In, values: [fast]}]
  containers:
    - {name: worker, image: busybox:1.36, imagePullPolicy: IfNotPresent, command: ["sh", "-c", "while true; do sleep 30; done"]}
EOF

Примените манифест Pod и дождитесь его планирования:

kubectl apply -f affinity-pod.yaml
kubectl wait --for=condition=Ready pod/affinity-pod -n practice-scheduling --timeout=120s
kubectl get pod affinity-pod -n practice-scheduling -o wide

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

В полученной таблице снова будет указан labex-v135. В этом одновузловом кластере обязательное правило workload-zone=local определяет возможность размещения, а предпочтение быстрого диска демонстрирует синтаксис взвешивания без необходимости добавлять второй узел только для сравнения.

Используйте taint и toleration

На этом шаге добавьте taint узлу, убедитесь, что Pod без toleration остаётся в состоянии Pending, а затем создайте разрешённый Pod.

Taint имеет ключ practice-reserved, значение true и эффект NoSchedule. Этот эффект блокирует размещение новых Pod без соответствующего toleration, но не выселяет Pod, которые уже работают на узле.

Команда kubectl taint node NODE key=value:effect добавляет такое правило к узлу. Следующая команда kubectl run создаёт самостоятельный Pod без toleration, а ожидание условия PodScheduled даёт планировщику ограниченные десять секунд, чтобы продемонстрировать ожидаемый отказ.

cd /home/labex/project/pod-scheduling
kubectl taint node labex-v135 practice-reserved=true:NoSchedule
kubectl run blocked-pod -n practice-scheduling --image=busybox:1.36 --image-pull-policy=IfNotPresent --restart=Never -- sh -c 'sleep 3600'
kubectl wait --for=condition=PodScheduled pod/blocked-pod -n practice-scheduling --timeout=10s || true
kubectl events -n practice-scheduling --for pod/blocked-pod

Истечение времени ожидания является ожидаемым результатом, а || true позволяет продолжить выполнение лабораторной работы. В событии планирования должно упоминаться отсутствие toleration для taint — это объясняет, почему Pod остаётся в состоянии Pending.

error: timed out waiting for the condition on pods/blocked-pod
LAST SEEN   TYPE      REASON             OBJECT            MESSAGE
...         Warning   FailedScheduling   Pod/blocked-pod   0/1 nodes are available: 1 node(s) had untolerated taint(s)...

В данном случае состояние Pending является правильным экспериментальным результатом. Планировщик обработал Pod, но не нашёл узел, на котором ему было бы разрешено разместить его.

Создайте соответствующий toleration. Toleration позволяет пройти через taint, но сам по себе не выбирает узел.

cat <<'EOF' > tolerated-pod.yaml
apiVersion: v1
kind: Pod
metadata: {name: tolerated-pod, namespace: practice-scheduling}
spec:
  nodeSelector: {workload-zone: local}
  tolerations:
    - {key: practice-reserved, operator: Equal, value: "true", effect: NoSchedule}
  containers:
    - {name: worker, image: busybox:1.36, imagePullPolicy: IfNotPresent, command: ["sh", "-c", "while true; do sleep 30; done"]}
EOF
kubectl apply -f tolerated-pod.yaml
kubectl wait --for=condition=Ready pod/tolerated-pod -n practice-scheduling --timeout=120s
kubectl delete pod blocked-pod -n practice-scheduling

У разрешённого Pod также есть nodeSelector: одно правило выбирает узел, а другое позволяет разместить Pod на нём. Удаление намеренно заблокированного Pod оставляет пространство имён в чистом и работоспособном состоянии.

Сообщение pod/tolerated-pod condition met подтверждает, что добавление соответствующего toleration изменило результат. Предыдущие Pod продолжают работать, поскольку NoSchedule влияет на новое размещение, но не выселяет уже запущенные Pod.

Итоги

Вы использовали метки, nodeSelector, обязательную и предпочтительную аффинность, события планировщика, taint и toleration, чтобы независимо задавать требования к размещению и правила допуска на узел.