Kube/Pythonproduction course
Курс · RU07-rollouts-scaling-and-availability.md

07. Развёртывания, масштабирование и доступность

Незнакомый термин? Откройте словарь Kubernetes и терминов курса.

Назначение, предварительные знания и результаты обучения

Нужны главы 00–06, kind с тремя узлами и заново собранный образ после изменений приложения. После главы вы сможете:

  • предсказать требуемую ёмкость RollingUpdate через maxSurge/maxUnavailable;
  • читать status и историю rollout и откатывать ревизию;
  • безопасно приостанавливать и возобновлять Deployment;
  • объяснить добровольные и недобровольные нарушения доступности и PDB;
  • настроить поле behavior HPA autoscaling/v2;
  • сравнить HPA, VPA и масштабирование узлов;
  • провести имитацию drain и восстановить возможность планирования.

Ментальная модель: доступность обеспечивают несколько согласованных циклов

Контроллер Deployment меняет ReplicaSets, HPA — желаемое число реплик, scheduler размещает Pods, PDB ограничивает eviction, а autoscaler узлов меняет ёмкость. Эти механизмы независимы и могут конфликтовать:

HPA → Deployment.spec.replicas → ReplicaSet → Pods
                     стратегия rollout ↗       ↘ scheduler → узлы
PDB → бюджет Eviction API                         autoscaler узлов → ёмкость

maxSurge — сколько Pods сверх желаемого числа допускается во время rollout. maxUnavailable — сколько желаемых реплик может быть недоступно. Для трёх реплик, maxSurge: 1, maxUnavailable: 0: всего может быть до четырёх Pods, старый Pod удаляется только после готовности нового. Нужен запас ёмкости, иначе rollout может зависнуть.

Ревизия создаётся при изменении шаблона Pod (.spec.template), а не при простом масштабировании. minReadySeconds требует устойчивой readiness перед состоянием Available. progressDeadlineSeconds помечает остановившийся rollout, но не откатывает его автоматически.

Полные манифесты доступности

PDB

apiVersion: policy/v1
kind: PodDisruptionBudget
metadata:
  name: python-api
  namespace: kube-course
  labels:
    app.kubernetes.io/name: python-api
spec:
  maxUnavailable: 1
  selector:
    matchLabels:
      app.kubernetes.io/name: python-api
      app.kubernetes.io/instance: course

PDB ограничивает добровольный eviction через Eviction API (drain, некоторые действия обслуживания и autoscaler). Он не предотвращает отказ узла, OOM, прямое удаление Pod или Deployment и не создаёт реплики. PDB с одной репликой и minAvailable: 1 может навсегда заблокировать drain без согласованного исключения.

HPA

apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
  name: python-api
  namespace: kube-course
  labels:
    app.kubernetes.io/name: python-api
spec:
  scaleTargetRef:
    apiVersion: apps/v1
    kind: Deployment
    name: python-api
  minReplicas: 3
  maxReplicas: 6
  behavior:
    scaleUp:
      stabilizationWindowSeconds: 0
      policies:
        - type: Percent
          value: 100
          periodSeconds: 60
        - type: Pods
          value: 2
          periodSeconds: 60
      selectPolicy: Max
    scaleDown:
      stabilizationWindowSeconds: 300
      policies:
        - type: Percent
          value: 25
          periodSeconds: 60
  metrics:
    - type: Resource
      resource:
        name: cpu
        target:
          type: Utilization
          averageUtilization: 70

Утилизация CPU — фактическое потребление относительно запроса CPU, поэтому отсутствующие или неверные запросы искажают смысл. Приближённая формула:

desiredReplicas = ceil(currentReplicas × currentMetric / desiredMetric)

Контроллер применяет допуск, учитывает отсутствующие метрики и неготовые Pods, а также политики поведения, поэтому результат не всегда буквально совпадает с формулой. Стабилизация уменьшения защищает от колебаний.

Стратегии развёртывания

RollingUpdate — стратегия по умолчанию, при которой обе версии временно работают одновременно; приложение, схема и протокол должны быть обратно совместимы. Recreate удаляет старую версию перед новой, создаёт простой и подходит только тогда, когда перекрытие опасно. Сине-зелёная схема и canary обычно реализуются несколькими Deployments вместе с Service, Gateway, сервисной сеткой или специализированным контроллером: это шаблоны и дополнения, а не отдельные значения стратегии базового Deployment.

Безопасная последовательность для БД: расширить схему → развернуть совместимый код → перенести и дозаполнить данные → позже сузить контракт. Откат образа в Kubernetes не отменяет разрушающую миграцию БД.

Практическое упражнение: успешный и неудачный rollout

Подготовка

