Rollback Deployment
Где Kubernetes хранит историю релизов и что на самом деле делает kubectl rollout undo с ReplicaSet.
Откуда берётся история
Deployment - объект Kubernetes, который держит нужное число одинаковых Pod. Pod - наименьшая единица запуска, внутри неё работает контейнер. ReplicaSet - объект, который следит за числом Pod.
Каждое обновление Deployment создаёт новый ReplicaSet. Старый не
удаляется сразу. Он остаётся в кластере со значением replicas: 0.
Набор таких неактивных ReplicaSet и есть история.
Сколько неактивных ReplicaSet хранится, задаёт поле
spec.revisionHistoryLimit. По умолчанию это 10.
Посмотреть историю:
kubectl rollout history deploy/app
REVISION CHANGE-CAUSE
1 <none>
2 <none>
Каждая запись - отдельный ReplicaSet со своей версией шаблона Pod.
Сам Deployment помнит активную ревизию в annotation
deployment.kubernetes.io/revision. Annotation - произвольная
пометка на объекте в формате ключ-значение.
Посмотреть конкретную ревизию:
kubectl rollout history deploy/app --revision=1
Команда покажет полный шаблон того обновления: образ, env, ports.
Что делает kubectl rollout undo
kubectl - командная утилита для управления Kubernetes. Без
аргументов команда undo откатывает на предыдущую ревизию N-1:
kubectl rollout undo deploy/app
С указанием конкретной ревизии:
kubectl rollout undo deploy/app --to-revision=1
После отката Deployment запускает обычный плавный переход в обратную сторону. Битый ReplicaSet сжимается, целевой растёт.
Глубже: что происходит с ReplicaSet при откате
На практике достаточно того, что выше: undo вернул рабочий образ,
Pod снова Running. Дальше идёт механика под капотом, если интересно,
как именно Deployment хранит ревизии.
Это место путает почти всех. Кажется, что откат создаёт новый ReplicaSet с нуля. На деле Deployment переиспользует уже существующий старый ReplicaSet.
Что происходит по шагам:
- Активен ReplicaSet №2 (revision=2, плохая версия).
- Выполняется
rollout undo. - Deployment копирует шаблон из revision=1 в свой
spec.template. - Hash (контрольная сумма шаблона) от этого шаблона совпадает с hash у ReplicaSet №1 - он ещё лежит в кластере, свёрнутый в ноль. Поэтому Deployment не создаёт новый RS, а масштабирует №1 обратно вверх.
- ReplicaSet №2 сжимается до 0. ReplicaSet №1 растёт до целевых replicas.
- ReplicaSet №1 получает новый номер ревизии: его аннотация
deployment.kubernetes.io/revisionменяется с 1 на 3.
В итоге rollout history покажет revision=2 (плохая) и revision=3
(откат). Revision=1 пропадёт из списка, но RS не удалён: это тот же
самый ReplicaSet №1, просто теперь под номером 3.
Причина в том, как устроен Deployment. Он хранит актуальный
spec.template и hash от него. Новый шаблон даёт новый hash и новый
ReplicaSet. Откат возвращает старый шаблон, hash совпадает с уже
существующим ReplicaSet, и тот переиспользуется.
Проверяют так:
kubectl get rs -l app=app -o jsonpath='{range .items[*]}{.metadata.name}{" "}{.metadata.annotations.deployment\.kubernetes\.io/revision}{"\n"}{end}'
Команда выведет список ReplicaSet с их revision. До отката там будут revision 1 и 2, после отката - 2 и 3.
Можно ли откатить откат
Да. После отката активна revision=3, а в истории осталась revision=2
с плохой версией. Команда kubectl rollout undo снова создаст
revision=4, скопировав шаблон из revision=2. Это вернёт ту самую
плохую версию.
Откатиться на revision=1, если её уже вытеснили из истории, нельзя. Записи больше нет, шаблон не сохранился. ReplicaSet не хранит шаблонов старше тех, что остались в истории.
kubectl rollout status и pause
После отката переход не завершён мгновенно. Идёт плавный переход:
kubectl rollout status deploy/app --timeout=120s
Команда блокируется до завершения. Нулевой код выхода означает, что откат прошёл. Ненулевой - таймаут или ошибку.
Если переход зависает, помогут две команды:
kubectl rollout pause deploy/app # остановить
kubectl rollout resume deploy/app # возобновить
pause замораживает текущее состояние и не даёт контроллеру
запускать новые переходы. Это удобно при отладке.
Какая ревизия активна прямо сейчас:
kubectl get deploy app -o jsonpath='{.metadata.annotations.deployment\.kubernetes\.io/revision}'
Домашка
Теорию закрепляет задание на своём кластере: 3 шага с проверкой. Кластер поднимается по треку Kubernetes локально, задания лежат в репозитории k8s-practice.
./run.sh start k8s-rollback # развернуть стартовое состояние
./run.sh verify k8s-rollback # проверить текущий шаг
Коротко
- История ревизий - это старые ReplicaSet, свёрнутые в
replicas: 0. revisionHistoryLimit(дефолт 10) определяет глубину отката.undoне создаёт новый ReplicaSet, а поднимает обратно старый с совпавшим hash шаблона.- Номера ревизий только растут: откат на 1 даёт запись 3, не 1.
rollout status --timeoutдаёт ненулевой код, если откат не стабилизировался.
Прошли материал до конца? Отметьте урок пройденным.
Прогресс сохранён в этом браузере
Почистишь историю или откроешь с телефона, и отметки пропадут. Оставь почту, они переедут в аккаунт. Пароль придумывать не нужно.
Готово. теперь в аккаунте: прогресс виден с любого устройства. Мой прогресс