← Kubernetes: планирование
5 мин · средне · Урок 2 из 7

Manual scheduling: Pod мимо шедулера

Что делает поле spec.nodeName, чем оно опасно в проде и при чём тут static pods control-plane.

Что вообще такое manual scheduling

Обычно за выбор ноды для Pod отвечает kube-scheduler. Он смотрит на Pod с пустым spec.nodeName, фильтрует ноды по taints, affinity, ресурсам, скорит и выбирает лучшую. Pod получает spec.nodeName = node-3, kubelet на этой ноде подхватывает его и запускает.

Manual scheduling это, когда spec.nodeName пишут в манифесте руками. Шедулер видит, что поле уже заполнено, и пропускает такой Pod мимо. Kubelet на указанной ноде видит «это моё» и запускает. Никаких проверок ресурсов, никаких taints, никакого affinity. Просто запускает.

обычный   Pod → scheduler → kubelet
nodeName  Pod ──────────────→ kubelet

Зачем оно вообще нужно: для static pods и для разовых диагностик. В проде workload так не деплоят, потому что обходишь все защиты шедулера. Поставишь Pod на ноду без свободной памяти, он встанет и тут же поймает OOMKilled.

Минимальный YAML

apiVersion: v1
kind: Pod
metadata:
  name: pinned-pod
spec:
  nodeName: worker-2
  containers:
    - name: nginx
      image: nginxinc/nginx-unprivileged:1.25

Где живёт nodeName

В spec самого Pod, не в spec.template.spec у Deployment. У Deployment задавать nodeName бессмысленно: ReplicaSet будет создавать Pod, шедулер у всех будет пропускать выбор, и все реплики поедут на одну ноду. Получишь single point of failure.

Для «привязки Deployment к группе нод» правильный путь это nodeSelector или nodeAffinity, не nodeName.

В кластере из одной ноды разницу между обычным Pod и Pod с nodeName по факту размещения не увидеть, оба окажутся на одном узле. Видно её в другом месте: у обычного Pod есть события от шедулера (Scheduled: Successfully assigned ...), у Pod с nodeName таких событий нет вообще.

Опечатка в имени ноды оставляет Pod в Pending навсегда и без единого события: шедулер на него не смотрит, а kubelet с таким именем не существует.

Static pods

Static pods это особый случай manual scheduling. Их создаёт сам kubelet, читая YAML-файлы из директории /etc/kubernetes/manifests/ (путь задаётся полем staticPodPath в конфиге kubelet; старый CLI-флаг --pod-manifest-path устарел). apiserver про эти pods не знает, но kubelet делает их видимыми как mirror pods (отсюда суффикс с именем ноды в названии).

Через static pods поднимают control-plane в kubeadm-кластерах: apiserver, controller-manager, scheduler сами стартуют как static pods на control-plane-ноде. Это решает курицу и яйцо: apiserver не нужен apiserver-у, чтобы запуститься.

Связанные команды

kubectl get nodes
kubectl get nodes -o name
kubectl get pod <name> -o jsonpath='{.spec.nodeName}'
kubectl get pods -o wide                    # видно NODE для каждого pod
kubectl describe node <name>                # ресурсы и pods на ноде
kubectl explain pod.spec.nodeName

Что часто ломается

Домашка

Теорию закрепляет задание на своём кластере: 1 шаг с проверкой. Кластер поднимается по треку Kubernetes локально, задания лежат в репозитории k8s-practice.

./run.sh start k8s-manual-scheduling     # развернуть стартовое состояние
./run.sh verify k8s-manual-scheduling    # проверить текущий шаг

Коротко

Прошли материал до конца? Отметьте урок пройденным.