Learning Platform
Глоссарий Troubleshooting
Урок 17.03 · 18 мин
Средний
RecreatedowntimeRWO PVCschema migrationDeployment strategy

Recreate strategy: атомарная замена с downtime

Recreate — вторая (и последняя) стратегия для Deployment. Полная противоположность RollingUpdate: вместо постепенной замены, controller сначала убивает ВСЕ старые Pods (scale to 0), и только потом создаёт ВСЕ новые. Между этими двумя моментами нет ни одного работающего Pod-а — это и есть downtime.

Звучит как regression — зачем добровольно соглашаться на downtime, когда есть RollingUpdate без него. Ответ: в реальности есть классы приложений, где две версии одновременно ломают всё больше, чем минута простоя. Этот урок — про то, когда Recreate — единственный правильный выбор.


Postgres в Docker: миграция версий и restart

YAML: переключение стратегии

apiVersion: apps/v1
kind: Deployment
metadata:
  name: web
spec:
  replicas: 3
  strategy:
    type: Recreate   # вместо RollingUpdate
  # rollingUpdate секция отсутствует — для Recreate она невалидна
  selector:
    matchLabels:
      app: web
  template:
    metadata:
      labels:
        app: web
    spec:
      containers:
        - name: app
          image: my-app:v1

Ключевое:

  • spec.strategy.type: Recreate — единственное поле
  • Нет rollingUpdate подсекции — maxSurge и maxUnavailable не применимы
  • Если оставить rollingUpdate поле + type: Recreate — API server отклонит или применит, но проигнорирует

Что делает controller при Recreate

Recreate flow: kill all → create all
step 1Изменение spec.template (например, новый image). DeploymentController создаёт новый ReplicaSet с replicas=0. Старый RS пока не трогается, держит свои Pod-ы.
scale old RS to 0
step 2Controller scaled старый RS с N до 0 — все старые Pods получают SIGTERM. terminationGracePeriodSeconds (default 30) — окно для graceful shutdown. Каждый Pod проходит свой preStop hook, потом SIGKILL.
DOWNTIME starts
downtime window0 Pods в Ready. Service не имеет endpoints → запросы возвращают connection refused / timeout. Длительность зависит от того, как быстро controller замечает что старые Pods ушли, плюс время создания и инициализации новых.
scale new RS to desired
step 3После того как Available count старого RS = 0, controller scales новый RS до desired (replicas=N). Все новые Pods создаются параллельно (а не один за одним). Image pull, init containers, main container start, readiness probe.
downtime ends when first Ready
step 4Как только хотя бы один новый Pod становится Ready, он добавляется в EndpointSlice → Service начинает балансировать на него. Downtime закончился. Дальше: больше Pods Ready, полная пропускная способность восстановлена.

Длительность downtime обычно:

  • ~30 секунд (termination grace) для shutdown
    • N секунд на pull нового image, если не закэширован
    • initialDelaySeconds + время readiness probe

В типичных случаях 30 секунд — 2 минуты простоя.

WARNING

В отличие от RollingUpdate, у Recreate нет maxSurge и maxUnavailable. По сути это эквивалентно “maxSurge=0, maxUnavailable=100%” — фактически весь Pod-set может стать unavailable. Но реализация в controller — отдельная: он явно ждёт пока Available count старого RS = 0, прежде чем скейлить новый.


Use cases: когда Recreate необходим

1. Schema migrations без backward-compatibility

# Old version: читает column `name`
# New version: переименовала `name` -> `full_name` в DB schema

# Если обе версии работают одновременно:
# - Old инстансы пишут в `name` (которой больше нет) → 500
# - New инстансы читают `full_name` (которой ещё нет в applied migration) → 500

С RollingUpdate в момент перехода обе версии работают одновременно — обе ломаются. С Recreate:

  1. Pre-Recreate: pre-stop hook или Job выполняет migration (renames column)
  2. Recreate: все старые Pods убиты (они уже не понимают новую schema, но они и не активны)
  3. New Pods стартуют, работают с новой schema

В Helm это паттерн pre-upgrade Job → Recreate Deployment.

2. RWO PVC с одним Pod

spec:
  replicas: 1
  strategy:
    type: Recreate   # для RollingUpdate был бы deadlock
  template:
    spec:
      volumes:
        - name: data
          persistentVolumeClaim:
            claimName: app-data   # accessMode: ReadWriteOnce
      containers:
        - name: app
          image: my-app:v1
          volumeMounts:
            - name: data
              mountPath: /data

