Как зафиксировать все поды и сервисы 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).