Настройка автоматического Failover базы данных
Автоматический failover — механизм, который обнаруживает недоступность мастер-сервера БД и автоматически повышает реплику до мастера без ручного вмешательства. Цель: снизить RTO (Recovery Time Objective) с десятков минут до секунд.
Инструменты для PostgreSQL
Patroni — стандарт de facto
Patroni — Python-демон, работающий на каждом узле PostgreSQL. Использует DCS (Distributed Consensus Store: etcd, Consul, ZooKeeper) для координации и выбора лидера.
# /etc/patroni/patroni.yml (на каждом узле)
scope: production-cluster
namespace: /service/
name: node1
restapi:
listen: 0.0.0.0:8008
connect_address: 192.168.1.10:8008
etcd3:
hosts: 192.168.1.20:2379,192.168.1.21:2379,192.168.1.22:2379
bootstrap:
dcs:
ttl: 30
loop_wait: 10
retry_timeout: 30
maximum_lag_on_failover: 1048576 # 1MB максимальный лаг при failover
synchronous_mode: false
initdb:
- encoding: UTF8
- data-checksums
postgresql:
listen: 0.0.0.0:5432
connect_address: 192.168.1.10:5432
data_dir: /var/lib/postgresql/14/main
authentication:
replication:
username: replication
password: replication_password
superuser:
username: postgres
password: postgres_password
parameters:
max_connections: 200
shared_buffers: 256MB
wal_level: replica
hot_standby: on
wal_log_hints: on
tags:
nofailover: false
noloadbalance: false
Запуск:
systemctl start patroni
Проверка состояния кластера:
patronictl -c /etc/patroni/patroni.yml list
# Покажет лидера и реплики с лагом
Ручной switchover (планируемое переключение):
patronictl -c /etc/patroni/patroni.yml switchover production-cluster
etcd для координации
# etcd cluster на трёх серверах
etcd --name etcd1 \
--data-dir /var/lib/etcd \
--listen-peer-urls http://192.168.1.20:2380 \
--listen-client-urls http://192.168.1.20:2379 \
--initial-advertise-peer-urls http://192.168.1.20:2380 \
--advertise-client-urls http://192.168.1.20:2379 \
--initial-cluster "etcd1=http://192.168.1.20:2380,etcd2=http://192.168.1.21:2380,etcd3=http://192.168.1.22:2380" \
--initial-cluster-state new
HAProxy для прозрачного переключения
Patroni предоставляет healthcheck endpoint:
-
GET /master→ 200 если узел лидер -
GET /replica→ 200 если узел реплика
# haproxy.cfg
frontend postgres_write
bind *:5000
default_backend postgres_master
frontend postgres_read
bind *:5001
default_backend postgres_replicas
backend postgres_master
option httpchk GET /master
http-check expect status 200
server node1 192.168.1.10:5432 check port 8008 inter 2s fall 3 rise 2
server node2 192.168.1.11:5432 check port 8008 inter 2s fall 3 rise 2
server node3 192.168.1.12:5432 check port 8008 inter 2s fall 3 rise 2
backend postgres_replicas
balance leastconn
option httpchk GET /replica
http-check expect status 200
server node1 192.168.1.10:5432 check port 8008 inter 2s fall 3 rise 2
server node2 192.168.1.11:5432 check port 8008 inter 2s fall 3 rise 2
server node3 192.168.1.12:5432 check port 8008 inter 2s fall 3 rise 2
MySQL: MySQL InnoDB Cluster
MySQL Router + Group Replication — официальное решение Oracle:
# Инициализация кластера (MySQL Shell)
mysqlsh [email protected]:3306
JS> dba.createCluster('myCluster')
JS> cluster = dba.getCluster()
JS> cluster.addInstance('[email protected]:3306')
JS> cluster.addInstance('[email protected]:3306')
JS> cluster.status()
Настройка MySQL Router:
mysqlrouter --bootstrap [email protected]:3306 --directory /etc/mysqlrouter
Router автоматически направляет запись на primary и обновляет маршрутизацию при failover.
Оркестратор (Orchestrator) для MySQL
GitHub Orchestrator — зрелый инструмент для управления топологией MySQL репликации:
{
"MySQLTopologyUser": "orchestrator",
"MySQLTopologyPassword": "orchestrator_pass",
"BackendDB": "sqlite",
"SQLite3DataFile": "/var/lib/orchestrator/orchestrator.db",
"RecoverMasterClusterFilters": ["*"],
"PromotionIgnoreHostnameFilters": [],
"RecoveryEnabled": true,
"RecoveryPeriodBlockSeconds": 300,
"OnFailureDetectionProcesses": [
"echo 'Detected {failureType} on {failedHost}' | mail -s 'DB Failover' [email protected]"
],
"PostMasterFailoverProcesses": [
"/opt/scripts/update-app-config.sh {successorHost} {successorPort}"
]
}
Уведомления и post-failover скрипты
После failover приложение должно знать новый адрес мастера. Варианты:
- VIP (Virtual IP) — плавающий IP, переключается между узлами через Keepalived/Pacemaker
- DNS с коротким TTL — обновление A-записи через API (Route53, Cloudflare)
- HAProxy — приложение всегда подключается к HAProxy, который знает текущего лидера
# Post-failover hook для Patroni
# /etc/patroni/callbacks/on_role_change.sh
#!/bin/bash
ROLE=$1
CLUSTER=$2
if [ "$ROLE" = "master" ]; then
# Обновить DNS
aws route53 change-resource-record-sets \
--hosted-zone-id $HOSTED_ZONE_ID \
--change-batch "{\"Changes\":[{\"Action\":\"UPSERT\",\"ResourceRecordSet\":{\"Name\":\"db-master.internal\",\"Type\":\"A\",\"TTL\":10,\"ResourceRecords\":[{\"Value\":\"$(hostname -I | awk '{print $1}')\"}]}}]}"
fi
Тестирование failover
# Тест: убиваем текущий мастер
patronictl -c /etc/patroni/patroni.yml failover production-cluster --force
# Мониторинг переключения
watch -n 1 patronictl -c /etc/patroni/patroni.yml list
# Замер времени недоступности (из приложения)
while ! psql -h db-master.internal -U app myapp -c "SELECT 1" 2>/dev/null; do
echo "$(date): waiting..."
sleep 0.5
done
Типичное время failover с Patroni + etcd: 10–30 секунд.
Срок выполнения
Настройка Patroni кластера на 3 узлах с HAProxy и etcd — 3–4 рабочих дня.