kubectl apply -f manifests/base/configmap.yaml
kubectl apply -f manifests/base/secret.example.yaml
kubectl apply -f manifests/base/serviceaccount.yaml
kubectl apply -f manifests/base/deployment.yaml
kubectl apply -f manifests/base/service.yaml
kubectl apply -f manifests/base/pdb.yaml
kubectl rollout status deployment/python-api -n kube-course --timeout=180s
kubectl rollout history deployment/python-api -n kube-course

Запишите ревизию и образ:

kubectl get deployment python-api -n kube-course \
  -o jsonpath='{.metadata.annotations.deployment\.kubernetes\.io/revision}{" "}{.spec.template.spec.containers[0].image}{"\n"}'

Управляемый rollout конфигурации

kubectl annotate deployment python-api -n kube-course \
  course.example.com/release-note="config-2026-07-29" --overwrite

Аннотация в metadata Deployment не меняет шаблон и не создаёт ревизию. Теперь:

kubectl patch deployment python-api -n kube-course --type merge \
  -p '{"spec":{"template":{"metadata":{"annotations":{"course.example.com/config-checksum":"lab-v2"}}}}}'
kubectl rollout status deployment/python-api -n kube-course --timeout=180s
kubectl rollout history deployment/python-api -n kube-course

Ревизия увеличилась, потому что изменился шаблон.

Неудачный rollout образа и rollback

kubectl set image deployment/python-api -n kube-course \
  api=registry.invalid.example/python-api:9.9.9
kubectl rollout status deployment/python-api -n kube-course --timeout=45s

Ожидаются тайм-аут и ненулевой код завершения; старые готовые Pods остаются благодаря maxUnavailable: 0, новый Pod получает ImagePullBackOff. Соберите доказательства:

kubectl get rs,pods -n kube-course -l app.kubernetes.io/instance=course
kubectl describe deployment python-api -n kube-course
kubectl get events -n kube-course --sort-by=.metadata.creationTimestamp |
  tail -n 20
kubectl rollout history deployment/python-api -n kube-course

Откат:

kubectl rollout undo deployment/python-api -n kube-course
kubectl rollout status deployment/python-api -n kube-course --timeout=180s
kubectl get deployment python-api -n kube-course \
  -o jsonpath='{.spec.template.spec.containers[0].image}{"\n"}'

Ожидаются kube-python-course:1.0.0 и три готовых Pod. undo --to-revision=N требует проверить историю; не угадывайте номер.

Приостановка полезна, чтобы сгруппировать изменения шаблона:

kubectl rollout pause deployment/python-api -n kube-course
kubectl set env deployment/python-api -n kube-course FEATURE_COLOR=violet
kubectl set env deployment/python-api -n kube-course APP_VERSION=1.0.1
kubectl rollout resume deployment/python-api -n kube-course
kubectl rollout status deployment/python-api -n kube-course --timeout=180s

В процессе GitOps вносите изменения в исходные манифесты, а не оставляйте императивное расхождение в работающем кластере.

Лабораторная работа с HPA

Metrics Server — дополнение. Для kind установите закреплённую версию v0.8.1:

kubectl apply -f \
  https://github.com/kubernetes-sigs/metrics-server/releases/download/v0.8.1/components.yaml
kubectl patch deployment metrics-server -n kube-system --type=json \
  -p='[{"op":"add","path":"/spec/template/spec/containers/0/args/-","value":"--kubelet-insecure-tls"}]'
kubectl rollout status deployment/metrics-server -n kube-system --timeout=180s
kubectl top nodes
kubectl top pods -n kube-course

--kubelet-insecure-tls допустим только в одноразовой лаборатории из-за сертификатов kubelet в kind; не переносите его в эксплуатационную среду. Если kubectl top не возвращает метрики, проверка HPA заблокирована: исследуйте APIService и журналы metrics-server, не заявляйте ложный успех.

kubectl apply -f manifests/base/hpa.yaml
kubectl get hpa python-api -n kube-course -w

Создайте Pod, генерирующий нагрузку внутри кластера:

kubectl apply -f manifests/labs/hpa-load.yaml

Манифест задаёт SecurityContext без root и ресурсы для политики restricted. Через несколько циклов метрик:

kubectl get hpa python-api -n kube-course
kubectl get deployment python-api -n kube-course
kubectl top pods -n kube-course -l app.kubernetes.io/instance=course

Ожидается, что при длительной нагрузке CPU число реплик вырастет не выше шести. После этого:

kubectl delete pod hpa-load -n kube-course --ignore-not-found

Уменьшение масштаба ждёт 300 секунд. Пока существует HPA, не храните фиксированное .spec.replicas в постоянно применяемом манифесте: GitOps и HPA будут спорить.

PDB и имитация drain

Убедитесь, что три Pods находятся в Ready, а выбранный рабочий узел содержит хотя бы один из них:

kubectl get pods -n kube-course -l app.kubernetes.io/instance=course -o wide
kubectl get pdb python-api -n kube-course
kubectl drain kube-course-worker \
  --ignore-daemonsets \
  --delete-emptydir-data \
  --timeout=180s

