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
Длительность downtime обычно:
- ~30 секунд (termination grace) для shutdown
-
- N секунд на pull нового image, если не закэширован
-
- initialDelaySeconds + время readiness probe
В типичных случаях 30 секунд — 2 минуты простоя.
В отличие от 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:
- Pre-Recreate: pre-stop hook или Job выполняет migration (renames column)
- Recreate: все старые Pods убиты (они уже не понимают новую schema, но они и не активны)
- 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.
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 + Recreate —
spec.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
На 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
| Аспект | RollingUpdate | Recreate |
|---|---|---|
| Downtime | None | Yes (30s — 2min typ.) |
| Resource overhead | +maxSurge replicas | None |
| Rollout speed | Slow (postponed by maxSurge/Ready) | Fast (parallel) |
| Two versions live | Yes (during rollout) | No (atomically) |
| Use case | Stateless apps default | Schema 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.