Когда вы заходите в плановое обслуживание кластера Kubernetes (апгрейд нод, замена дисков, обновление ОС, работы у провайдера), чаще всего вы делаете одно и то же: kubectl drain и ждёте, пока поды переедут. И вот здесь внезапно выясняется: часть подов не уезжает, дрен «висит», а maintenance window не резиновое.
В центре этой истории обычно PodDisruptionBudget (он же poddisruptionbudget, сокращённо kubernetes pdb). PDB — это контракт между владельцем приложения и оператором кластера: «вот сколько реплик можно потерять при плановых (voluntary) прерываниях». С одной стороны, он защищает сервис от того, что вы случайно выдавите сразу слишком много реплик. С другой — если PDB настроен неправильно или приложение не готово к эвикшенам, он же и блокирует обслуживание.
Ниже — практичная памятка, как работать с disruption budget так, чтобы и сервис жил, и drain node не превращался в лотерею.
Что такое PDB и какие прерывания он контролирует
PodDisruptionBudget ограничивает количество подов, которые можно добровольно выселить одновременно. К voluntary disruptions относятся операции, которые инициирует человек или контроллер, соблюдающий eviction API: kubectl drain, вывод ноды из эксплуатации, а также часть сценариев кластерной автоматики (в зависимости от версии Kubernetes и конкретной реализации).
Важно: PDB не защищает от недобровольных (involuntary) событий: падение ноды, OOMKill, kernel panic, потеря сети или диска. То есть PDB — это инструмент именно для безопасного обслуживания и управляемых изменений, а не «серебряная пуля» от аварий.
Ментальная модель: PDB — это «сколько можно добровольно выселить сейчас», а не «сколько должно работать всегда».
minAvailable vs maxUnavailable
PDB задаётся одним из двух способов:
minAvailable— минимальное число (или процент) подов, которые должны оставаться доступными.maxUnavailable— максимальное число (или процент) подов, которые могут быть недоступны.
Указывать оба нельзя. На практике maxUnavailable часто понятнее операторам: «можно потерять не больше N». minAvailable удобен, когда вы мыслите «должно оставаться не меньше N».
Как PDB влияет на kubectl drain и почему нода может не дрениться
Команда kubectl drain выполняет выселение подов через eviction API. Kubernetes проверяет PDB для каждого пода и решает: можно ли «прервать» его прямо сейчас, не нарушив disruption budget.
Типичные причины, почему drain node упирается:
- Слишком строгий PDB для малого числа реплик. Например, 2 реплики и
minAvailable: 2— вы запретили любые эвикшены. - Нехватка ресурсов на остальных нодах: поды физически некуда переселить. PDB тут ни при чём, но по симптомам выглядит как «дрен не заканчивается».
- Под не становится Ready после пересоздания (пробы, зависимости, миграции), поэтому доступных реплик становится меньше, и PDB начинает блокировать следующие эвикшены.
- Мало реплик или singleton (1 реплика) при
minAvailable: 1илиmaxUnavailable: 0— вы сделали «нулевой бюджет». - Неправильный selector в PDB: он матчится не на те поды или не матчится вообще, и ожидания не совпадают с реальностью.
Быстрая диагностика: что именно блокирует эвикшены
Начинайте с проверки статуса PDB и текущего «бюджета»:
kubectl get pdb -A
kubectl describe pdb -n my-ns my-app-pdb
В kubectl describe важны поля:
Allowed disruptions— сколько подов можно выселить прямо сейчас.Current healthyиDesired healthy— сколько подов считается здоровыми и сколько должно оставаться.
Если Allowed disruptions равен 0, kubectl drain будет ждать (или падать ошибкой эвикшена), пока не появится доступный «бюджет».
Если вам удобнее тестировать сценарии обслуживания на отдельном стенде, поднимайте кластер на VDS: так проще воспроизвести влияние PDB на drain и оценить реальный запас по ресурсам.
Практика: примеры PodDisruptionBudget для типовых приложений
Ниже — несколько рабочих шаблонов, от которых удобно отталкиваться. В примерах используем maxUnavailable, потому что в эксплуатации проще считать «сколько можно потерять».

