Рецепт 11. Изоляция PostgreSQL-нагрузок в Kubernetes с Kind

Как использовать taints, tolerations и pod anti-affinity для полной изоляции PostgreSQL-инстансов на выделенных узлах Kubernetes — поверх node labels из Рецепта 10.
Опубликовано:

Продолжение Рецепта 10: taints, tolerations и pod anti-affinity для полной изоляции PostgreSQL в Kubernetes.


В Рецепте 10 мы настроили многонодовый Kind-кластер с выделенными PostgreSQL-узлами через node labels и nodeSelector. Но одних меток недостаточно — они направляют PostgreSQL на нужные узлы, но не запрещают другим нагрузкам приземляться на те же узлы.

В этой статье решаем две задачи:

Для этого используем taint node-role.kubernetes.io/postgres и pod anti-affinity.

Прежде чем начать

Ознакомьтесь с Рецептом 10 — он даёт фундамент для этого руководства.

Taints на многонодовом кластере

Расширим конфигурацию Kind: к каждому postgres-узлу добавим taint через kubeadmConfigPatches:

YAML
kind: Cluster
apiVersion: kind.x-k8s.io/v1alpha4
name: multi-node-taints-template
nodes:

# Control Plane
- role: control-plane

# Инфраструктура / Приложения
- role: worker
  labels:
    infra.node.kubernetes.io:
- role: worker
  labels:
    app.node.kubernetes.io:

# PostgreSQL-узлы (3)
- role: worker
  labels:
    postgres.node.kubernetes.io:
  kubeadmConfigPatches:
  - |
    kind: JoinConfiguration
    nodeRegistration:
      taints:
      - key: node-role.kubernetes.io/postgres
        effect: NoSchedule
- role: worker
  labels:
    postgres.node.kubernetes.io:
  kubeadmConfigPatches:
  - |
    kind: JoinConfiguration
    nodeRegistration:
      taints:
      - key: node-role.kubernetes.io/postgres
        effect: NoSchedule
- role: worker
  labels:
    postgres.node.kubernetes.io:
  kubeadmConfigPatches:
  - |
    kind: JoinConfiguration
    nodeRegistration:
      taints:
      - key: node-role.kubernetes.io/postgres
        effect: NoSchedule

# Запасной узел
- role: worker
Нажмите, чтобы развернуть и увидеть больше

Обратите внимание на дополнительный «запасной» узел — его назначение описано ниже.

Создаём кластер:

SH
kind create cluster --config multi-node-taints-template.yaml --name cnpg
Нажмите, чтобы развернуть и увидеть больше

Назначаем метки ролей:

SH
kubectl label node -l postgres.node.kubernetes.io node-role.kubernetes.io/postgres=
kubectl label node -l infra.node.kubernetes.io node-role.kubernetes.io/infra=
kubectl label node -l app.node.kubernetes.io node-role.kubernetes.io/app=
Нажмите, чтобы развернуть и увидеть больше

Проверка:

SH
kubectl get nodes
Нажмите, чтобы развернуть и увидеть больше
PLAINTEXT
NAME                 STATUS   ROLES           AGE     VERSION
cnpg-control-plane   Ready    control-plane   5m14s   v1.35.0
cnpg-worker          Ready    infra           5m1s    v1.35.0
cnpg-worker2         Ready    app             5m1s    v1.35.0
cnpg-worker3         Ready    postgres        5m      v1.35.0
cnpg-worker4         Ready    postgres        5m1s    v1.35.0
cnpg-worker5         Ready    postgres        5m1s    v1.35.0
cnpg-worker6         Ready    <none>          5m1s    v1.35.0
Нажмите, чтобы развернуть и увидеть больше

Проверяем taint на postgres-узле:

SH
kubectl describe node cnpg-worker5 | grep '^Taints'
Нажмите, чтобы развернуть и увидеть больше

Изоляция CNPG-кластеров на postgres-узлах

Цели:

  1. PostgreSQL запускается только на postgres-узлах — nodeSelector (как в Рецепте 10).
  2. На postgres-узлах ничего кроме PostgreSQL — taint NoSchedule + toleration в манифесте кластера.
  3. Каждый инстанс одного кластера — на отдельном узле — pod anti-affinity с topologyKey: kubernetes.io/hostname.

