Hot/warm/cold standby, health probes, witness-quorum, STONITH, DNS/VIP failover и failback. 4 сценария: steady state, automatic failover, split-brain prevention, controlled failback.
KEKey · @kuzminykh_igor_b3550a9b
0 stars
0 views
92d ago · last update
failover.js·4 scenarios
Loading canvas…
Зачем
Любой single-node primary рано или поздно умрёт — kernel panic, hardware-сбой, выкатили плохой релиз, перегрелся ДЦ. Без failover клиенты увидят 5xx или connection refused до тех пор, пока дежурный не проснётся, не диагностирует и не переключит вручную. Это минуты-часы downtime и потеря выручки.
Failover — автоматическое (или контролируемое полу-автоматическое) переключение нагрузки на резервный узел при отказе основного. Это не «магия высокой доступности»: это явный механизм, у которого есть бюджеты RTO/RPO, явная цена в инфраструктуре и явные failure modes (в первую очередь — split-brain).
Тема обязательная для любого продакшна, который называет себя HA. Failover — основа любого Multi-AZ/Multi-region архитектурного паттерна, и его принципы одинаковы для Postgres, Redis, Kafka controller'а, etcd-кластера и DNS-балансировщика.
Mental model
Три цифры, которые всё определяют:
RTO (Recovery Time Objective) — сколько времени бизнес готов лежать. Hot standby: < 1 секунды. Warm: секунды-минуты. Cold: минуты-часы.
RPO (Recovery Point Objective) — сколько данных бизнес готов потерять. Синхронная репликация: ~0. Асинхронная: секунды. Бэкапы: минуты-часы.
Cost — деньги, которые вы платите за каждый шаг улучшения RTO/RPO. Hot standby удваивает инфра-счёт.
Failover-сценарий распадается на пять фаз, и любая ошибка в любой из них ломает всю цепочку:
Detection — health-probe / heartbeat замечает, что primary не отвечает (N подряд миссов, не первый же таймаут).
Decision — кворум (witness / arbiter) подтверждает: primary действительно мёртв, а не вы потеряли связь со здоровой нодой.
Fencing (STONITH) — старый primary насильно выключается (IPMI power-off, отзыв cloud-IAM-токена), чтобы он не ожил и не начал писать.
Promotion — standby проигрывает последние WAL-записи и открывается на запись.
Traffic redirect — DNS / VIP / service-mesh / connection-pool перенаправляет клиентов на нового primary.
Главный враг failover — не отказ оборудования, а split-brain: ситуация, когда обе ноды считают себя primary одновременно. Защита от split-brain — это quorum (нечётное число голосующих узлов) плюс fencing. Без них «high availability» превращается в «high probability of data corruption».
Что показывает диаграмма
На холсте — каноническая двух-ДЦ конфигурация с witness'ом:
DC1 (Primary site) — активный primary с health-probe. Сюда идут все нормальные reads/writes.
DC2 (Standby site) — hot standby и witness (arbiter). Standby получает синхронную репликацию; witness не хранит данные, только голосует.
Client + DNS-слой — клиент резолвит логическое имя db.example.com через DNS с низким TTL. Это «слой непрямой адресации», позволяющий переключить трафик не трогая клиентов.
Ребра — это физические соединения: репликация primary→standby, heartbeat'ы probe→nodes, vote-пинги witness↔nodes, VIP-апдейт standby→DNS. Это типичная топология Patroni + etcd, AWS RDS Multi-AZ или MongoDB replica set с арбитром.
Сценарии
Нормальная работа: sync-репликация даёт RPO=0. Каждый коммит дожидается ack от standby перед тем как вернуть клиенту 200. Если standby тормозит — primary тоже тормозит (это сознательная цена за нулевую потерю данных). Health-probe гоняет heartbeat'ы по обоим узлам.
Primary падает. Probe фиксирует 3 пропущенных heartbeat'а — это важно: 1 пропуск ничего не значит (network jitter, GC pause). Дальше witness подтверждает кворум (2 голоса из 3), отдаётся STONITH-команда на принудительное отключение primary, standby проигрывает остаток WAL и промотируется. Standby обновляет A-запись в DNS, клиент перерезолвит после истечения TTL и ретраит. Типичный RTO такого сценария — 10-30 секунд.
Сеть между ДЦ рвётся. Каждая сторона думает, что вторая мертва. Без защиты — оба промотируются, оба принимают writes, через час у вас два расходящихся истории и невосстановимая каша. С защитой: standby + witness образуют кворум 2/3, primary остаётся в меньшинстве 1/3 и сам себя демотирует (refuses writes). Это и есть суть quorum-based failover.
Старый primary починили. Нельзя просто включить и сказать «ты снова главный» — он отстал на N часов. Сначала он бутстрапится как follower, догоняет WAL до текущей позиции, потом во время плановой maintenance window происходит контролируемый switchover: drain → handoff → VIP move. Failback всегда плановый, никогда — автоматический.
Trade-offs (ADR)
На диаграмме закреплены два ключевых решения, доступных через кнопку [DECISIONS] на нодах:
ADR-001: Hot vs warm vs cold standby — выбирается строго по бизнес-RTO/RPO, не по «техническому интересу». Hot standby для транзакционного ядра, warm для аналитики и внутренних тулов, cold (S3 + IaC) для dev/test. Cost растёт не линейно: hot standby — это второй кластер целиком плюс синхронная репликация по дорогому каналу между AZ/регионами.
ADR-002: Witness и split-brain prevention — два узла никогда не дадут корректного failover. Нужно либо три полноценных узла (Raft/Paxos), либо два узла + lightweight witness в третьем failure domain. Witness не должен делить судьбу ни с одним из data-узлов: если витнес стоит в том же AZ, что и primary, AZ-outage уносит quorum целиком.
Дополнительные неявные трейд-оффы:
Sync vs async replication. Sync даёт RPO=0, но primary тормозит на скорость standby. Async даёт latency, но при failover теряются unacked-транзакции.
DNS TTL. Низкий TTL (30s) ускоряет failover, но грузит DNS и ломает кеши. Высокий TTL (5 мин) экономит, но клиенты после failover до 5 минут стучатся в мёртвый адрес.
Aggressive vs conservative detection. Маленький timeout = быстрый failover, но много ложных срабатываний (flapping). Большой timeout = меньше false positives, но дольше downtime.
Реальные системы
PostgreSQL: Patroni + etcd — каноническая OSS-связка. Patroni-агент на каждой ноде, leader-выборы через etcd. Auto-failover, REST API, интеграция с HAProxy/Consul для traffic redirect.
AWS RDS Multi-AZ — managed warm standby, асинхронная репликация на standby в другом AZ. Failover за 60-120 секунд через DNS-флип. Multi-AZ Cluster (новее) — синхронная репликация на 2 reader'а, RTO < 35s.
AWS Aurora — storage-level replication через shared distributed storage. Compute failover за секунды, потому что не нужно копировать данные.
MongoDB replica set — 3+ узла или 2 data + 1 arbiter. Primary избирается через Raft-подобный протокол. Auto-failover из коробки.
Redis Sentinel — отдельный набор sentinel-процессов мониторит master/replica и оркестрирует failover. Sentinel'ов должно быть ≥3 для quorum.
Redis Cluster — sharded по slot'ам, каждый шард — primary + N реплик, failover внутри шарда.
Kafka controller — KRaft (новый) использует Raft для controller-выборов; раньше эту роль играл ZooKeeper-ансамбль.
Kubernetes — control-plane HA: 3+ etcd-узла + N apiserver'ов за load-balancer'ом. Failover на уровне etcd через Raft, на уровне apiserver — health-check LB.
DNS-уровень: Route53 health checks + failover routing policy, или active-active с weighted routing.
Anti-patterns
Two-node cluster без witness. Гарантированный split-brain при разрыве сети. Либо три ноды, либо два + arbiter в третьем failure domain.
Witness в том же AZ/rack, что и одна из data-нод. Outage домена уносит quorum целиком, кластер становится недоступен даже для read.
Auto-failback (старый primary починился → автоматически становится primary). Никогда не делайте. Failback должен быть плановым и наблюдаемым.
DNS TTL 24h в HA-конфигурации. Низкий TTL — обязательное условие фастового failover, иначе клиенты ходят к мёртвому адресу.
Один heartbeat-канал. Если probe ходит только по той же сети, что и репликация — partition сети одновременно убивает и видимость, и репликацию, и вы не отличите «мёртв» от «изолирован».
Игнорирование fencing (STONITH). Старый primary может ожить (kernel разморозился после GC pause) и начать принимать writes параллельно с новым. Принудительное отключение — обязательно.
Слишком агрессивный timeout (200ms на heartbeat). GC pause / network blip → ложный failover, flapping, real downtime больше чем было бы без HA.
«У нас есть бэкапы, нам failover не нужен». Бэкап — это RPO часы и RTO часы. Это disaster recovery, не high availability. Это разные инструменты для разных задач.
Тестирование failover только в проде, и только во время реального инцидента. GameDay'и / chaos engineering — единственный способ убедиться, что цепочка из 5 фаз действительно работает.
Когда НЕ использовать
Прототипы и MVP. До product-market fit стоимость HA-инфраструктуры (двойной кластер, witness, мониторинг) не окупается. Single-node + регулярные бэкапы + готовность к 1-2 часам downtime — нормально.
Stateless-сервисы. Им нужен load balancer + health check + auto-scaling, а не failover в смысле этой статьи. Failover — про state (БД, очереди, координационные сервисы).
Batch / offline pipelines. Если ETL может стартануть с последнего checkpoint через 30 минут — это retry, а не failover.
Когда RTO/RPO допускают ручное переключение. Если бизнес готов к 30 минутам downtime раз в год, ручной runbook + warm standby проще, дешевле и надёжнее, чем хрупкая auto-failover-машинерия.
Когда нет команды, чтобы это эксплуатировать. HA-кластер без on-call ротации, chaos drills и observability — это не HA, это more failure surface.