Стандартный stateless deployment: можно потерять 1 реплику
Подходит для веба/API за балансировщиком, где реплик 2+ и нет жёсткой привязки к конкретной ноде.
apiVersion: policy/v1
kind: PodDisruptionBudget
metadata:
name: my-app-pdb
namespace: my-ns
spec:
maxUnavailable: 1
selector:
matchLabels:
app: my-app
Если у вас 3 реплики, то одновременно можно добровольно «уронить» 1. Это обычно достаточно, чтобы безопасно дренить ноды по одной.
Две реплики и строгая доступность: оставляем минимум одну
Если реплик всего 2, то maxUnavailable: 1 всё ещё ок: можно выселить одну реплику, вторая продолжит обслуживать.
Опасная зона: maxUnavailable: 0 или minAvailable: 2 при двух репликах. Такое уместно только если вы никогда не хотите voluntary disruptions и готовы обслуживать иначе (например, через отдельный механизм миграции или временное масштабирование).
Процентный budget для больших пулов
Для 20–200 реплик удобно задавать проценты, чтобы правила масштабировались вместе с нагрузкой:
apiVersion: policy/v1
kind: PodDisruptionBudget
metadata:
name: workers-pdb
namespace: my-ns
spec:
maxUnavailable: 10%
selector:
matchLabels:
app: workers
Тут есть нюанс: проценты округляются, и на малых числах реплик поведение может отличаться от ожиданий. Если реплик мало (например, 3–5), проценты лучше не использовать — берите абсолютные значения.
Singleton (1 реплика): что делать, если нужен maintenance
Если приложение в 1 реплику и вы поставите maxUnavailable: 0, вы запретите его выселять через eviction. На плановом обслуживании это означает: нода с этим подом не дренится.
Рабочие варианты:
- На время обслуживания временно масштабировать Deployment до 2 реплик и иметь PDB, допускающий потерю одной реплики.
- Перевести компонент в HA (2+ реплики) и закрепить это PDB.
- Если это stateful и одиночный мастер — проработать архитектуру (репликация, failover), потому что PDB проблему не решит.
Maintenance window: как планировать обслуживание с учётом PDB
Правильно настроенный disruption budget — это не только про безопасность, но и про предсказуемость. Чтобы maintenance window не «съедался» ожиданиями, продумайте процесс.
Чек-лист перед обслуживанием
- Проверьте, что у ключевых workloads есть PDB, и он соответствует реальной стратегии доступности.
- Убедитесь, что есть запас по ресурсам: если вы выведете одну ноду, оставшиеся должны «переварить» переселение.
- Проверьте, что Pod anti-affinity и
topologySpreadConstraintsне делают переселение невозможным при уменьшении числа нод. - Прогоните «сухой» сценарий: сможете ли вы дренить хотя бы одну ноду без ручных вмешательств.
Типовой порядок действий: cordon → drain → работы → uncordon
Минимальный безопасный сценарий обслуживания ноды:
kubectl cordon node-1
kubectl drain node-1 --ignore-daemonsets --delete-emptydir-data
kubectl uncordon node-1
Параметр --delete-emptydir-data удаляет данные emptyDir при выселении. Это удобно, но опасно для подов, которые (ошибочно) хранят там важное. Если не уверены — сначала найдите такие поды.
Почему drain может быть «долгим» даже при нормальном PDB
PDB отвечает только за «можно ли выселить». А «когда под станет Ready на новой ноде» — это уже про ваше приложение и инфраструктуру. Частые источники задержек:
- долгий старт приложения (миграции, прогрев кэша);
- строгие
readinessProbe, которые не проходят до прогрева; - узкие места по сети/диску при массовом пересоздании подов;
- подвисшие завершения из-за длинного
terminationGracePeriodSecondsи отсутствия корректного graceful shutdown.
Если вы хотите «упаковать» обслуживание в окно, ускорение обычно даёт не изменение PDB, а оптимизация старта и остановки плюс достаточный резерв ресурсов.
Связанный практический кейс на уровне приложений: если у вас PHP-сервисы, их «долгий stop/start» часто выявляется через slowlog и тайминги воркеров; пригодится разбор PHP-FPM slowlog: как читать и применять в эксплуатации.
Если вместе с обслуживанием вы подтягиваете безопасность (например, закрываете техдолг по TLS), держите под рукой актуальные SSL-сертификаты: так проще планировать замену сертификатов в то же окно, не размазывая изменения по нескольким релизам.
Частые ошибки в PodDisruptionBudget и как их исправлять
Ошибка 1: PDB есть, но selector не совпадает с реальными pod labels
Симптом: PDB «не работает» (или работает для других подов). Проверка:
kubectl get pod -n my-ns --show-labels
kubectl get pdb -n my-ns -o yaml
Убедитесь, что spec.selector.matchLabels точно соответствует меткам подов. Для сложных селекторов используйте matchExpressions, но держите их простыми: чем проще selector, тем меньше сюрпризов на обслуживании.
Ошибка 2: «нулевой бюджет» там, где нужно обслуживать
Симптом: Allowed disruptions: 0 всегда, даже когда всё здорово. Частая причина — minAvailable равен числу реплик или maxUnavailable: 0 на stateless сервисе.
Исправление: либо увеличьте число реплик, либо разрешите хотя бы один voluntary eviction. В идеале — оба варианта.
Ошибка 3: PDB конфликтует с реальной доступностью приложения
PDB считает «healthy» поды через готовность (readiness) и условия доступности контроллера. Если ваш readinessProbe слишком оптимистичный или, наоборот, слишком строгий, расчёт бюджета будет не отражать реальную картину.
Практический подход: сначала стабилизируйте жизненный цикл пода (startup/readiness/liveness, graceful shutdown), а уже потом «зажимайте» PDB. Иначе вы получите ложные блокировки на ровном месте.
Как сочетать PDB с кластерной автоматикой и обновлениями
PDB часто воспринимают как «препятствие» автоматизации, но на самом деле это механизм согласования. Если у вас есть автоскейлинг нод, автоматические обновления, ручные работы — PDB помогает всем участникам не вырубить сервис.
Рекомендации для эксплуатации:
- Для критичных stateless сервисов держите 3+ реплики и
maxUnavailable: 1(или 10% на больших пулах). - Для системных компонентов и ingress-контроллеров также задавайте PDB, иначе обслуживание нод может «съесть» сразу несколько экземпляров.
- Если есть строгие требования к доступности, используйте не только PDB, но и распределение подов по нодам и зонам (anti-affinity/spread) плюс резерв мощности.

Шпаргалка: быстрые команды для проверки перед maintenance
kubectl get pdb -A
kubectl describe pdb -n my-ns my-app-pdb
kubectl get pod -A -o wide --field-selector spec.nodeName=node-1
kubectl cordon node-1
kubectl drain node-1 --ignore-daemonsets --delete-emptydir-data
Итог: как сделать PDB союзником, а не блокером
PodDisruptionBudget — это инструмент дисциплины. Он заставляет заранее ответить на вопросы: «сколько реплик должно пережить обслуживание», «что будет, если одну ноду вывести», «есть ли у нас запас мощности».
Чтобы maintenance window проходило спокойно:
- не делайте «нулевой» disruption budget для сервисов, которые нужно обслуживать;
- держите достаточное число реплик и резерв ресурсов;
- перед обслуживанием смотрите
Allowed disruptionsи устраняйте причины, почему он равен нулю; - оптимизируйте readiness и graceful shutdown — это напрямую сокращает время дренов.
С таким подходом poddisruptionbudget перестаёт быть загадочной сущностью из YAML и становится понятным регулятором, который делает drain node предсказуемым и безопасным.