Всё это выражается в .spec.affinity:

YAML
affinity:
  nodeSelector:
    node-role.kubernetes.io/postgres: ""

  tolerations:
  - key: node-role.kubernetes.io/postgres
    operator: Exists
    effect: NoSchedule

  enablePodAntiAffinity: true
  topologyKey: kubernetes.io/hostname
  podAntiAffinityType: required
Нажмите, чтобы развернуть и увидеть больше

Запуск

Устанавливаем оператор CloudNativePG:

SH
curl -sSfL \
  https://raw.githubusercontent.com/cloudnative-pg/artifacts/main/manifests/operator-manifest.yaml | \
  kubectl apply --server-side -f -
Нажмите, чтобы развернуть и увидеть больше

Убеждаемся, что оператор запустился не на postgres-узле (taints работают):

SH
kubectl get pods -A -o wide
Нажмите, чтобы развернуть и увидеть больше

Создаём PostgreSQL-кластер:

YAML
apiVersion: postgresql.cnpg.io/v1
kind: Cluster
metadata:
  name: cluster-example
spec:
  instances: 3

  storage:
    size: 1Gi

  affinity:
    nodeSelector:
      node-role.kubernetes.io/postgres: ""

    tolerations:
    - key: node-role.kubernetes.io/postgres
      operator: Exists
      effect: NoSchedule

    enablePodAntiAffinity: true
    topologyKey: kubernetes.io/hostname
    podAntiAffinityType: required
Нажмите, чтобы развернуть и увидеть больше
SH
kubectl apply -f cluster-example.yaml
Нажмите, чтобы развернуть и увидеть больше

Проверяем размещение:

SH
kubectl get pods -o wide
Нажмите, чтобы развернуть и увидеть больше
PLAINTEXT
NAME                READY   STATUS    RESTARTS   AGE     IP           NODE           ...
cluster-example-1   1/1     Running   0          3m12s   10.244.7.4   cnpg-worker3   ...
cluster-example-2   1/1     Running   0          2m34s   10.244.1.4   cnpg-worker5   ...
cluster-example-3   1/1     Running   0          113s    10.244.4.4   cnpg-worker4   ...
Нажмите, чтобы развернуть и увидеть больше

Все три инстанса — на разных postgres-узлах. Изоляция работает.

Проверка anti-affinity

Масштабируем до 4 инстансов:

SH
kubectl scale cluster --replicas 4 cluster-example
Нажмите, чтобы развернуть и увидеть больше

Четвёртый под зависнет в Pending — свободных postgres-узлов нет, а anti-affinity запрещает размещение двух инстансов на одном узле:

SH
kubectl describe pod cluster-example-4
Нажмите, чтобы развернуть и увидеть больше

Это ожидаемое поведение — подтверждение корректности настройки.

Что дальше?

Два варианта выхода из ситуации:

Добавление нового узла для PostgreSQL

Используем запасной узел cnpg-worker6. Назначаем ему taint и label:

SH
kubectl taint node cnpg-worker6 node-role.kubernetes.io/postgres=:NoSchedule
kubectl label node cnpg-worker6 node-role.kubernetes.io/postgres=
Нажмите, чтобы развернуть и увидеть больше

После этого четвёртый инстанс будет успешно создан на cnpg-worker6.

Откат к трём инстансам

Если добавлять узел не нужно:

SH
kubectl scale cluster --replicas 3 cluster-example
kubectl delete job cluster-example-4-join
kubectl delete pvc cluster-example-4
Нажмите, чтобы развернуть и увидеть больше

Кластер вернётся в здоровое состояние.

Заключение

Комбинация taints + tolerations + pod anti-affinity обеспечивает полную изоляцию PostgreSQL-нагрузок:

Такой подход реализует чёткое разделение ответственности: DBA управляют ресурсами Cluster (nodeSelector, tolerations, anti-affinity), а инфраструктурная команда — узлами (labels, taints). Разделение упрощает управление и способствует сотрудничеству.

Благодаря портативности Kubernetes эти методы применимы к любой платформе — частной, публичной, гибридной или managed.

Начать поиск

Введите ключевые слова для поиска статей

↑↓
ESC
⌘K Горячая клавиша