Drain помечает узел как недоступный для планирования и выполняет eviction. PDB допускает максимум одну недоступную реплику; контроллер создаёт замены на другом рабочем узле. Данные emptyDir удаляются — флаг делает риск явным. При тайм-ауте drain узел может остаться SchedulingDisabled; восстановление обязательно:

kubectl uncordon kube-course-worker
kubectl rollout status deployment/python-api -n kube-course --timeout=180s
kubectl get nodes
kubectl get pdb python-api -n kube-course

Не используйте --disable-eviction или принудительный режим, пока цель лабораторной — проверить PDB.

VPA и автоматическое масштабирование узлов

VPA — дополнение из контроллера и CRDs, которое рекомендует или меняет requests; режим обновления может выселять и пересоздавать Pods. VPA и HPA, использующие одну метрику CPU или памяти, могут конфликтовать: VPA меняет знаменатель, HPA — число реплик. Распространённое решение: VPA в режиме рекомендаций или без обновлений и HPA по трафику или пользовательской метрике.

Autoscaler узлов добавляет или удаляет ёмкость из-за непланируемых Pods, утилизации и политик; в локальном kind нет облачного провайдера ёмкости. HPA не создаёт узлы. Cluster Autoscaler не спасает Pod с невыполнимым ограничением, непривязанным PVC или квотой. Масштабирование до нуля и по событиям обычно требует отдельного дополнения.

Самостоятельная задача

Предскажите максимальное общее и доступное число Pods при четырёх репликах, maxSurge: 25%, maxUnavailable: 25%; наблюдайте за rollout. Затем создайте PDB minAvailable: 100% и покажите, почему drain блокируется. Восстановите узел и удалите слишком строгий PDB. Критерий приёмки: округление и Events объяснены.

Дерево диагностических решений

Rollout не завершён
├─ новый ReplicaSet не создаётся → недопустимый шаблон/admission/контроллер
├─ новые Pods Pending → нет резервной ёмкости/affinity/запросов ресурсов/PVC
├─ ImagePullBackOff/CrashLoop → данные образа/конфигурации/процесса
├─ Running, но не Ready → проверка/приложение/зависимость
└─ старые Pods не уходят → доступность новых/minReady/PDB? (удаление rollout — не всегда eviction)

HPA не масштабирует
├─ TARGETS <unknown> → отсутствуют API метрик/сервер/запрос ресурсов
├─ метрика ниже цели → масштабирование не нужно; проверить фактическую нагрузку
├─ достигнуты максимум/минимум → границы
├─ `behavior`/стабилизация `scaleUp` → окно политики
└─ Pods Pending после масштабирования → ёмкость/scheduler; цикл HPA работает

Drain заблокирован
├─ PDB disruptionsAllowed=0 → реплики/readiness/бюджет
├─ Pod без контроллера → решение об удалении/владельце
├─ emptyDir → явное решение о потере данных
└─ finalizer/отключение тома → данные хранилища/контроллера

Типичные ошибки: успех rollout приравнивают к успеху для бизнеса; rollback выполняют без совместимости БД; PDB имеет невыполнимый minAvailable; для проверки PDB напрямую удаляют Pod; HPA не имеет запросов ресурсов или метрик; HPA и GitOps спорят за число реплик; нет запаса ёмкости при maxUnavailable: 0; узел оставлен закрытым для планирования после неудачного drain.

Эксплуатационные компромиссы, безопасность и стоимость

maxSurge ускоряет безопасный rollout, но требует ёмкости и денег. PDB защищает доступность во время обслуживания, но может блокировать обновление узла; платформе нужна политика исключений и эскалации. HPA снижает перегрузку, но может масштабироваться слишком поздно и усиливать нагрузку на БД и последующие зависимости; ограничения частоты, очереди и сброс нагрузки всё ещё необходимы. Минимальное число реплик определяется SLO и областями отказа, а не средней нагрузкой. Идентичности autoscaler имеют сильные разрешения — применяйте аудит и минимальные привилегии.

Самопроверка

  1. Создаёт ли scale новую ревизию?
  2. Что защищает PDB?
  3. Почему maxUnavailable: 0 может зависнуть?
  4. На чём основана утилизация CPU в HPA?
  5. Создаёт ли HPA узлы?
  6. Откатывает ли Deployment миграцию БД?
  7. Почему после неудачного drain нужен uncordon?

Ответы: 1) нет; 2) доступность при добровольном eviction; 3) нет запаса ёмкости или новая реплика не достигла readiness; 4) потребление относительно запроса ресурсов; 5) нет; 6) нет; 7) drain закрывает узел для планирования даже при последующей ошибке.

Резюме и источники

Доступность — согласованный контракт ёмкости, rollout, здоровья, нарушений и автоматического масштабирования. Далее: безопасность и мультитенантность.