Модуль 11. Надёжность и масштабирование
Надёжность — это не «кластер сам всё переживёт», а согласованный дизайн:
- Приложение (BI/Trino/СУБД): умеет переживать рестарты и переключения лидера.
- Kubernetes: правильно настроены PDB, анти-аффинити, topology spread, классы приоритетов.
- Инфраструктура: пулы нод, несколько зон (AZ/ЦОД), сторидж/сеть без единой точки отказа.
- Данные: PITR/репликация, DR-сценарий (backup-only, active-passive).
- Масштабирование: горизонтальное (HPA/KEDA), вертикальное (VPA-рекомендации), «бюджеты соединений», автоскейл очередей.
- Эксплуатация: окна работ, план эвакуации нод, хаос-дни и регулярные DR-учения.
HA-паттерны по слоям
СУБД
Postgres Pro (Patroni + pgBackRest/WAL-архив):
- Replica ≥ 2 + один лидер. Репликация синхронная для RPO≈0 на критических БД, асинхронная — для второстепенных.
- Пулы нод: pool=db. WAL и дата — на быстрых RWO (RBD/NVMe). Разнести лидер/реплики по разным AZ.
- pgBouncer перед приложениями (pool transaction, max_client_conn — ваш «коннект-бюджет»).
- PITR + ежеквартальные учения восстановления.
ClickHouse / ADQM:
- Репликация таблиц (replicated MergeTree), шарды × реплики, ZooKeeper/OpenSearch-аналоги избыточны.
- HA = реплики на разных нодах/зонах; контролируйте merge-нагрузку и диск.
Метрики HA: replication lag, failover time, число клиентских ошибок при свитче, время восстановления по PITR.
Trino
- Workers масштабируются, координатор — критическая точка. Делайте standby-координатор (passive) + быстрый failover, sticky-сессии для UI/BI.
- HPA на workers по очереди запросов/CPU; spill на быстрые локальные диски.
- PDB на workers — не убивайте все одновременно при drain.
BI (Superset/Metabase и др.)
- Web — минимум 2 реплики, stateless. Сессии/кэш — в Redis (единую точку Redis сделайте HA либо как кластер).
- Фоновые воркеры (Celery и т. п.) — несколько реплик + KEDA по очереди.
- Экспорты в S3 (а не «через веб-поток»), чтобы перезапуски веб-слоя не «обрывали» выгрузки.
Контроль обновлений и эвакуаций: PDB, topology spread, аффинити
PodDisruptionBudget (PDB)
Защищает от «слишком дружного» выселения подов во время drain/апгрейда нод.
Мини-пример (Trino workers, оставить минимум 2 живых):
apiVersion: policy/v1
kind: PodDisruptionBudget
metadata: { name: trino-workers-pdb, namespace: vitrines-shared }
spec:
minAvailable: 2
selector:
matchLabels: { app: trino, role: worker }
TopologySpreadConstraints
Раскладывает реплики по зонам/нодам для устойчивости.
Мини-пример (рассыпать по зонам, maxSkew=1):
spec:
topologySpreadConstraints:
- maxSkew: 1
topologyKey: topology.kubernetes.io/zone
whenUnsatisfiable: ScheduleAnyway
labelSelector: { matchLabels: { app: trino, role: worker } }
Anti-Affinity/Node Affinity
- PodAntiAffinity: не класть все реплики на одну ноду.
- NodeAffinity: держать БД на pool=db, воркеров Trino — на pool=etl.
Резервирование по зонам/ЦОД (AZ/Region)
- Сетевой и сторидж слой: Ceph/RBD в нескольких стойках, или использование независимых СХД по AZ.
- Контроль плейсмента: StorageClass WaitForFirstConsumer, чтобы PV создавался там, где Pod.
- DNS/Ingress: отдельные ingress-классы для разных AZ, MetalLB/BGP — без единой точки отказа.
- Чёткие RTO/RPO по сервисам (см. ниже). Учитывайте латентность меж-AZ для репликаций.
DR-сценарии (что именно вы делаете, когда «упала площадка»)
|
Сценарий |
Где использовать |
RPO/RTO |
Суть |
|---|---|---|---|
|
Backup-only |
Dev/низкая критичность |
RPO=интервал бэкапа, RTO=часы |
Бэкапы в объектное хранилище + инструкции восстановления |
|
Pilot-light |
Средняя критичность |
RPO=минуты, RTO=десятки минут |
Минимальный «скелет» в DR (S3, каталоги, пустой Trino), быстро разворачиваем compute |
|
Active-Passive |
Прод BI/DWH |
RPO≈0 (синхр. WAL)/минуты, RTO=минуты |
Горячая реплика БД, репликация бакетов, включение compute в DR |
|
Active-Active |
Редко для BI |
Сложно/дорого |
Два полноценных региона, сложные конфликты данных |
Практично для BI/DWH: Active-Passive.
Что именно реплицировать:
- БД метаданных (лидер→реплика),
- Lakehouse бакеты (MinIO bucket replication),
- Конфиги/манифесты (GitOps — один источник правды),
- Секреты (Vault DR/replication),
-
Каталоги (Hive/Iceberg) — снапшоты/реплика.
Что поднимаем в DR: Trino workers/BI web/воркеры — быстро и из Git.
Автомасштабирование очередей и вычислений
Очереди
- KEDA — масштабирует по длине/лагу: Redis (Celery), Kafka lag, Prometheus-метрики.
- Idempotency задач и rate-limit на коннекторы, чтобы масштаб не «забил» источники.
Мини-пример (KEDA по Redis queue length):
apiVersion: keda.sh/v1alpha1
kind: ScaledObject
metadata: { name: superset-workers, namespace: bi }
spec:
scaleTargetRef: { name: superset-worker }
minReplicaCount: 2
maxReplicaCount: 20
triggers:
- type: redis
metadata:
addressFromEnv: REDIS_ADDR
listName: celery
listLength: "100"
Trino workers / ETL
- HPA по queuedQueries (через Prometheus Adapter) или CPU.
- Guardrails: лимит concurrent-запросов в Trino/ClickHouse, budget соединений к БД.
Ресурсные резервы и обновления без простоя
- N+1: держите запас compute и коннектов (например, 30% headroom в prime-time).
- RollingUpdate: maxUnavailable=0 для критичных веб-слоёв (или canary через Argo Rollouts).
- Maintenance windows: PDB + drain по одной ноде, «зелёные» проверки SLI между шагами.
- PriorityClass: платформа (каталоги/Trino coordinator/pgBouncer) выше пользовательских ETL-джоб.
Риски и контрмеры
|
Риск |
Проявление |
Как смягчить |
|---|---|---|
|
PDB отсутствуют/слишком строгие |
Массовая недоступность или невозможность drain |
Ввести PDB с реальными значениями minAvailable; проверить обновления |
|
Реплики «свалены» в одну зону |
Потеря AZ = потеря сервиса |
TopologySpreadConstraints, anti-affinity, node/zone labels |
|
Одинарный координатор Trino без standby |
Замирание кластера при рестарте |
Standby-координатор + быстрый failover, sticky-сессии |
|
Redis/очередь — SPOF |
Дубли/потери заданий |
Redis Sentinel/Cluster, персистентность, KEDA c minReplicas>0 |
|
Бэкапы «есть», восстановления нет |
Долгий RTO, сюрпризы |
Регулярный DR-дэй: восстановление «с нуля», чек-листы |
|
Автоскейл «съедает коннекты» |
5xx/timeout у источников |
Connection budgets, лимиты Trino/pgBouncer, HPA с верхней границей |
|
Репликация бакетов не настроена |
DR «пустой» |
MinIO bucket replication, каталоги/метаданные синхронизируются |
|
Непродуманные окна обновлений |
Пики инцидентов |
Заморозки на отчётные окна, поэтапные выкаты, канареечные проверки SLI |
Мини-фрагменты конфигураций (ровно сколько нужно)
PDB для BI web (оставить 1 из 2 минимум):
apiVersion: policy/v1
kind: PodDisruptionBudget
metadata: { name: bi-web-pdb, namespace: bi }
spec:
minAvailable: 1
selector: { matchLabels: { app: superset, tier: web } }
Anti-Affinity для Redis (не на одну ноду):
spec:
affinity:
podAntiAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
- labelSelector: { matchLabels: { app: redis } }
topologyKey: kubernetes.io/hostname
Практика: хаос-инжиниринг (эмулируем падение ноды), проверяем SLO
Подготовка
- SLO/SLI определены (Модуль 5): доступность BI (99.5%), p95 latency, Trino success-rate, Postgres RPO/RTO.
- kube-prometheus-stack, логи Ingress, алерты burn-rate настроены.
- PDB/anti-affinity/topology spread включены.
- Набор GameDay-сценариев и runbook’ов на каждый.
Сценарии (выполняйте по одному)
- Drain одной рабочей ноды (с воркерами Trino/BI web):
- kubectl drain <node> --ignore-daemonsets --delete-emptydir-data --force
Ожидание: HPA пересоберёт воркеры, PDB не даст уронить все реплики, SLO-доступность не проседает.
- Удаление Pod лидера Postgres (Patroni):
- kubectl delete pod pg-0 -n data-platform
Ожидание: failover < 30–60 сек, p95 BI не «краснеет» дольше оговорённого, RPO=0 при синхроне WAL.
-
Падение Redis воркеров (Celery): удалить Pod Redis-мастера.
Ожидание: Sentinel/Cluster переключает; очередь не теряется, воркеры переподключаются. -
Отключение одной AZ (имитация): node-селект по лейблу зоны и drain всех нод зоны по очереди.
Ожидание: сервис остаётся доступен, деградация в пределах SLO.
Наблюдаем и фиксируем
- В Grafana: availability, p95 latency, queued queries Trino, replication lag, error rate.
- В Alertmanager: алерты burn-rate/lag/replica-down — сработали? Были ли флаппинги?
Критерии приёмки
- BI availability не ниже SLO (на час эксперимента).
- p95 UI ≤ целевого порога после 2–3 минут стабилизации.
- Postgres failover ≤ X сек, RPO в допустимых пределах.
- Trino success-rate ≥ 99% (допустима краткая деградация).
- Никаких массовых 5xx на Ingress, дубликатов отчётов после возврата.
Пост-мортем (короткий)
- Что сработало/нет, какие алерты лишние/отсутствуют, что добавить в runbook’и.
- Обновить PDB/лимиты/HPA-границы по результатам.
Чек-лист «готово к надёжному продакшену»
- У СУБД есть реплики в разных AZ; PITR проверен; pgBouncer введён.
- У Trino есть standby-координатор, workers под HPA, spill-диски быстрые.
- BI web ≥ 2 реплики, Redis HA, воркеры под KEDA, экспорты в S3.
- Везде заданы PDB, anti-affinity, topologySpreadConstraints.
- Пулы нод и taints: stateful/etl/gpu разнесены; приоритеты настроены.
- DR: выбран сценарий (обычно active-passive), репликация бакетов, журналов и метаданных.
- SLO/алерты по burn-rate, логи и метрики «как код», GameDay календарь.
- Runbook’и на failover БД, падение AZ, деградацию Trino/BI; ответственные и RTO/RPO прописаны.
Надёжность в k8s для BI/DWH — это совокупность мелочей: от PDB и раскладки по зонам до standby-координатора Trino и idempotent-воркеров. Если у вас есть резерв по ресурсам, четкий DR-план, автоскейл очередей и регулярные хаос-учения, то падение ноды, перетасовка подов и даже отказ площадки превращаются из катастрофы в контролируемое событие, не «съедающее» ваш SLO и бюджет ошибок.




