07. Развёртывания, масштабирование и доступность
Незнакомый термин? Откройте словарь Kubernetes и терминов курса.
Назначение, предварительные знания и результаты обучения
Нужны главы 00–06, kind с тремя узлами и заново собранный образ после изменений приложения. После главы вы сможете:
- предсказать требуемую ёмкость RollingUpdate через
maxSurge/maxUnavailable; - читать
statusи историю rollout и откатывать ревизию; - безопасно приостанавливать и возобновлять Deployment;
- объяснить добровольные и недобровольные нарушения доступности и PDB;
- настроить поле
behaviorHPAautoscaling/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 имеют сильные разрешения — применяйте аудит
и минимальные привилегии.
Самопроверка
- Создаёт ли
scaleновую ревизию? - Что защищает PDB?
- Почему
maxUnavailable: 0может зависнуть? - На чём основана утилизация CPU в HPA?
- Создаёт ли HPA узлы?
- Откатывает ли Deployment миграцию БД?
- Почему после неудачного drain нужен
uncordon?
Ответы: 1) нет; 2) доступность при добровольном eviction; 3) нет запаса ёмкости или новая реплика не достигла readiness; 4) потребление относительно запроса ресурсов; 5) нет; 6) нет; 7) drain закрывает узел для планирования даже при последующей ошибке.
Резюме и источники
Доступность — согласованный контракт ёмкости, rollout, здоровья, нарушений и автоматического масштабирования. Далее: безопасность и мультитенантность.