Модуль 27. Stateful-приложения в Kubernetes
Stateful-нагрузки требуют трёх вещей: устойчивая идентичность подов, надёжное хранилище и дисциплина отказоустойчивости/бэкапов на уровне самого приложения (не k8s).
Опорные кирпичи в Kubernetes:
- StatefulSet (упорядоченная идентичность pod-ов: pod-0, pod-1…),
- Headless Service (DNS-имена каждого pod),
- VolumeClaimTemplates (по PVC на реплику),
- StorageClass/CSI (какой диск и как создавать),
- PodDisruptionBudget/TopologySpread/Anti-Affinity (живучесть при эвакуациях/апгрейдах),
- Операторы (Patroni/PG-операторы, ClickHouse Operator, Strimzi для Kafka и др.).
Базовые паттерны StatefulSet (что настроить сразу)
Обязательные элементы:
- Headless Service (ClusterIP: None) — даёт DNS pod-0.svc, pod-1.svc.
- VolumeClaimTemplates — отдельный RWO-том на под.
-
updateStrategy:
- RollingUpdate с partition — поштучные перезапуски;
- OnDelete — только под управлением оператора (часто у БД).
- podManagementPolicy:
- OrderedReady (по умолчанию, безопаснее),
- Parallel — если приложение это поддерживает.
- Anti-Affinity и TopologySpread — чтобы реплики не оказались на одной ноде/в одной зоне.
- PDB — сколько подов должно остаться доступно при эвакуациях (напр., minAvailable: N-1).
Мини-эскиз (идея):
spec:
serviceName: mydb-headless # Headless Service
podManagementPolicy: OrderedReady
updateStrategy:
type: RollingUpdate
template:
spec:
terminationGracePeriodSeconds: 60
affinity:
podAntiAffinity: # не сажать реплики на одну ноду
preferredDuringSchedulingIgnoredDuringExecution:
- weight: 100
podAffinityTerm:
labelSelector: { matchLabels: { app: mydb } }
topologyKey: "kubernetes.io/hostname"
topologySpreadConstraints: # равномерно по зонам/нодам
- maxSkew: 1
topologyKey: topology.kubernetes.io/zone
whenUnsatisfiable: DoNotSchedule
labelSelector: { matchLabels: { app: mydb } }
volumeClaimTemplates:
- metadata: { name: data }
spec:
accessModes: [ ReadWriteOnce ]
storageClassName: fast-rwo
resources: { requests: { storage: 500Gi } }
Хранилище и производительность
- БД → RWO-блок (Ceph RBD/облачные диски/локальные NVMe). Не NFS/RWX.
- SC: WaitForFirstConsumer — диск создастся в зоне, где реальный под.
- ФС: XFS (часто для ClickHouse), ext4/XFS для PostgreSQL; online-расширение включайте и тестируйте.
- Отдельные PV под WAL/журналы (PG) — уменьшает латентность.
- ReclaimPolicy=Retain — чтобы удаление PVC не уничтожило данные.
Отказоустойчивость — на уровне приложения
Общие приёмы
- Синхронная/полусинхронная репликация (PG), реплики/шарды (CH), RF/ISR (Kafka).
- Quorum и fencing при failover (критично для PG).
- Readiness должен означать готовность принимать свою роль (primary/replica), а не «жив HTTP-порт».
- Сервисы на роли: отдельные Service для primary и replica (PG), per-shard (CH), listeners (Kafka).
PostgreSQL в k8s: Patroni и операторы
Путь «сверху вниз»:
- Zalando Postgres Operator (внутри — Patroni) или CrunchyData PGO — удобный «all-in-one» для продакшена.
- Patroni (Spilo) — сам по себе (DCS = Kubernetes/etcd/Consul). В k8s обычно DCS = Kubernetes (через Endpoints/ConfigMap).
ХА-параметры (идеи):
- Синхронная реплика synchronous_standby_names (1 шт. на write-SLA).
- max_wal_size, checkpoint_timeout, wal_compression.
- Пулы соединений: pgBouncer (отдельный Deployment/Service).
- Сервисы: pg-primary (write), pg-replicas (read). Метки ролей выставляет оператор/Patroni.
Бэкапы и PITR:
- pgBackRest / WAL-G в S3/MinIO; PITR по LSN/WAL-архиву.
- Храните bootstrap snapshot + WALы ~7–30 дней; тестируйте restore регулярно.
Наблюдаемость:
- postgres_exporter, lag по репликам, locks, deadlocks, connections %, size/db, autovacuum.
- Алерты: replication lag, max_connections приближается, checkpoint слишком часто/редко.
Риски:
- Split-brain (неправильный fencing), «промах» DNS/Service при failover, медленные диски на WAL, большой autovacuum debt.
ClickHouse в k8s: оператор и Keeper
Оператор (Altinity ClickHouse Operator):
- Описываете шарды × реплики, сториджи, профили.
- Метаданные — ClickHouse Keeper (вместо ZooKeeper) или ZooKeeper кластером.
- Хранилище: RWO SSD/XFS; S3 — для внешних дисков/таблиц (но не как POSIX).
- Сервисы/доступ: Headless per-pod, общий Service для балансировки запросов (или через Trino).
Бэкапы:
- clickhouse-backup (S3/объектное), консистентные снапшоты таблиц; планируйте удалённые диски + ALTER TABLE FREEZE.
- DR — резерв с репликами в другом кластере/зоне.
Наблюдаемость:
- Экспортер: запросы/ошибки, merges backlog, replication queue, parts, disk space, задержки.
- Алерты: рост merges/replication backlog, space < 15%, rejected queries.
Риски:
- Непродуманное партиционирование → лавины parts, медленные merge; «мелкие файлы» в S3; contention на диске.
Kafka в k8s: Strimzi / KRaft
Оператор: Strimzi (Kafka + ZK или KRaft без ZK).
- Replication Factor/ISR/min.insync.replicas — база отказоустойчивости.
- Rack-aware (теги зон) — брокеры распределяются по зонам, лидеры не «складываются» в одной зоне.
- Хранилище: RWO быстрые диски; JBOD профили поддерживаются оператором.
- External access: LB/NodePort, настройка listeners, TLS/SASL.
- Автоскейл контролируемо: Cruise Control (rebalancing), но не «волшебная палочка».
DR/бэкап:
- MirrorMaker 2 / Cluster Linking — репликация топиков между кластерами (актив-пассив/актив-актив).
- «Бэкап» сегментов диска — редко практикуется; ориентируйтесь на репликацию и ретеншн.
Наблюдаемость:
- Kafka Exporter/JMX: consumer lag, offline partitions, under-replicated partitions, request latency.
- Алерты: U/R partitions > 0, lag растёт, controller changes.
Риски:
- Неверные параметры ISR → потеря сообщений при сбое; внешний доступ без proper TLS; заполнение дисков.
Сеть и сервисы для stateful
- Headless для адресации конкретной реплики.
- Сервисы на роль (PG primary/replicas), per-shard (CH), listeners (Kafka).
- Readiness должен проверять готовность роли: PG-primary доступен для write, replica — для read; CH — доступен Keeper и нужные диски; Kafka — broker в ISR.
- externalTrafficPolicy: Local — если нужен реальный client IP на входе (обычно для Ingress/HTTP-BI, не для БД).
Бэкапы и DR — стратегическая дисциплина
Три слоя:
- Логический (инструменты приложения): pgBackRest/WAL-G, clickhouse-backup, дампы метаданных Kafka.
- Снапшоты томов (CSI/Velero): быстрые, но осторожно с консистентностью (freeze или остановка записи).
- Репликация/зеркалирование: CH-реплики в другой зоне/кластере; Kafka — MM2/Cluster Linking; PG — stream-реплика на standby-кластер.
Практика:
- Храните в S3/MinIO с версионированием; шифрование.
- Регулярные учения восстановления (restore-день).
- RPO/RTO: зафиксируйте целевые значения и проверяйте их в учениях.
Наблюдаемость и SLO (коротко)
- PG: success/write latency, replication lag, connections %, checkpoints;
- CH: запросы/ошибки, merges backlog, replication queue, disk usage;
- Kafka: under-replicated partitions, consumer lag, request latency, disk usage.
- SLO: успешность ≥ 99%, p95 latency, целевой lag; алерты по burn-rate (см. Модуль 25).
Практика (лабораторка за 1–2 дня)
-
PostgreSQL (оператор)
- Развернуть кластер 1 primary + 2 replicas (Patroni-база).
- Настроить Service: pg-primary, pg-replicas; поставить pgBouncer.
- Подключить pgBackRest в MinIO; выполнить базовый PITR.
- Эмулировать падение ноды → убедиться в корректном failover.
- ClickHouse (оператор)
- Кластер 2×2 (2 шард × 2 реплики), Keeper.
- Создать таблицы ReplicatedMergeTree; настроить clickhouse-backup в S3.
- Нагрузочный тест (SELECT с агрегацией), посмотреть merges/backlog.
- 3 брокера (rack-aware по зонам), RF=3, min.insync.replicas=2.
- Включить TLS/SASL, настроить внешний доступ (LB).
- MirrorMaker2 в «второй» кластер (мини-DR).
- Протестировать lag/URP алерты.
- Kafka (Strimzi)
Риски и анти-паттерны
|
Риск |
Проявление |
Как избежать |
|---|---|---|
|
NFS/RWX под БД |
Блокировки, падение производительности, коррапт |
Только RWO-блок (RBD/NVMe/облачные диски) |
|
Нет PDB/Anti-Affinity |
«Убили» сразу две реплики при апгрейде |
PDB + anti-affinity + spread по зонам |
|
Readiness «всегда зелёный» |
Трафик на «не ту» роль, ошибки |
Readiness по роли (primary/replica), здравые пробы |
|
Volume snapshot вместо логического бэкапа |
Невозможность PITR/неконсистентность |
Логические бэкапы первичны; снапшоты — дополнение |
|
Split-brain (PG) |
Две «праймари», потеря консистентности |
Правильный fencing, quorum, DCS проверенный |
|
Kafka ISR неверный |
Потеря сообщений при сбоях |
RF≥3, min.insync.replicas≥2, rack-aware |
|
Мелкие файлы в CH/S3 |
Медленные сканы, merges-шторм |
Правильное партиционирование, компакты/TTL |
|
Reclaim=Delete в проде |
Потеря данных при удалении PVC |
Retain для критичных; регламент утилизации PV |
|
Оператор с кластер-админом «навсегда» |
Избыточные права, риск компрометации |
Минимальные RBAC под CRD/NS, ротация токенов |
Чек-лист «готово к проду (stateful)»
- StatefulSet + Headless + PVC per-pod, SC с WaitForFirstConsumer.
- PDB/Anti-Affinity/TopologySpread по зонам; terminationGrace настроен.
- Services по ролям (PG), per-shard (CH), корректные listeners (Kafka).
- Логические бэкапы (PG: pgBackRest/WAL-G; CH: clickhouse-backup; Kafka: MM2/Linking), снапшоты CSI — дополнительно.
- Учения восстановления (restore) и failover проведены; RPO/RTO задокументированы.
- Наблюдаемость: экспортеры + SLO/алерты (lag, merges/URP, p95).
- Безопасность: Secrets через Vault/ESO, диски и трафик с шифрованием, доступ по RBAC.
- Runbook’и: «PG failover», «CH merges backlog», «Kafka URP/lag», «PITR».
Вопрос-ответ
В: Можно ли делать бэкап БД только снапшотом PV (Velero/CSI)?
О: Нежелательно. Это не даёт PITR и может быть неконсистентно. Держите логические бэкапы (pgBackRest/WAL-G, clickhouse-backup) как основное, снапшоты — как ускоритель RTO.
В: Как направлять write/read в PostgreSQL?
О: Два Service: pg-primary (селектор по метке роли) и pg-replicas. Для роутинга на уровне приложений — используйте эти DNS. Плюс — pgBouncer.
В: ClickHouse лучше держать «монолитом» или шардировать?
О: Для роста — шарды × реплики. Партиционирование по дате/ключу, распределённые таблицы. Следите за merges и размером parts.
В: Как обеспечить DR для Kafka?
О: Два кластера + MirrorMaker 2 (или Cluster Linking), RF≥3, rack-aware, ретеншн под RPO. «Бэкап файлов» брокера — не основная стратегия.
В: Можно ли хранить WAL/бэкапы PG на NFS?
О: Лучше S3/объектное (MinIO/облако) с версионированием и проверкой целостности. NFS — только если гарантируете надёжность и пропускную.
В: Что выбрать для Postgres — оператор или «чистый» Patroni?
О: Для прод-процесса удобнее оператор (Zalando/Crunchy): меньше «ручной» обвязки, встроенные бэкапы/пользователи/обновления.
В: Как тестировать отказоустойчивость безопасно?
О: Хаос-инжиниринг: cordon/drain ноды, kubectl delete pod реплики, симуляция сетевых потерь; замеряйте MTTR и корректность failover.
Stateful-сервисы в k8s — это приложение-центричная дисциплина: правильный StatefulSet и сеть — лишь основа. Настоящая надёжность достигается операторами, корректной репликацией/кворумом, логическими бэкапами и регулярными учениями восстановления. С такими практиками PostgreSQL, ClickHouse и Kafka чувствуют себя в Kubernetes так же уверенно, как и на «железе» — но управляются и обновляются несравнимо проще.



