Как стать автором
Поиск
Написать публикацию
Обновить
548.78
OTUS
Развиваем технологии, обучая их создателей

PDB для StatefulSet с minAvailable=100 % и контролируемый rolling-update через Partition

Уровень сложностиСредний
Время на прочтение4 мин
Количество просмотров212

Привет!

Сегодня мы рассмотрим, как перезапустить полноценный ZooKeeper‑кластер в Kubernetes так, чтобы ни один из узлов не потерял кворум даже на микросекунду. Берём два проверенных инструмента — строгий PodDisruptionBudget с minAvailable: 100% и StatefulSet с updateStrategy.RollingUpdate.partition.

Зачем вообще это

  • ZooKeeper теряет кворум, если одновременно «отваливаются» больше ⌈N/2⌉ нод.

  • Классический kubectl rollout restart sts/zk бьёт по последнему поду, дожидается его готовности и переходит к предыдущему. На бумаге это безопасненько, но при drain-операциях или хаотичных эвикшенах можно внезапно уронить нод.

  • Подцепив PDB с 100 % мы закручиваем гайки: eviction-контроллер не сможет снести под даже при drain-узла.

  • partition даёт ручной триггер для каждого пода: пока мы явно не скажем «- 1», ничего не перезапустится.

Готовим строгий PDB

apiVersion: policy/v1
kind: PodDisruptionBudget
metadata:
  name: zk-pdb-strict
spec:
  minAvailable: 100%   # нулевая терпимость к эвикшенам
  selector:
    matchLabels:
      app: zookeeper

Создаём:

kubectl apply -f zk-pdb-strict.yaml
kubectl get pdb zk-pdb-strict

Поле ALLOWED DISRUPTIONS всегда «0», и это именно то, что нам нужно. Теперь ни плановый drain, ни kubectl delete pod не разрушат кластер (контроллер просто скажет «eviction forbidden»).

Настраиваем StatefulSet

Напишем минимальный фрагмент StatefulSet с RollingUpdate-стратегией и явно заданным partition. Пусть у нас три реплики (replicas: 3).

apiVersion: apps/v1
kind: StatefulSet
metadata:
  name: zk
spec:
  serviceName: zk-hs
  replicas: 3
  updateStrategy:
    type: RollingUpdate
    rollingUpdate:
      partition: 3   # freeze-точка — выше самого большого ординала
  selector:
    matchLabels:
      app: zookeeper
  template:
    metadata:
      labels:
        app: zookeeper
    spec:
      containers:
      - name: zk
        image: bitnami/zookeeper:3.9.3
        readinessProbe:
          exec:
            command: ["zkServer.sh", "status"]
        livenessProbe:
          exec:
            command: ["zkServer.sh", "status"]

При изменении шаблона подов (новый тег образа, другие ресурсы) контроллер не заденет ни один экземпляр, потому что ординалы 0 – 2 меньше partition.

Патчим шаблон подов

Например, хотим обновить образ до 3.9.4:

kubectl patch sts zk --type='json' \
  -p='[{"op":"replace","path":"/spec/template/spec/containers/0/image","value":"bitnami/zookeeper:3.9.4"}]'

Смотрим, что ничего не происходит:

kubectl rollout status sts/zk   # висит на Current revision

Это нормально — partition держит обновление.

Самописный one-liner для чистого rolling-update

#!/usr/bin/env bash
set -euo pipefail

STS="zk"
REPLICAS=$(kubectl get sts "$STS" -o=jsonpath='{.spec.replicas}')
for ((ORD=$((REPLICAS-1)); ORD>=0; ORD--)); do
  echo "Понижаем partition до $ORD"
  kubectl patch sts "$STS" -p \
    "{\"spec\":{\"updateStrategy\":{\"rollingUpdate\":{\"partition\":$ORD}}}}"
  echo " Ждём готовность pod/$STS-$ORD"
  kubectl rollout status --watch --timeout=600s sts/"$STS" \
    --revision=$(kubectl get sts "$STS" -o=jsonpath='{.status.updateRevision}')
done
echo " Все поды обновлены"

Что происходит:

  1. partition = 2 — контроллер убивает zk-2, поднимает новый с 3.9.4, ждёт Ready.

  2. Цикл ставит partition = 1 — очередь доходит до zk-1.

  3. partition = 0 — наконец обновляется zk-0.

В любой момент только одна реплика недоступна, PDB не нарушается, кворум не покидает нас ни на минуту.

Проверяем, что кворум жив

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

kubectl exec zk-0 -- zkCli.sh stat | grep -E 'Mode|Zxid'
kubectl exec zk-1 -- zkCli.sh stat | grep -E 'Mode|Zxid'
kubectl exec zk-2 -- zkCli.sh stat | grep -E 'Mode|Zxid'

Или сделать sanity-тест:

kubectl exec zk-0 -- zkCli.sh create /hello "$(date +%s)"
kubectl exec zk-2 -- zkCli.sh get /hello

Данные доступны сразу на всех серверах при условии двух готовых подов из трёх — именно то, что мы и сохраняем.

Возможные проблемы

Ситуация

Что пойдёт не так

Как лечить

Drain ноды

kubectl drain зависнет — PDB запрещает эвикшен

Временно снижаем minAvailable до 67 %, проведите drain, верните 100 %

CrashLoopReadiness

Readiness Probe ложится, под считается Unready, кворум падает

Перед обновлением убедитесь, что проба ruok стабильно отвечает

Смена конфигурации ZK

Требуется reload, а не restart

Используйте reconfig прямо из zkCli.sh, не трогая StatefulSet

Включён Istio

Sidecar мешает быстрому отсоединению от сети

Добавляем preStop с graceful shutdown в 25 сек, иначе лидер не успеет пересчитаться

Автоматизируем в CI

Простейший GitLab-job:

update_zk:
  stage: deploy
  script:
    - kubectl config use-context prod
    - ./scripts/partition-rolling.sh
  when: manual

Можно хранить желаемый тег образа в Helm-values, а скрипт брать replicas и release из helm status. Главное — никогда не трогайте partition и PDB в разных MR: держите их под одной ревизией, иначе рискуете схлопотать «Eviction is refused» в самый неудобный момент.

Что насчёт maxUnavailable

С Kubernetes 1.25 можно задать maxUnavailable прямо в StatefulSet — это альтернатива нашему циклу. Но ZooKeeper-кластеру критичен порядок остановки (последний ординал первым), а maxUnavailable не гарантирует этот порядок. Плюс нам нужна совместимость с версиями до 1.25, где поля ещё нет. Поэтому комбинация partition + скрипт пока быстрее и надёжнее.

Итог

Спасибо, что дочитали до конца — надеюсь, теперь раскатывать ZooKeeper без потери кворума для вас будет так же естественно, как kubectl get pods. Делитесь своими кейсами, подходами и замечаниями в комментариях — всегда интересно увидеть, как это решают в других задачах.


Даже в зрелых командах CI/CD часто остаётся точкой боли: внешние сервисы ломают изоляцию, пайплайны зависимы от ручного вмешательства, а деплой — это всё ещё зона повышенного риска. Если вы хотите перевести доставку кода в предсказуемую, автономную и контролируемую систему — эти уроки помогут на практике разобраться, как это сделать.

Чтобы открыть доступ ко всем открытым урокам, а заодно проверить своей уровень знаний Kubernetes, пройдите вступительное тестирование.

Теги:
Хабы:
+8
Комментарии0

Публикации

Информация

Сайт
otus.ru
Дата регистрации
Дата основания
Численность
101–200 человек
Местоположение
Россия
Представитель
OTUS