Patroni + PostgreSQL: отказоустойчивый кластер, который пережил отключение электричества
Семёнов Евгений Сергеевич, директор АйТи Фреш. 15+ лет в инфраструктуре. PostgreSQL в 2025 году — основная БД для многих корпоративных приложений: 1С, Битрикс24, самописные CRM, ЭДО. Но одиночный PostgreSQL — это single point of failure: упал диск или сервер — и бизнес встал на 2–6 часов восстановления из бэкапа. Patroni закрывает этот вопрос. В статье — рабочая схема, которую мы выкатили трём клиентам за последний год, с разбором реальных проблем и того, что помогло их решить.
Зачем Patroni
Сам PostgreSQL поддерживает потоковую репликацию и базовый hot standby — реплика готова принять трафик при падении мастера. Но переключение вручную: обнаружить падение, промоутить реплику, перевести клиентов. Это 10–20 минут при идеальных обстоятельствах и полное отсутствие автоматики ночью в выходные.
Patroni добавляет:
- Автоматическое обнаружение падения мастера через etcd/Consul.
- Автоматический выбор лидера через distributed lock.
- Автоматическое восстановление упавшего мастера как реплики после возврата.
- REST API для мониторинга состояния каждого узла.
- Интеграция с HAProxy/PgBouncer для автоматической маршрутизации клиентов.
- pg_rewind для быстрого восстановления реплик без полного бэкапа.
Архитектура минимального кластера
Минимальная production-конфигурация, которую я ставлю клиентам:
- 3 узла — pg01, pg02, pg03. На каждом PostgreSQL 15/16, Patroni и etcd.
- etcd cluster из 3 нод для кворума (без него — split-brain).
- HAProxy на отдельной виртуалке или двух с keepalived VIP — точка входа для приложений.
- PgBouncer (опционально) за HAProxy для пуллинга соединений.
Характеристики узла БД для офиса до 50 пользователей 1С: 8 vCPU, 32 ГБ RAM, 500 ГБ NVMe. У нас на стенде — Dell Xeon Platinum 8280, 40G Mellanox между узлами, что даёт repl-lag ниже 5 мс при пиковой нагрузке.
Установка etcd-кластера
etcd — распределённое хранилище ключ-значение, используется Patroni для выбора лидера. Ставим на все 3 узла (Ubuntu 22.04):
# На всех узлах
apt install -y etcd-server etcd-client
# /etc/default/etcd на pg01 (аналогично на pg02/pg03, меняйте IP и имя)
ETCD_NAME="pg01"
ETCD_DATA_DIR="/var/lib/etcd/default"
ETCD_LISTEN_PEER_URLS="http://10.10.50.11:2380"
ETCD_LISTEN_CLIENT_URLS="http://10.10.50.11:2379,http://127.0.0.1:2379"
ETCD_INITIAL_ADVERTISE_PEER_URLS="http://10.10.50.11:2380"
ETCD_INITIAL_CLUSTER="pg01=http://10.10.50.11:2380,pg02=http://10.10.50.12:2380,pg03=http://10.10.50.13:2380"
ETCD_INITIAL_CLUSTER_TOKEN="pg-cluster-prod"
ETCD_INITIAL_CLUSTER_STATE="new"
ETCD_ADVERTISE_CLIENT_URLS="http://10.10.50.11:2379"
systemctl enable --now etcd
# Проверка
etcdctl member list
etcdctl endpoint health --endpoints=http://pg01:2379,http://pg02:2379,http://pg03:2379
Установка PostgreSQL и Patroni
На всех узлах:
apt install -y postgresql-16 postgresql-server-dev-16 python3-pip
systemctl disable --now postgresql
rm -rf /var/lib/postgresql/16/main
pip3 install psycopg2-binary patroni[etcd]
# или через apt: apt install patroni
Конфиг Patroni для pg01 — /etc/patroni/patroni.yml:
scope: pg-cluster-prod
namespace: /service/
name: pg01
restapi:
listen: 10.10.50.11:8008
connect_address: 10.10.50.11:8008
etcd:
hosts: 10.10.50.11:2379,10.10.50.12:2379,10.10.50.13:2379
bootstrap:
dcs:
ttl: 30
loop_wait: 10
retry_timeout: 10
maximum_lag_on_failover: 1048576
synchronous_mode: true
postgresql:
use_pg_rewind: true
parameters:
max_connections: 500
shared_buffers: 8GB
effective_cache_size: 24GB
work_mem: 16MB
maintenance_work_mem: 1GB
wal_level: replica
hot_standby: "on"
max_wal_senders: 10
max_replication_slots: 10
wal_log_hints: "on"
archive_mode: "on"
archive_command: "test ! -f /var/lib/postgresql/archive/%f && cp %p /var/lib/postgresql/archive/%f"
initdb:
- encoding: UTF8
- locale: ru_RU.UTF-8
- data-checksums
pg_hba:
- host replication replicator 10.10.50.0/24 scram-sha-256
- host all all 10.10.0.0/16 scram-sha-256
users:
admin:
password: ********
options:
- createrole
- createdb
replicator:
password: ********
options:
- replication
postgresql:
listen: 0.0.0.0:5432
connect_address: 10.10.50.11:5432
data_dir: /var/lib/postgresql/16/main
bin_dir: /usr/lib/postgresql/16/bin
authentication:
replication:
username: replicator
password: ********
superuser:
username: postgres
password: ********
tags:
nofailover: false
noloadbalance: false
clonefrom: false
nosync: false
На pg02 и pg03 — аналогичный файл, меняется только name, listen и connect_address.
Первый запуск
# Systemd-юнит
cat > /etc/systemd/system/patroni.service << 'EOF'
[Unit]
Description=Patroni
After=syslog.target network.target etcd.service
[Service]
Type=simple
User=postgres
Group=postgres
ExecStart=/usr/bin/patroni /etc/patroni/patroni.yml
KillMode=process
TimeoutSec=30
Restart=no
[Install]
WantedBy=multi-user.target
EOF
chown -R postgres:postgres /etc/patroni /var/lib/postgresql/16
systemctl daemon-reload
systemctl enable --now patroni
# Проверка состояния
patronictl -c /etc/patroni/patroni.yml list
Запускаем сначала pg01 — он становится лидером. Потом pg02 и pg03 — они подтягивают базу как реплики.
HAProxy для маршрутизации
HAProxy на двух отдельных виртуалках (hap01, hap02) с общим VIP через keepalived. Конфиг /etc/haproxy/haproxy.cfg:
global
maxconn 500
defaults
mode tcp
timeout connect 10s
timeout client 1d
timeout server 1d
listen pg_write
bind *:5432
option httpchk GET /leader
http-check expect status 200
default-server check port 8008 inter 3s rise 2 fall 3 on-marked-down shutdown-sessions
server pg01 10.10.50.11:5432
server pg02 10.10.50.12:5432
server pg03 10.10.50.13:5432
listen pg_read
bind *:5433
balance roundrobin
option httpchk GET /replica
http-check expect status 200
default-server check port 8008 inter 3s rise 2 fall 3
server pg01 10.10.50.11:5432
server pg02 10.10.50.12:5432
server pg03 10.10.50.13:5432
Порт 5432 на VIP всегда ведёт на текущий мастер, 5433 — балансирует между репликами для read-only запросов. Приложения подключаются к VIP:5432 для записи и VIP:5433 для read-only отчётов.
Тестирование failover
Всегда перед продакшеном нужно погонять failover-сценарии:
| Сценарий | Ожидаемое поведение |
|---|---|
| Ручной switchover | Мастер переходит на другой узел за 5–10 секунд, старый становится репликой |
| Kill -9 PostgreSQL на мастере | Patroni перезапускает процесс, мастер не меняется |
| Systemctl stop patroni на мастере | Новый мастер выбирается за 15–30 секунд, HAProxy переключает трафик |
| Жёсткое выключение хоста мастера | То же, что выше, но без graceful shutdown |
| Network partition (1 узел) | Изолированный узел становится репликой в read-only |
| Network partition (2 узла) | Одиночный узел останавливает PostgreSQL — нет кворума |
# Тесты
patronictl -c /etc/patroni/patroni.yml switchover --master pg01 --candidate pg02
patronictl -c /etc/patroni/patroni.yml failover --candidate pg03
patronictl -c /etc/patroni/patroni.yml list
patronictl -c /etc/patroni/patroni.yml history
Мини-кейс: HA для 1С в оптовой торговле
В марте 2026 года клиент — оптовый поставщик электроники, 95 пользователей 1С ERP, база 680 ГБ. Требование — минимальный простой при сбоях, SLA 99,9% в часы работы (8–20). База на одном PostgreSQL 14 падала раз в квартал из-за проблем с дисками, каждый простой — 2–3 часа работы ста пользователей.
Решение: трёхузловой кластер Patroni на Dell R650 (dual Xeon Gold, 128 ГБ, 1.5 ТБ NVMe) в дата-центре МТС, между узлами — 40G Mellanox. Миграция:
- Неделя 1: развёртывание тестового кластера, замеры производительности, тюнинг shared_buffers, work_mem под нагрузку 1С.
- Неделя 2: нагрузочное тестирование через pgbench — 8000 TPS на кластере с sync-репликацией.
- Неделя 3: миграция данных через pg_basebackup, настройка репликации с существующего PostgreSQL.
- Неделя 4: переключение пользователей в окно обслуживания (45 минут), мониторинг в течение недели.
Итог за 3 месяца: один незапланированный failover (упал диск на мастере в среду в 14:30), переключение заняло 22 секунды, пользователи заметили только повторную попытку проведения документа. Стоимость проекта — 420 000 руб. за внедрение + 28 000 руб./мес. сопровождение.
Синхронная репликация
По умолчанию Patroni работает в асинхронном режиме — failover может потерять несколько последних транзакций. Для финансовых систем это недопустимо. Включаем sync:
bootstrap:
dcs:
synchronous_mode: true
synchronous_mode_strict: true
synchronous_node_count: 1
В синхронном режиме мастер ждёт подтверждения от указанного числа реплик перед коммитом. В обмен на гарантию сохранности — небольшое снижение latency, обычно на 2–5 мс в LAN. synchronous_mode_strict запрещает коммит вовсе, если нет синхронной реплики.
Мониторинг кластера
Я всегда ставлю комбо prometheus + postgres_exporter + patroni_exporter + grafana:
# Prometheus scrape configs
- job_name: 'patroni'
static_configs:
- targets: ['10.10.50.11:8008','10.10.50.12:8008','10.10.50.13:8008']
metrics_path: /metrics
- job_name: 'postgres'
static_configs:
- targets: ['10.10.50.11:9187','10.10.50.12:9187','10.10.50.13:9187']
Ключевые метрики для мониторинга: pg_up, pg_replication_lag, patroni_cluster_unlocked, pg_locks_count, pg_database_size_bytes, pg_stat_activity_count.
Частые ошибки при внедрении Patroni
- Два узла вместо трёх. При четырёх нет кворума в etcd — выбора лидера не будет.
- etcd на тех же дисках что PostgreSQL. При IO-нагрузке etcd зависает, Patroni думает что сервер упал.
- maximum_lag_on_failover слишком маленький. Если реплика отстала на 2 МБ — failover не произойдёт даже при падении мастера.
- Нет архивов WAL. При длительной изоляции реплика отстанет и потребует full basebackup.
- Не тестируют failover. «Всё работает, не трогай» — классическая ошибка. Тестируйте ежеквартально.
Построим PostgreSQL HA-кластер под ключ
Внедрение Patroni-кластеров PostgreSQL для 1С, Битрикс24, самописных систем. Проектирование, железо, настройка, нагрузочное тестирование, миграция данных, мониторинг, сопровождение. 15+ лет опыта с БД, SLA 99.9%.
Телефон: +7 903 729-62-41
Telegram: @ITfresh_Boss
Семёнов Евгений Сергеевич, директор АйТи Фреш
FAQ — частые вопросы про Patroni
- Что такое Patroni?
- Patroni — менеджер для PostgreSQL, написанный Zalando на Python. Автоматизирует управление репликацией, выбирает лидера через распределённый консенсус (etcd/Consul/ZooKeeper), выполняет автоматический failover и восстановление реплик.
- Сколько узлов нужно для кластера?
- Минимум 3 узла для etcd (для кворума) и 2+ узла PostgreSQL для репликации. Типовая конфигурация — 3 хоста, на каждом свой etcd и свой PostgreSQL. Для production лучше 3 PostgreSQL + 3 etcd на разных хостах.
- Какая задержка failover?
- При правильной настройке Patroni переключает мастер за 10–30 секунд от момента падения. Timeout можно уменьшить, но ниже 5 секунд — риск ложных срабатываний из-за сетевых задержек.
- Нужен ли HAProxy для Patroni?
- Необязательно, но рекомендуется. HAProxy проверяет роль каждого узла через HTTP API Patroni и направляет трафик на текущего лидера. Альтернативы — Keepalived с VIP, pgbouncer с настройкой, PgCat.
- Можно ли использовать Patroni без etcd?
- Да, Patroni поддерживает Consul, ZooKeeper, Kubernetes API и raw storage. Но etcd — самый простой и популярный вариант для bare-metal и VM-инсталляций, имеет минимальный overhead.