Как зафиксировать все поды и сервисы Kubernetes namespace на одной конкретной ноде

Иногда в Kubernetes возникает задача запустить все поды одного namespace строго на одной определённой ноде. Например, если эта нода выделена под изолированную нагрузку или располагается в отдельной зоне доступности (AZ), либо если требуется развернуть временную тестовую среду.

В этой заметке разберём, как это сделать просто и надёжно, а также расскажем о плюсах и ограничениях такого подхода.

Задача

Цель: все поды и сервисы внутри одного namespace Kubernetes должны работать только на одной конкретной ноде.

Причины могут быть разные:

  • Выделенный стенд для отладки;
  • Аппаратные ограничения (например, тестирование железа);
  • Эффективная локализация сервисов для снижения сетевой задержки;
  • Специфический licensing (например, лицензия на ПО привязана к узлу).

Самый простой и управляемый способ решения — использовать механизм nodeSelector.

Как работает nodeSelector

Когда мы добавляем в манифест пода или deployment секцию:

spec:
  nodeSelector:
    kubernetes.io/hostname: my-target-node

Мы указываем Kubernetes scheduler'у размещать поды только на ноде с hostname my-target-node. Kubernetes не будет рассматривать другие ноды для размещения этих подов.

Как узнать имя ноды?

Можно получить список нод и их hostname:

kubectl get nodes -o wide

Или более подробно:

kubectl get nodes -o jsonpath='{range .items[*]}{.metadata.name} {"\t"} {.metadata.labels.kubernetes\.io/hostname} {"\n"} {end}'

Обычно hostname совпадает с именем ноды, но лучше проверить.

Пример полного Deployment

apiVersion: apps/v1
kind: Deployment
metadata:
  name: example-app
  namespace: my-namespace
spec:
  replicas: 3
  selector:
    matchLabels:
   app: example
  template:
    metadata:
      labels:
        app: example
    spec:
      nodeSelector:
        kubernetes.io/hostname: my-target-node
      containers:
      - name: app
        image: nginx

Таким образом, все поды Deployment будут размещены строго на нужной ноде.

Что с сервисами?

Важно понимать, что Kubernetes Service — это абстракция, которая сама по себе не зависит от ноды.

Сервис просто направляет трафик на backend-поды, а если все поды уже на одной ноде, сервис тоже будет работать через неё.

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

Если используется NodePort и требуется, чтобы трафик шел только через нужную ноду, придётся настроить firewall или использовать MetalLB в случае bare-metal.

А как обезопаситься от случайных ошибок?

Чтобы случайно не запустить поды вне нужной ноды, можно использовать дополнительные механизмы:

1. Taints и tolerations

  • Проставить taint на все остальные ноды:
kubectl taint nodes nodeX dedicated=other:NoSchedule
  • На нужной ноде либо taint не ставим, либо добавляем в манифесты tolerations.

2. Admission policies (Kyverno, OPA Gatekeeper)

  • Автоматически патчить или проверять, что все поды в namespace используют правильный nodeSelector.

Пример политики на Kyverno:

apiVersion: kyverno.io/v1
kind: ClusterPolicy
metadata:
  name: enforce-node-selection
spec:
  rules:
  - name: enforce-node
    match:
   resources:
        kinds:
        - Pod
        namespaces:
        - my-namespace
    validate:
      message: "All pods in this namespace must use the correct nodeSelector."
      pattern:
        spec:
          nodeSelector:
            kubernetes.io/hostname: my-target-node

Выводы

Плюсы подхода с nodeSelector:

  • Простая реализация;
  • Управляемость через YAML;
  • Прозрачность и предсказуемость для scheduler'а.

Ограничения:

  • Нужно помнить о ручном управлении nodeSelector при обновлении манифестов;
  • Если нода выходит из строя — поды не пересоздаются на других нодах;
  • Не защищает от ошибок конфигурации без дополнительных мер (taints, admission policies).