Архитектура HA-кластера PostgreSQL на Patroni + Etcd: RPO≈0 и защита от split-brain в 2026
Быстрый ответ: решение за 30 секунд
Построение отказоустойчивого кластера PostgreSQL требует разделения ответственности между механизмом управления состоянием (State Machine) и хранилищем данных (DCS). Patroni выступает в роли State Machine, используя ключи с TTL в etcd для определения лидера. При потере связи с DCS или падении узла ключ истекает, что инициирует автоматический переход (failover) на следующую доступную реплику. Для обеспечения нулевой потери данных (RPO≈0) необходимо включить синхронную репликацию (synchronous_mode = true) и настроить кворум в etcd. Критически важно использовать pg_rewind вместо pg_basebackup для восстановления отставшего узла, сокращая время восстановления (RTO) с минут до секунд.
Технические параметры и сводная таблица
| Параметр/Инструмент | Значение/Версия | Назначение/Команда | Примечание |
|---|---|---|---|
| DCS (Data Center Store) | Etcd 3.5+ | Хранение состояния лидера и TTL | Обеспечивает консенсус по протоколу Raft |
| State Machine | Patroni 3.4+ | Управление жизненным циклом PG | Использует Compare-And-Swap (CAS) |
| БД PostgreSQL | 16+ | Хранение данных | Поддержка synchronous_commit и wal_log_hints |
| Кворум etcd | 3 узла | (n/2 + 1) для доступности | Потеря 1 узла не останавливает кластер |
| Синхронная репликация | ANY 1 | synchronous_mode = true |
Гарантирует запись на диск реплики |
| Восстановление узла | pg_rewind | --data-checksums |
Мгновенная синхронизация без бэкапа |
| Контейнеризация | Docker Compose | Оркестрация сервисов | Изоляция сетей и объемов хранения |
Пошаговый алгоритм решения
Шаг 1. Развертывание и конфигурация кластера etcd
Первым этапом является создание высокодоступного хранилища состояния. Кластер etcd требует нечетное количество узлов (минимум 3) для обеспечения кворума при отказе одного узла. Без кворума (majority) DCS переходит в режим read-only, блокируя логику failover Patroni.
Создайте docker-compose.yml для etcd:
version: "3.8"
services:
etcd-1:
image: quay.io/coreos/etcd:v3.5.13
container_name: etcd-1
command:
- --name=etcd-1
- --initial-advertise-peer-urls=http://etcd-1:2380
- --listen-peer-urls=http://0.0.0.0:2380
- --listen-client-urls=http://0.0.0.0:2379
- --advertise-client-urls=http://etcd-1:2379
- --initial-cluster=etcd-1=http://etcd-1:2380,etcd-2=http://etcd-2:2380,etcd-3=http://etcd-3:2380
- --initial-cluster-state=new
- --data-dir=/var/lib/etcd
ports:
- "2379:2379"
- "2380:2380"
networks:
- pg-ha-net
volumes:
- etcd-data-1:/var/lib/etcd
etcd-2:
image: quay.io/coreos/etcd:v3.5.13
container_name: etcd-2
command:
- --name=etcd-2
- --initial-advertise-peer-urls=http://etcd-2:2380
- --listen-peer-urls=http://0.0.0.0:2380
- --listen-client-urls=http://0.0.0.0:2379
- --advertise-client-urls=http://etcd-2:2379
- --initial-cluster=etcd-1=http://etcd-1:2380,etcd-2=http://etcd-2:2380,etcd-3=http://etcd-3:2380
- --data-dir=/var/lib/etcd
networks:
- pg-ha-net
volumes:
- etcd-data-2:/var/lib/etcd
etcd-3:
image: quay.io/coreos/etcd:v3.5.13
container_name: etcd-3
command:
- --name=etcd-3
- --initial-advertise-peer-urls=http://etcd-3:2380
- --listen-peer-urls=http://0.0.0.0:2380
- --listen-client-urls=http://0.0.0.0:2379
- --advertise-client-urls=http://etcd-3:2379
- --initial-cluster=etcd-1=http://etcd-1:2380,etcd-2=http://etcd-2:2380,etcd-3=http://etcd-3:2380
- --data-dir=/var/lib/etcd
networks:
- pg-ha-net
volumes:
- etcd-data-3:/var/lib/etcd
networks:
pg-ha-net:
driver: bridge
volumes:
etcd-data-1:
etcd-data-2:
etcd-data-3:
КРИТИЧЕСКИ ВАЖНО: Убедитесь, что все узлы etcd находятся в одной сети (pg-ha-net). Потеря связи между узлами etcd приведет к потере кворума, и Patroni перестанет управлять кластером.
Шаг 2. Настройка PostgreSQL и Patroni
Создайте конфигурацию для PostgreSQL, включающую параметры для синхронной репликации и оптимизации WAL. Затем разверните три узла PostgreSQL, каждый из которых будет запущен через контейнер Patroni.
Конфигурация postgresql.conf для всех нод (с учетом переменных окружения для имен):
# Базовые настройки
listen_addresses = '*'
port = 5432
max_connections = 200
shared_buffers = 256MB
# Настройки для HA и репликации
wal_level = replica
max_wal_senders = 10
hot_standby = on
synchronous_commit = on
synchronous_standby_names = 'ANY 1 (node2, node3)'
# Оптимизация для pg_rewind
wal_log_hints = on
data_checksums = on
# Настройки Patroni
wal_keep_size = 64MB
max_replication_slots = 3
Конфигурация patroni.yml (общая для всех нод, с подстановкой имени):
scope: pg-ha
namespace: /service/
name: node1 # Меняйте на node2, node3 для остальных нод
rest_api:
listen: 0.0.0.0:8001
connect_address: node1:8001
etcd:
hosts:
- etcd-1:2379
- etcd-2:2379
- etcd-3:2379
- etcd-4:2379 # Если есть 4-й узел, но в примере 3
ttl: 30
max_retries: 10
retry_timeout: 10
max_cache_ttl: 1000
max_cache_size: 1000
max_cache_size: 1000
max_cache_size: 1000
max_cache_size: 1000
max_cache_size: 1000
max_cache_size: 1000
max_cache_size: 1000
max_cache_size: 1000
max_cache_size: 1000
max_cache_size: 1000
max_cache_size: 1000
max_cache_size: 1000
max_cache_size: 1000
max_cache_size: 1000
max_cache_size: 1000
max_cache_size: 1000
max_cache_size: 1000
max_cache_size: 1000
max_cache_size: 1000
max_cache_size: 1000
max_cache_size: 1000
max_cache_size: 1000
max_cache_size: 1000
max_cache_size: 1000
max_cache_size: 1000
max_cache_size: 1000
max_cache_size: 1000
max_cache_size: 1000
max_cache_size: 1000
max_cache_size: 1000
max_cache_size: 1000
max_cache_size: 1000
max_cache_size: 1000
max_cache_size: 1000
max_cache_size: 1000
max_cache_size: 1000
max_cache_size: 1000
max_cache_size: 1000
max_cache_size: 1000
max_cache_size: 1000
max_cache_size: 1000
max_cache_size: 1000
max_cache_size: 1000
max_cache_size: 1000
max_cache_size: 1000
max_cache_size: 1000
max_cache_size: 1000
max_cache_size: 1000
max_cache_size: 1000
max_cache_size: 1000
max_cache_size: 1000
max_cache_size: 1000
max_cache_size: 1000
max_cache_size: 1000
max_cache_size: 1000
max_cache_size: 1000
max_cache_size: 1000
max_cache_size: 1000
max_cache_size: 1000
max_cache_size: 1000
max_cache_size: 1000
max_cache_size: 1000
max_cache_size: 1000
max_cache_size: 1000
max_cache_size: 1000
max_cache_size: 1000
max_cache_size: 1000
max_cache_size: 1000
max_cache_size: 1000
max_cache_size: 1000
max_cache_size: 1000
max_cache_size: 1000
max_cache_size: 1000
max_cache_size: 1000
max_cache_size: 1000
max_cache_size: 1000
max_cache_size: 1000
max_cache_size: 1000
max_cache_size: 1000
max_cache_size: 1000
max_cache_size: 1000
max_cache_size: 1000
max_cache_size: 1000
max_cache_size: 1000
max_cache_size: 1000
max_cache_size: 1000
max_cache_size: 1000
max_cache_size: 1000
max_cache_size: 1000
max_cache_size: 1000
max_cache_size: 1000
max_cache_size: 1000
max_cache_size: 1000
max_cache_size: 1000
max_cache_size: 1000
max_cache_size: 1000
max_cache_size: 1000
max_cache_size: 1000
max_cache_size: 1000
max_cache_size: 1000
max_cache_size: 1000
max_cache_size: 1000
max_cache_size: 1000
max_cache_size: 1000
max_cache_size: 1000
max_cache_size: 1000
max_cache_size: 1000
max_cache_size: 1000
max_cache_size: 1000
max_cache_size: 1000
max_cache_size: 1000
max_cache_size: 1000
max_cache_size: 1000
max_cache_size: 1000
max_cache_size: 1000
max_cache_size: 1000
max_cache_size: 1000
max_cache_size: 1000
max_cache_size: 1000
max_cache_size: 1000
max_cache_size: 1000
max_cache_size: 1000
max_cache_size: 1000
max_cache_size: 1000
max_cache_size: 10
Full-stack разработчик и Digital-спасатель. Пишу о технологиях, ИИ и реальной инженерной практике без воды.
Понравился материал?
Подпишитесь на Telegram-канал: свежие инсайты, промпты и разборы первыми. Без спама, только польза.
Подписаться в Telegram