Введение: зачем нужна отказоустойчивость в Kubernetes
Отказоустойчивость кластера Kubernetes означает способность инфраструктуры продолжать обслуживать рабочие нагрузки при выходе из строя отдельных компонентов: узла, зоны доступности или экземпляра управляющего сервиса. Единая точка отказа (SPOF) - это любой компонент, поломка которого приводит к полной или частичной недоступности приложения. В продакшн-среде простой сервиса обходится дорого: по данным отраслевых исследований, час простоя для среднего бизнеса может стоить от 100 до 300 тысяч долларов, а для крупных финансовых и торговых платформ - значительно больше.
Kubernetes предоставляет механизмы для устранения большинства SPOF: мультизональное распределение узлов, резервирование контрольной плоскости и etcd, правила pod anti-affinity, горизонтальное автомасштабирование через HPA и стратегии обновления без простоя. Эта статья - практическое руководство по проектированию кластера, в котором нет единой точки отказа. Разбираем каждый механизм с примерами конфигураций и командами, которые можно применить сразу.
Если вы только начинаете проектировать production-кластер, рекомендуем сначала изучить архитектуру кластера Kubernetes и ключевые объекты API, а затем вернуться к этой статье для настройки отказоустойчивости.
Архитектура высокой доступности: мультизональное распределение узлов
Зоны доступности (Availability Zones, AZ) - это изолированные локации внутри одного региона облачного провайдера: отдельные дата-центры с независимым питанием, охлаждением и сетевыми каналами. Размещение узлов кластера в разных зонах защищает от сбоев уровня дата-центра: пожара, аварии электропитания, обрыва магистрального канала.
Минимальная отказоустойчивая конфигурация - три зоны доступности. При падении одной зоны оставшиеся две сохраняют кворум для etcd и достаточную ёмкость для перезапуска рабочих нагрузок. Двух зон недостаточно: при отказе одной вторая становится единственной точкой отказа, а etcd теряет кворум, если его узлы распределены поровну.
Для облачных провайдеров зоны задаются автоматически: AWS использует метку topology.kubernetes.io/zone=us-east-1a, GCP - topology.kubernetes.io/zone=europe-west1-b, Azure - topology.kubernetes.io/zone=westeurope-1. Для bare-metal кластеров зоны назначаются вручную через метки узлов.
Практика: разметка узлов по зонам
Назначьте каждому узлу метку зоны командой kubectl label nodes:
kubectl label nodes worker-1 topology.kubernetes.io/zone=zone-a
kubectl label nodes worker-2 topology.kubernetes.io/zone=zone-b
kubectl label nodes worker-3 topology.kubernetes.io/zone=zone-cПроверьте, что метки применились:
kubectl get nodes -L topology.kubernetes.io/zoneПосле разметки планировщик Kubernetes учитывает зоны при размещении подов, если в манифестах заданы правила affinity или topologySpreadConstraints. Для принудительного распределения подов по зонам используйте topologySpreadConstraints с maxSkew: 1 - это гарантирует равномерное распределение реплик между зонами.
Подробнее о выборе инфраструктуры для production-кластеров - в руководстве по проектированию и управлению промышленными кластерами Kubernetes.
Резервирование контрольной плоскости и etcd
Контрольная плоскость (control plane) управляет кластером: API server принимает запросы, controller manager поддерживает желаемое состояние, scheduler распределяет поды по узлам. Все компоненты control plane должны быть зарезервированы. Минимальная отказоустойчивая конфигурация - три реплики каждого компонента, распределённые по трём зонам доступности.
etcd - распределённое key-value хранилище, в котором Kubernetes хранит всё состояние кластера: объекты API, конфигурации, секреты. Потеря etcd означает потерю кластера. Для обеспечения высокой доступности etcd разворачивают как кластер из нечётного числа узлов: 3 или 5. Нечётное количество необходимо для работы алгоритма консенсуса Raft: кворум достигается при большинстве голосов. Кластер из 3 узлов переживает отказ одного, из 5 - отказ двух.
Настройка кластера etcd высокой доступности
Разверните etcd на трёх выделенных узлах в разных зонах. Пример конфигурации для первого узла:
etcd \
--name etcd-1 \
--initial-advertise-peer-urls http://10.0.0.1:2380 \
--listen-peer-urls http://0.0.0.0:2380 \
--advertise-client-urls http://10.0.0.1:2379 \
--listen-client-urls http://0.0.0.0:2379 \
--initial-cluster etcd-1=http://10.0.0.1:2380,etcd-2=http://10.0.0.2:2380,etcd-3=http://10.0.0.3:2380 \
--initial-cluster-state new \
--data-dir /var/lib/etcdПроверьте состояние кластера и кворум:
etcdctl endpoint health --cluster
etcdctl member listРегулярно создавайте резервные копии etcd - это единственный надёжный способ восстановить кластер после катастрофического сбоя:
etcdctl snapshot save /backup/etcd-$(date +%Y%m%d).dbХраните резервные копии в отдельной зоне или регионе, чтобы отказ одной зоны не уничтожил и данные, и их копию.
Распределение рабочих нагрузок: pod anti-affinity
Pod anti-affinity - это правила, которые запрещают или не рекомендуют планировщику размещать поды одного приложения на одном узле или в одной зоне. Если все реплики Deployment окажутся на одном узле, отказ этого узла приведёт к полной недоступности приложения, даже если в кластере есть другие свободные узлы.
Два типа anti-affinity:
- requiredDuringSchedulingIgnoredDuringExecution - жёсткое правило: под не будет запланирован, если условие нарушается.
- preferredDuringSchedulingIgnoredDuringExecution - мягкое правило: планировщик постарается выполнить условие, но при нехватке ресурсов разместит под на том же узле.
Для продакшн-среды используйте жёсткие правила с topologyKey: topology.kubernetes.io/zone, чтобы реплики гарантированно распределялись по зонам.
Пример конфигурации anti-affinity для распределения по зонам
apiVersion: apps/v1
kind: Deployment
metadata:
name: web-app
spec:
replicas: 3
selector:
matchLabels:
app: web-app
template:
metadata:
labels:
app: web-app
spec:
affinity:
podAntiAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
- labelSelector:
matchExpressions:
- key: app
operator: In
values:
- web-app
topologyKey: topology.kubernetes.io/zone
containers:
- name: web
image: nginx:1.25
ports:
- containerPort: 80Этот манифест гарантирует, что три реплики приложения разместятся в трёх разных зонах. Если зон меньше, чем реплик, лишние поды останутся в статусе Pending - это сигнал о необходимости расширить инфраструктуру.
Дополнительно настройте PodDisruptionBudgets, чтобы ограничить количество одновременно недоступных подов при плановых операциях. Подробнее - в статье о самовосстановлении в Kubernetes.
Автоматическое масштабирование с HPA
Horizontal Pod Autoscaler (HPA) автоматически изменяет количество реплик Deployment, StatefulSet или ReplicaSet в зависимости от наблюдаемых метрик: загрузки CPU, потребления памяти или кастомных метрик из Prometheus. HPA поддерживает доступность сервиса при резких всплесках нагрузки: когда трафик растёт, контроллер добавляет реплики, когда падает - убирает лишние.
Ключевые параметры HPA:
- minReplicas - минимальное количество реплик, ниже которого HPA не опустится, даже при нулевой нагрузке.
- maxReplicas - верхний предел, защищающий кластер от неконтролируемого расхода ресурсов.
- targetCPUUtilizationPercentage - целевой процент использования CPU, при котором HPA поддерживает текущее количество реплик.
Для стабильной работы HPA обязательно задавайте requests для CPU и памяти в манифестах подов. Без requests HPA не сможет рассчитать процент утилизации.
Настройка HPA на основе загрузки CPU
Создайте HPA командой:
kubectl autoscale deployment web-app --cpu-percent=70 --min=3 --max=12Или через YAML-манифест:
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
name: web-app-hpa
spec:
scaleTargetRef:
apiVersion: apps/v1
kind: Deployment
name: web-app
minReplicas: 3
maxReplicas: 12
metrics:
- type: Resource
resource:
name: cpu
target:
type: Utilization
averageUtilization: 70Проверьте работу HPA:
kubectl get hpa web-app-hpa --watchКолонка TARGETS покажет текущую утилизацию CPU относительно целевой. При превышении 70% HPA начнёт добавлять реплики с шагом, рассчитанным по формуле: desiredReplicas = ceil(currentReplicas * (currentMetricValue / desiredMetricValue)).
Стратегии обновления без простоя
Обновление приложений - частая причина простоев. Kubernetes предоставляет стратегии, которые позволяют обновлять поды без прерывания обслуживания трафика.
RollingUpdate - стратегия по умолчанию для Deployment. Поды обновляются по одному или небольшими партиями: старые реплики продолжают обслуживать запросы, пока новые проходят readiness-проверки. Два ключевых параметра:
- maxSurge - сколько дополнительных подов можно создать сверх желаемого количества в процессе обновления. Например, при
maxSurge: 1и 3 репликах во время обновления будет до 4 подов. - maxUnavailable - сколько подов может быть недоступно в любой момент обновления. Значение
0гарантирует, что трафик всегда будет обслуживаться полным количеством реплик.
Настройка RollingUpdate для Deployment
apiVersion: apps/v1
kind: Deployment
metadata:
name: web-app
spec:
replicas: 3
strategy:
type: RollingUpdate
rollingUpdate:
maxSurge: 1
maxUnavailable: 0
selector:
matchLabels:
app: web-app
template:
metadata:
labels:
app: web-app
spec:
containers:
- name: web
image: nginx:1.25
readinessProbe:
httpGet:
path: /health
port: 80
initialDelaySeconds: 5
periodSeconds: 5Параметр maxUnavailable: 0 в сочетании с readinessProbe гарантирует, что ни один под не будет удалён, пока новый не прошёл проверку готовности. Это исключает потерю трафика при обновлении.
Для более сложных сценариев используйте blue-green или canary деплойменты через service mesh (Istio, Linkerd) или инструменты типа Argo Rollouts. Они позволяют переключать трафик между версиями постепенно и автоматически откатываться при обнаружении ошибок.
Обновление узлов кластера выполняйте по одному: сначала kubectl cordon для запрета планирования новых подов, затем kubectl drain для эвакуации существующих, обновление узла и kubectl uncordon для возврата в кластер. Повторяйте для каждого узла.
Проверка отказоустойчивости: тестирование сбоев
Отказоустойчивость, которую не тестировали, - это гипотеза. Регулярно проверяйте, что кластер действительно переживает отказы. Начните с простых сценариев:
- Удалите под критичного приложения командой
kubectl delete podи убедитесь, что ReplicaSet создал новый, а трафик не прерывался. - Перезагрузите рабочий узел и проверьте, что поды переехали на другие узлы в течение заданного таймаута.
- Отключите сетевой доступ к одной зоне (если это возможно в вашей среде) и убедитесь, что etcd сохранил кворум, а приложения продолжают работать.
Для систематического хаос-тестирования используйте Chaos Mesh или Litmus Chaos. Эти инструменты позволяют внедрять контролируемые сбои: убивать поды, замедлять сеть, имитировать отказ узла или целой зоны. Пример сценария Chaos Mesh для удаления случайного пода:
apiVersion: chaos-mesh.org/v1alpha1
kind: PodChaos
metadata:
name: pod-kill-example
spec:
action: pod-kill
mode: one
selector:
namespaces:
- production
labelSelectors:
app: web-app
scheduler:
cron: "@every 10m"Проверяйте восстановление etcd отдельно: остановите один узел etcd, убедитесь, что кластер продолжает обслуживать запросы, затем восстановите узел и проверьте синхронизацию данных.
Заключение: чек-лист отказоустойчивого кластера
Отказоустойчивый кластер Kubernetes - это не одна настройка, а комбинация архитектурных решений и операционных практик. Чек-лист для проверки вашего кластера:
- Узлы распределены минимум по трём зонам доступности, каждая зона имеет метку
topology.kubernetes.io/zone. - Control plane зарезервирован: минимум три реплики API server, controller manager и scheduler.
- etcd развёрнут как кластер из 3 или 5 узлов в разных зонах, настроено регулярное резервное копирование.
- Для критичных приложений настроен pod anti-affinity с
topologyKey: topology.kubernetes.io/zone. - HPA настроен для всех приложений с переменной нагрузкой, заданы minReplicas и maxReplicas.
- Стратегия RollingUpdate с
maxUnavailable: 0и readinessProbe применяется для всех Deployment. - Сбои тестируются регулярно: от ручных проверок до автоматизированного chaos engineering.
Примените эти рекомендации к своему кластеру. Начните с аудита текущей конфигурации: проверьте распределение узлов по зонам, состояние etcd и наличие anti-affinity правил. Затем внедряйте недостающие механизмы по одному, тестируя после каждого изменения. Дополнительно изучите 10 типичных ошибок при настройке Kubernetes, чтобы избежать распространённых проблем на этапе внедрения.
Для размещения отказоустойчивого кластера в облаке рассмотрите облачную инфраструктуру Timeweb Cloud с поддержкой Kubernetes и гибким масштабированием ресурсов.