RWO PVC может быть смонтирован на ТОЛЬКО ОДНОЙ ноде одновременно. При RollingUpdate controller хочет создать новый Pod (с тем же PVC) ДО того как убил старый. На той же ноде это работает, но если scheduler отправит новый Pod на другую ноду — PVC mount failed:

Warning  FailedAttachVolume  pod/web-new ...
Multi-Attach error for volume "pvc-xxx": Volume is already exclusively attached to one node

С Recreate: старый Pod убит, PVC detach-ится, новый Pod может смонтировать на любой ноде. Без deadlock.

NOTE

StatefulSet решает эту проблему иначе — через OnDelete или RollingUpdate updateStrategy, который удаляет Pod-ы по одному с гарантией identity. Для Deployment с RWO PVC — единственный sane вариант это replicas=1 + Recreate.

3. Single-writer apps (leader без gating)

Приложения, которые требуют эксклюзивности и не имеют встроенного leader election:

  • Старый PostgreSQL master без HA-конфига
  • Legacy job processors с file lock
  • In-memory state без shared storage

Если две версии одновременно запустят leader-ловлю или начнут писать в один файл — повреждение данных. Recreate гарантирует атомарность смены.


Recreate + readiness probe

Даже с Recreate readiness probe важна. Без неё:

  • Старые убиты → 0 Pods
  • Новые создаются → Phase=Running, но приложение ещё не готово
  • Service сразу добавляет Pod в endpoints (нет readiness gate)
  • Клиенты получают 500/connection refused, пока приложение инициализируется

С readiness probe Pod добавляется в Service только когда /healthz отвечает 200. Downtime удлиняется, но trafic не идёт на не-готовый Pod.

containers:
  - name: app
    image: my-app:v1
    readinessProbe:
      httpGet:
        path: /healthz
        port: 8080
      initialDelaySeconds: 5
      periodSeconds: 3

Recreate vs StatefulSet updateStrategy

Это разные механизмы, путать нельзя:

  • Deployment + Recreatespec.strategy.type: Recreate. Все Pod-ы убиваются параллельно, потом все создаются параллельно. Identity Pod-ов меняется (новые имена с другим pod-template-hash).
  • StatefulSet + RollingUpdate (default) — Pod-ы обновляются по одному, начиная с самого высокого ordinal (web-2 → web-1 → web-0). Каждый Pod сохраняет своё имя и identity.
  • StatefulSet + OnDelete — controller вообще не делает rollout. Update template только меняет spec.template, но Pod-ы не пересоздаются. Админ вручную удаляет Pod-ы, и при создании контроллер использует новый template.
# StatefulSet
spec:
  updateStrategy:
    type: RollingUpdate    # или OnDelete
    rollingUpdate:
      partition: 0          # ordinals >= partition обновляются (canary-like)

# Deployment
spec:
  strategy:
    type: Recreate          # или RollingUpdate
    # rollingUpdate секция (maxSurge, maxUnavailable) — для RollingUpdate
DANGER

На CKAD ловушка: “convert deployment to atomically update” — это про Deployment.strategy.type=Recreate. “Stateful application identity matters” — это про StatefulSet с другим updateStrategy. Перепутать = неправильный объект.


CKAD сценарий: convert RollingUpdate → Recreate

Задание: Deployment web использует RollingUpdate. Изменить стратегию на Recreate, обосновать.

# Edit live
kubectl edit deploy/web
# или patch
kubectl patch deploy/web -p '{"spec":{"strategy":{"$retainKeys":["type"],"type":"Recreate"}}}'

Важно: при switching из RollingUpdate в Recreate нужно явно удалить rollingUpdate подсекцию (с maxSurge/maxUnavailable). Иначе:

# Неправильно (после edit):
spec:
  strategy:
    type: Recreate
    rollingUpdate:           # ← оставшийся мусор
      maxSurge: 25%
      maxUnavailable: 25%

API-сервер примет конфигурацию, но controller будет использовать тип Recreate (значения rollingUpdate проигнорированы). Лучше — чисто:

spec:
  strategy:
    type: Recreate

Через kubectl patch с $retainKeys это делается атомарно.


Trade-off summary

АспектRollingUpdateRecreate
DowntimeNoneYes (30s — 2min typ.)
Resource overhead+maxSurge replicasNone
Rollout speedSlow (postponed by maxSurge/Ready)Fast (parallel)
Two versions liveYes (during rollout)No (atomically)
Use caseStateless apps defaultSchema migrations, RWO, single-writer

