← Kubernetes: рабочие нагрузки
7 мин · средне · Урок 3 из 11

Rolling update: обновление без простоя

Как Deployment переливает трафик со старого ReplicaSet на новый, что делают maxSurge и maxUnavailable, откуда берутся ревизии.

Что делает Deployment при обновлении

Deployment - объект Kubernetes, который держит нужное число одинаковых Pod. Pod - наименьшая единица, которую запускает Kubernetes, внутри неё работает контейнер. ReplicaSet - объект, который следит, чтобы число Pod совпадало с заданным.

Deployment хранит шаблон Pod в поле spec.template. Шаблон меняется: новый образ, новые env, новые resources. После этого Deployment создаёт новый ReplicaSet и запускает плавный переход.

Старый ReplicaSet постепенно сжимается до нуля Pod. Новый растёт до нужного числа реплик. Этот плавный переход и называют rolling update.

RS старый (v1)  ●●● → ···  3 → 0 Pod
RS новый  (v2)  ··· → ●●●  0 → 3 Pod

Поведением перехода управляет блок spec.strategy:

spec:
  strategy:
    type: RollingUpdate
    rollingUpdate:
      maxSurge: 25%
      maxUnavailable: 25%

maxSurge - сколько Pod сверх replicas разрешено поднять на время перехода. При replicas: 3 и 25% получится один лишний Pod.

maxUnavailable - сколько Pod из старого набора разрешено выключить до подъёма новых. При нуле новые Pod поднимаются первыми, и только потом гасятся старые.

По умолчанию оба значения равны 25%. На тесном кластере ставят maxSurge: 0, чтобы не плодить лишние Pod. Если приложению важна доступность, ставят maxUnavailable: 0, тогда новые Pod поднимаются раньше выключения старых.

ПолеЧто разрешаетЗначение 0
maxSurgeлишние Pod сверх replicasни одного лишнего Pod, переход медленнее
maxUnavailableнедостающие Pod из старого наборасначала поднять новый, потом гасить старый

Вторая стратегия - Recreate. Она сначала убивает все старые Pod, потом поднимает все новые. Здесь гарантирован короткий простой. Она нужна приложениям, которые не работают в двух версиях одновременно.

kubectl set image

kubectl - командная утилита для управления Kubernetes. Поменять образ можно через правку YAML и kubectl apply. В проде чаще делают это одной командой:

kubectl set image deploy/app app=nginx:1.27

Слева от = имя контейнера в Deployment, справа новый образ. Команда правит spec.template, и rolling update запускается сразу.

Дождаться окончания перехода:

kubectl rollout status deploy/app

Команда вернёт нулевой код выхода, когда все реплики станут Ready на новом образе.

Если указать тег, которого нет в registry, kubelet не сможет скачать образ. Новый Pod застрянет в статусе ErrImagePull, потом ImagePullBackOff, и rollout status будет ждать его Ready бесконечно. При maxUnavailable: 0 старые Pod продолжают обслуживать трафик, так что простоя нет, но переход не завершится, пока образ не починят или не откатятся назад.

Чтобы зафиксировать причину обновления, добавляют annotation. Annotation - произвольная пометка на объекте в формате ключ-значение.

kubectl annotate deploy/app kubernetes.io/change-cause="upgrade to 1.27"

Эта annotation попадёт в rollout history в колонку CHANGE-CAUSE.

rollout history

Команда kubectl rollout history deploy/app показывает все ревизии. Ревизия - сохранённая версия шаблона Pod на момент изменения.

REVISION  CHANGE-CAUSE
1         <none>
2         upgrade to 1.27

За каждой ревизией стоит отдельный ReplicaSet. Сам Deployment ссылается на текущую через annotation deployment.kubernetes.io/revision.

Сколько ReplicaSet остаётся в истории, задаёт поле spec.revisionHistoryLimit. По умолчанию это 10. При значении 0 история стирается, и откатить будет некуда.

Посмотреть полный шаблон конкретной ревизии:

kubectl rollout history deploy/app --revision=2

Команда покажет образ, env, ports для этой ревизии без отката.

Откат в двух словах

Если новая версия оказалась битой, переход разворачивают обратно:

kubectl rollout undo deploy/app
kubectl rollout undo deploy/app --to-revision=1

Без аргументов команда вернёт ревизию N-1. После отката номер ревизии не повторяется. Откатились с revision=2 на revision=1 - в истории появится revision=3 с тем же шаблоном, что был у revision=1. Номер всегда растёт, история остаётся линейной. Что при этом происходит с ReplicaSet под капотом, разбирает следующий урок.

Полезные команды

# Что есть прямо сейчас
kubectl get deploy app -o wide
kubectl get rs -l app=app

# Текущий образ
kubectl get deploy app -o jsonpath='{.spec.template.spec.containers[0].image}'

# История
kubectl rollout history deploy/app

Домашка

Теорию закрепляет задание на своём кластере: 3 шага с проверкой. Кластер поднимается по треку Kubernetes локально, задания лежат в репозитории k8s-practice.

./run.sh start k8s-rolling-update     # развернуть стартовое состояние
./run.sh verify k8s-rolling-update    # проверить текущий шаг

Коротко

Прошли материал до конца? Отметьте урок пройденным.