Как работает контроллер Deployment при обновлении приложения?
Общая идея
Deployment — это контроллер в Kubernetes, который управляет жизненным циклом набора реплик через ReplicaSet. Когда вы меняете spec.template (например, образ контейнера, переменные окружения, resource limits и т.д.), Deployment трактует это как новую версию приложения и начинает процесс обновления, чтобы привести фактическое состояние к желаемому без ручного удаление/создания Pod'ов.
Что происходит при изменении шаблона Pod (PodTemplate)
-
kubectl/API-сервер записывает обновлённый Deployment в etcd.
-
Контроллер deployment (в controller-manager) видит изменение шаблона и создаёт новый ReplicaSet с этим шаблоном. У нового RS появляется своя аннотация/ревизия (deployment.kubernetes.io/revision) и метка pod-template-hash.
-
Начинается стратегия обновления — по умолчанию RollingUpdate, но может быть Recreate. Контроллер будет постепенно увеличивать количество Pod-ов в новом RS и уменьшать количество Pod-ов в старом RS согласно правилам стратегии.
RollingUpdate — как именно контроллер меняет Pod'ы
RollingUpdate контролируется двумя параметрами в spec.strategy.rollingUpdate:
-
maxSurge — сколько дополнительных Pod'ов (в абсолютном числе или процентах) можно создать выше желаемого replicas во время обновления.
-
maxUnavailable — сколько Pod'ов может быть одновременно недоступно (неготовых) во время обновления.
Пример расчёта (replicas=10, maxSurge=2, maxUnavailable=1):
-
В любой момент число Pod-ов = старые + новые ≤ replicas + maxSurge = 12.
-
Обеспечивается, что доступных Pod-ов (готовых по readiness) ≥ replicas - maxUnavailable = 9.
Алгоритм итеративный: контроллер поднимает новые Pod'ы (в пределах maxSurge), ждёт их готовности (readiness), затем уменьшает старые Pod'ы, и повторяет, пока все старые не заменятся новыми.
Контроллер использует статус ReplicaSet'ов и Pod'ов: считает сколько новых Pod'ов создано, сколько уже Ready, и сколько старых ещё осталось — и действует так, чтобы не нарушить условия maxSurge/maxUnavailable.
Recreate vs RollingUpdate
-
Recreate: сначала контроллер остановит (удалит) все старые Pod'ы, а затем создаст новые. Приводит к простою, но полезен, если нельзя держать одновременно старые и новые экземпляры (например, при миграции состояния).
-
RollingUpdate (по умолчанию): обеспечивает непрерывность, заменяя Pod'ы постепенно.
Что учитывается при принятии решения о продвижении обновления
-
Readiness probe: Pod считается готовым только после прохождения readiness; контроллер ждёт этого перед удалением старых экземпляров. Неправильно настроенная probe — частая причина «застревания» rollout.
-
Liveness и startup probes: влияют на рестарты контейнера, но для rollout важна именно готовность.
-
Resource requests / scheduling: если нет места на нодах, новые Pod'ы будут в Pending и rollout не двинется вперёд.
-
ImagePullBackOff / CrashLoopBackOff: если новые контейнеры не запускаются, прогресс останавливается и rollout может закончиться с ошибкой.
Контроль прогресса и ошибки
-
progressDeadlineSeconds (типично 600s по умолчанию) — если за этот период Deployment не достигает прогресса (новые Pods не становятся Ready), контроллер пометит Deployment как Progressing=False с причиной ProgressDeadlineExceeded.
-
revisionHistoryLimit — сколько старых ReplicaSet хранить для отката (rollback). По умолчанию обычно 10. Старые RS удаляются по мере старения, чтобы ограничить хранение истории.
Пауза, возобновление и откат
-
Обновление можно поставить на паузу (pause) — контроллер остановит продвижение обновления, это даёт время вручную проверить состояние или внести дополнительные изменения.
-
После resume продвижение продолжается.
-
Откат (rollback) реализуется через восстановление предыдущего шаблона — контроллер создаёт новый ReplicaSet, копирующий шаблон старой ревизии, и запускает аналогичный RollingUpdate в обратную сторону. Для отката нужна сохранённая ревизия (не стертая revisionHistoryLimit).
Технические детали и нюансы
-
Каждый новый уникальный PodTemplate приводит к созданию отдельного ReplicaSet; имена ReplicaSet'ов включают hash шаблона (pod-template-hash) для различения.
-
Selector Deployment (label selector) обычно неизменяем; это гарантирует, что контроллер управляет постоянным набором Pod'ов.
-
Контроллер использует «adoption»/«orphan» логику для захвата существующих ReplicaSet/Pod'ов при необходимости, но это требует совпадения селекторов/меток.
-
Для специальных стратегий развёртывания (canary, blue/green, A/B) нативный Deployment даёт базовую функциональность (rolling), но часто используют дополнительные инструменты или манипуляции с сервисами/Ingress, чтобы направлять процент трафика на новую версию.
Поведение при проблемах
-
Если новые Pod'ы не становятся Ready — rollout останавливается, Deployment остаётся в состоянии ожидающего прогресса. Нельзя «пройти» проверку просто потому, что контейнер запущен: важна readiness.
-
Для аварийного принудительного перезапуска можно изменить аннотацию в spec.template.metadata (например, добавить timestamp) — это создаст новую ревизию и запустит заново обновление.