Killer-моменты

  • Recreate = atomically replace. Полный downtime гарантирован — нет момента когда обе версии живы.
  • maxSurge/maxUnavailable не применимы. Если присутствуют в YAML с type=Recreate — игнорируются.
  • RWO PVC + Deployment: только Recreate. RollingUpdate упадёт на Multi-Attach error при scheduling нового Pod на другую ноду.
  • Recreate != StatefulSet OnDelete/RollingUpdate. Это разные объекты с разным lifecycle. StatefulSet — для stateful identity, Deployment Recreate — для stateless с atomicity.
  • Helm pre-upgrade hooks + Recreate — типовой паттерн для schema migration: pre-upgrade Job выполняет migration, потом Recreate Deployment запускает новые Pods с новой schema.

Проверка знанийKnowledge check
Deployment с replicas=1, PVC accessMode=ReadWriteOnce, strategy=RollingUpdate, maxSurge=1, maxUnavailable=0. Что произойдёт при обновлении image, если scheduler решит запустить новый Pod на ДРУГОЙ ноде?
ОтветAnswer
Deadlock. Controller хочет создать новый Pod (surge=1, всего 2 Pods), но он попадает на другую ноду. PVC уже attached к старой ноде (RWO = только одна нода). Новый Pod в Pending: 'Multi-Attach error for volume: Volume is already exclusively attached to one node'. Controller не может убить старый Pod (maxUnavailable=0), новый Pod не может стартануть → rollout висит до progressDeadlineSeconds. Лекарство: переключить на Recreate (старый Pod убивается первым, PVC detach-ится, новый монтирует) или явно nodeAffinity к одной ноде. Production: для RWO + Deployment всегда Recreate.
Проверка знанийKnowledge check
Чем Deployment с strategy=Recreate отличается от StatefulSet с updateStrategy=RollingUpdate?
ОтветAnswer
(1) Identity: Deployment Recreate — Pod-ы меняют имя (новый pod-template-hash в каждой ревизии, web-aaa12 -> web-bbb34). StatefulSet — Pod-ы сохраняют имена (web-0, web-1, web-2 при любых update). (2) Order: Deployment Recreate — все Pods убиваются параллельно, все создаются параллельно (downtime). StatefulSet RollingUpdate — один Pod за раз, от высшего ordinal к низшему (web-2 → web-1 → web-0), без downtime если репликаций > 1. (3) Use case: Recreate — для stateless с atomicity (single-writer, RWO). StatefulSet — для stateful с identity (databases, Kafka brokers). Это разные объекты Kubernetes, не разные значения одного поля.
Проверка знанийKnowledge check
Почему `pre-upgrade` Job в Helm + Recreate Deployment — типовой паттерн для schema migration?
ОтветAnswer
Schema migration — это операция, которую нельзя выполнить во время работы старой версии (она ещё пишет в старую schema) или новой (она ожидает новую schema). Решение: (1) Helm pre-upgrade hook запускает Job со специальным образом, который выполняет миграцию (alembic upgrade head, knex migrate latest и т.д.). (2) Job завершается успешно → Helm продолжает upgrade. (3) Deployment strategy=Recreate → старые Pods убиваются (они в этот момент уже не могут работать корректно с новой schema), новые Pods создаются с новым кодом, который понимает новую schema. RollingUpdate здесь сломает всё: некоторое время старые Pods (стара schema) и новые (новая schema) сосуществуют, оба возвращают 500-ки на половине запросов.
Проверка знанийKnowledge check
После выполнения kubectl patch deploy/web с указанием только spec.strategy.type=Recreate в YAML деплоймента остался блок rollingUpdate с maxSurge 25 процентов и maxUnavailable 25 процентов. Что будет с rollout-ом?
ОтветAnswer
Rollout будет Recreate-стилем — controller использует spec.strategy.type для решения, какую логику применить. Значения rollingUpdate.maxSurge и rollingUpdate.maxUnavailable игнорируются, когда type=Recreate. API-сервер обычно принимает такой YAML, но это код-смелл. Чистый patch использует strategic-merge с $retainKeys, который говорит удалить все остальные поля strategy (включая rollingUpdate). После — YAML чист: strategy.type=Recreate без лишнего. На CKAD-проверке часто смотрят на kubectl get deploy -o yaml — оставшийся rollingUpdate выглядит как ошибка задания.

Проверьте понимание

Результат: 0 из 0
Аналитический
Вопрос 1 из 5. Deployment с RWO PVC, replicas=1, strategy=RollingUpdate, maxSurge=1, maxUnavailable=0. Меняем image. Scheduler разместил новый Pod на ДРУГОЙ ноде. Что произойдёт?

Закончили урок?

Отметьте его как пройденный, чтобы отслеживать свой прогресс

Войдите чтобы оценить урок

Прогресс модуля
0 из 5