Привет!
Сегодня мы рассмотрим, как перезапустить полноценный 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 " Все поды обновлены"
Что происходит:
-
partition= 2 — контроллер убиваетzk-2, поднимает новый с 3.9.4, ждёт Ready. -
Цикл ставит
partition= 1 — очередь доходит доzk-1. -
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 ноды |
|
Временно снижаем |
|
CrashLoopReadiness |
Readiness Probe ложится, под считается Unready, кворум падает |
Перед обновлением убедитесь, что проба ruok стабильно отвечает |
|
Смена конфигурации ZK |
Требуется reload, а не restart |
Используйте |
|
Включён Istio |
Sidecar мешает быстрому отсоединению от сети |
Добавляем |
Автоматизируем в 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 часто остаётся точкой боли: внешние сервисы ломают изоляцию, пайплайны зависимы от ручного вмешательства, а деплой — это всё ещё зона повышенного риска. Если вы хотите перевести доставку кода в предсказуемую, автономную и контролируемую систему — эти уроки помогут на практике разобраться, как это сделать.
-
28 июля, 20:00
GitlabCI + ArgoCD — сборка и доставка приложений, не покидая кластер
Как собрать и доставить код, не выходя за границы Kubernetes: связка GitlabCI + ArgoCD для полной автономии CI/CD. -
12 августа, 20:00
CI/CD: 90 минут от платформы до конвейера
От пустого проекта до рабочего пайплайна за 100 секунд, а дальше — по шагам: всё, что нужно знать, чтобы CI/CD действительно работал.
Чтобы открыть доступ ко всем открытым урокам, а заодно проверить своей уровень знаний Kubernetes, пройдите вступительное тестирование.
ссылка на оригинал статьи https://habr.com/ru/articles/930390/
Добавить комментарий