Workstation Logo
Продукты
AI LabsАгенты OpenAIАгенты ClaudeGrok BotWorkstation CRM (WSL CRM)МаркетингВсе продукты
Решения ИИ
Рабочие станции ИИAI SME PackagesЧастный ИИКластеры GPUПограничный ИИЛаборатория корпоративного ИИИИ по отраслям
Услуги
Platform ModernisationDigital EngineeringData Foundations & AIAutonomous OperationsИИ-консалтингАвтоматизация DevOpsКибербезопасностьРазработка ПОСоздание агентовНастройка MLOps
О нас
ПартнёрыИстории клиентов
Статьи
Документация
WSL ProxyRing PromoterWSL VaultJobshoutSysOps 24/7
Блог
Связаться с намиLogin
Workstation

AI-рабочие станции, мультиагентное AI-ПО, GPU-инфраструктура и решения на базе интеллектуальных агентов для современного бизнеса.

Связаться с нами

AI-решения

Рабочие станции ИИAI SME PackagesЧастный ИИКластеры GPUПограничный ИИЛаборатория корпоративного ИИИИ по отраслям

Продукты

Все продуктыWSL CRM и ERPМаркетингАгенты OpenAIWSL ProxyRing PromoterWSL VaultJobshoutSysOps 24/7

Компания

О насПочему WorkstationПартнёрыИстории клиентовЦеныКонтакты

Ресурсы

СтатьиДокументацияБлогПоискКарта сайта
Офис в Великобритании
77-79 Marlowes, Hemel Hempstead HP1 1LFКак добраться: съезд 20 с трассы M25, Внешний ЛондонРег. номер компании: 11641870Пн - Пт: 9:00 - 18:00 GMT
+44 7515 356 146
Офис в Бельгии
Workstation SRL, Rue Vanderkindere 34, 1180 Uccle, BrusselsBE 0751.518.683Пн - Пт: 9:00 - 18:00 CET
+32 492 45 67 46
Офис в Индии
#159 Sector 9, Pocket 1, DDA Flats, 110077 Dwarka, New Delhi
+91 98881 98841

© 2026 Workstation AI. Все права защищены.

КонфиденциальностьФайлы cookieУсловия использованияКарта сайта

Loading blog...

Home / Blog
DatabaseKubernetesDevOpsBackend

Встроенная высокая доступность PostgreSQL: Patroni, потоковая репликация и стратегии аварийного переключения на производстве

PostgreSQL Native HA с Patroni, потоковой репликацией и HAProxy

Balinder Walia12 апреля 2026 г.30 min read

Запустить один экземпляр PostgreSQL несложно до тех пор, пока первый незапланированный сбой не напомнит вам, что ценность базы данных зависит от ее доступности. Сбои дисков, паника ядра, сетевые разделы и неудачные обновления не являются теоретическими рисками — это уверенность в работе на достаточно длительном графике. PostgreSQL не поставляется со встроенной функцией автоматического переключения при сбое, но предоставляет все примитивы репликации, необходимые для создания высокодоступного кластера. Patroni, платформа высокой доступности с открытым исходным кодом, поддерживаемая Zalando, объединяет эти примитивы в систему аварийного переключения производственного уровня, которая прошла боевые испытания в тысячах кластеров PostgreSQL по всему миру.

Эта статья представляет собой подробное руководство по проектированию. Мы рассмотрим потоковую репликацию PostgreSQL (синхронную и асинхронную), архитектуру и конфигурацию Patroni и т. д. в качестве распределенного хранилища конфигурации, HAProxy для маршрутизации соединений с разделением чтения и записи, PgBouncer для создания пула соединений, архивации WAL и восстановления на определенный момент времени, логическую репликацию для выборочной синхронизации данных, pg_basebackup для первоначальной резервной подготовки, Repmgr как альтернативу Patroni, шаблоны развертывания для конкретных облаков AWS, Azure и GCP, развертывание k3s без операционной системы с помощью Rancher и Longhorn, мониторинг с помощью pg_stat_replication и Prometheus/Grafana, процедуры переключения и переключения при сбое, предотвращение разделения мозга, настройка производства и планирование хаоса для проверки аварийного переключения.

PostgreSQL Основы потоковой репликации

Потоковая репликация

— это основа высокой доступности PostgreSQL. Он работает путем непрерывной доставки записей журнала упреждающей записи (WAL) с основного сервера на один или несколько резервных серверов. Резервный сервер применяет эти записи WAL в режиме реального времени, сохраняя почти идентичную копию данных основного сервера. Этот механизм был представлен в PostgreSQL 9.0 и совершенствовался в каждой последующей версии.

Существует два режима потоковой репликации:асинхронныйисинхронный. В асинхронном режиме основной сервер не ждет, пока резервные серверы подтвердят получение записей WAL, прежде чем совершить транзакцию. Это обеспечивает максимальную производительность записи, но создает окно потенциальной потери данных — если основной сервер выйдет из строя до того, как резервный получит самый последний WAL, эти транзакции будут потеряны. В синхронном режиме основной сервер ожидает, пока хотя бы один резервный сервер подтвердит, что записи WAL были записаны в постоянное хранилище, прежде чем сообщить о фиксации транзакции. Это исключает потерю данных за счет увеличения задержки фиксации, поскольку каждая запись должна возвращаться в резервный режим.

Выбор между синхронной и асинхронной репликацией не является двоичным. PostgreSQL поддерживаетsynchronous_commitна уровне сеанса, поэтому чувствительные к задержке рабочие нагрузки могут использовать асинхронную фиксацию, в то время как критические финансовые транзакции используют синхронную фиксацию в одном кластере.

Настройка первичного сервера для репликации

Основной сервер должен быть настроен для создания записей WAL на уровне, достаточном для репликации, и для разрешения резервных подключений. Следующие настройкиpostgresql.confявляются обязательными.

# postgresql.conf on the primary
wal_level = replica                    # minimum for streaming replication
max_wal_senders = 10                   # max concurrent replication connections
max_replication_slots = 10             # prevent WAL removal before standby consumption
wal_keep_size = 2GB                    # retain WAL as fallback if slots are unused
hot_standby = on                       # allow read queries on standbys
synchronous_commit = on                # 'on' for sync, 'off' for pure async
synchronous_standby_names = 'ANY 1 (standby1, standby2)'  # sync replication targets
archive_mode = on                      # enable WAL archiving for PITR
archive_command = 'test ! -f /archive/%f && cp %p /archive/%f'
listening_addresses = '*'
port = 5432

Аутентификация для соединений репликации обрабатывается вpg_hba.conf. Соединения репликации используют выделенный тип соединения.

# pg_hba.conf — replication entries
# TYPE   DATABASE        USER            ADDRESS              METHOD
host     replication     replicator      10.0.1.0/24          scram-sha-256
host     replication     replicator      10.0.2.0/24          scram-sha-256
host     all             all             10.0.0.0/16          scram-sha-256

Создайте пользователя репликации на основном сервере.

CREATE ROLE replicator WITH REPLICATION LOGIN PASSWORD 'strong_replication_password';

Обеспечение резервного режима с помощью pg_basebackup

Утилитаpg_basebackupсоздает физическую копию каталога данных первичного сервера, который становится отправной точкой для нового резервного сервера. Он обрабатывает базовое резервное копирование и потоковую передачу WAL атомарно, поэтому полученная копия является согласованной.

# On the standby server
pg_basebackup -h primary-host -U replicator -D /var/lib/postgresql/16/main \
  -Fp -Xs -P -R

# -Fp: plain format
# -Xs: stream WAL during backup
# -P:  show progress
# -R:  create standby.signal and configure primary_conninfo in postgresql.auto.conf

Флаг-Rимеет решающее значение — он записываетprimary_conninfoвpostgresql.auto.confи создаетstandby.signal, который сообщает PostgreSQL о запуске в режиме ожидания. В PostgreSQL 12 и более поздних версияхrecovery.confзаменяется этими двумя механизмами.

# postgresql.auto.conf (generated by pg_basebackup -R)
primary_conninfo = 'host=primary-host port=5432 user=replicator password=strong_replication_password application_name=standby1'
primary_slot_name = 'standby1_slot'

Создайте слот репликации на основном сервере перед запуском резервного сервера, чтобы предотвратить очистку WAL до того, как резервный сервер сможет его использовать.

SELECT pg_create_physical_replication_slot('standby1_slot');
SELECT pg_create_physical_replication_slot('standby2_slot');

Patroni: автоматизированная оркестровка высокой доступности

Потоковая репликация

обеспечивает избыточность данных, но не обеспечивает автоматическое переключение при сбое. В случае сбоя основного устройства кто-то — человек-оператор или система автоматизации — должен повысить статус резервного до основного, перенастроить оставшиеся резервные устройства, чтобы они следовали за новым основным, и обновить маршрутизацию соединения. Patroni все это автоматизирует.

Patroni — это демон Python, который работает вместе с каждым экземпляром PostgreSQL. Он использует распределенное хранилище конфигураций (DCS) — обычно etcd, но также ZooKeeper или Consul — для координации выборов лидера и состояния кластера. Каждый узел Patroni постоянно записывает свое состояние работоспособности в DCS. Если лидеру (основному) не удается обновить свой ключ DCS в течение настроенного срока жизни, Patroni инициирует выборы лидера среди исправных резервных серверов. Победитель становится основным, а оставшиеся узлы переконфигурируются как резервные для нового основного — все автоматически, обычно в течение 10–30 секунд.

Архитектура Patroni HA — кластер PostgreSQL с 3 узламиКлиенты приложенийБалансировщик нагрузки HAProxyпорт 5000 (RW) · порт 5001 (RO)Основной (ведущий)PostgreSQL 16 + ПатрониУзел1 — 10.0.1.10:5432Режим ожидания 1 (синхронизация)PostgreSQL 16 + Patroniузел 2 — 10.0.1.11:5432Режим ожидания 2 (асинхронный)PostgreSQL 16 + Патрониузел 3 — 10.0.1.12:5432синхронизация потоковой передачиасинхронная потоковая передачаКластер etcd (DCS)3 узла — выбор лидера и усиление; хранилище конфигурацийПервичныйРежим ожиданияи т. д. РСУHAProxy

Конфигурация Patroni YAML

Patroni настраивается с помощью файла YAML, который определяет соединение DCS, параметры PostgreSQL, поведение репликации и настройки начальной загрузки. Ниже приведена конфигурация производственного уровня для основного узла.

# /etc/patroni/patroni.yml — Node 1 (Primary)
scope: pg-ha-cluster
namespace: /postgresql-ha/
name: node1

restapi:
  listen: 0.0.0.0:8008
  connect_address: 10.0.1.10:8008

etcd3:
  hosts:
    - 10.0.2.10:2379
    - 10.0.2.11:2379
    - 10.0.2.12:2379

bootstrap:
  dcs:
    ttl: 30
    loop_wait: 10
    retry_timeout: 10
    maximum_lag_on_failover: 1048576  # 1MB — only promote standbys within this lag
    synchronous_mode: true
    synchronous_mode_strict: false
    postgresql:
      use_pg_rewind: true
      use_slots: true
      parameters:
        wal_level: replica
        hot_standby: 'on'
        max_connections: 200
        max_wal_senders: 10
        max_replication_slots: 10
        wal_keep_size: 2GB
        synchronous_commit: 'on'
        archive_mode: 'on'
        archive_command: 'test ! -f /archive/%f && cp %p /archive/%f'
        archive_timeout: 60
        wal_log_hints: 'on'
        shared_preload_libraries: 'pg_stat_statements'
        track_commit_timestamp: 'on'
      pg_hba:
        - host replication replicator 10.0.0.0/16 scram-sha-256
        - host all all 10.0.0.0/16 scram-sha-256
        - host all all 0.0.0.0/0 scram-sha-256

  initdb:
    - encoding: UTF8
    - data-checksums

  users:
    admin:
      password: 'admin_secure_password'
      options:
        - createrole
        - createdb
    replicator:
      password: 'repl_secure_password'
      options:
        - replication

postgresql:
  listen: 0.0.0.0:5432
  connect_address: 10.0.1.10:5432
  data_dir: /var/lib/postgresql/16/main
  bin_dir: /usr/lib/postgresql/16/bin
  config_dir: /var/lib/postgresql/16/main
  pgpass: /tmp/pgpass0
  authentication:
    superuser:
      username: postgres
      password: 'postgres_secure_password'
    replication:
      username: replicator
      password: 'repl_secure_password'
    rewind:
      username: postgres
      password: 'postgres_secure_password'
  parameters:
    unix_socket_directories: '/var/run/postgresql'
  create_replica_methods:
    - basebackup
  basebackup:
    max-rate: 100M
    checkpoint: fast

tags:
  nofailover: false
  noloadbalance: false
  clonefrom: false
  nosync: false

Резервные узлы используют идентичную конфигурацию со своими собственными значениямиname,connect_addressиlisten. Остальное берет на себя Patroni — он определяет, должен ли узел быть ведущим или репликой, на основе состояния DCS, и соответствующим образом настраивает PostgreSQL.

etcd как распределенное хранилище конфигураций

etcd — это нервная система кластера Patroni. Он хранит информацию о текущем лидере, топологию кластера, желаемую конфигурацию и состояние работоспособности каждого узла. Кластер etcd из трех узлов — это минимум для производства, допускающий сбой одного узла при сохранении кворума.

# Install and configure etcd on three dedicated nodes
# /etc/etcd/etcd.conf.yml — Node etcd1 (10.0.2.10)
name: etcd1
data-dir: /var/lib/etcd
listen-client-urls: http://0.0.0.0:2379
listen-peer-urls: http://0.0.0.0:2380
advertise-client-urls: http://10.0.2.10:2379
initial-advertise-peer-urls: http://10.0.2.10:2380
initial-cluster: etcd1=http://10.0.2.10:2380,etcd2=http://10.0.2.11:2380,etcd3=http://10.0.2.12:2380
initial-cluster-state: new
initial-cluster-token: patroni-etcd-cluster

# Start etcd
systemctl enable --now etcd

# Verify cluster health
etcdctl endpoint health --cluster \
  --endpoints=http://10.0.2.10:2379,http://10.0.2.11:2379,http://10.0.2.12:2379

Для производственных развертываний включите TLS между узлами etcd, а также между клиентами etcd и Patroni. Незашифрованный трафик etcd предоставляет злоумышленникам на сетевом уровне учетные данные и конфигурацию кластера.

HAProxy для маршрутизации соединений

Patroni предоставляет REST API на каждом узле (порт 8008 по умолчанию), который сообщает, является ли узел текущим лидером или репликой. HAProxy использует эти конечные точки проверки работоспособности для маршрутизации трафика: записи идут к лидеру, чтения — к работоспособным репликам. Это обеспечивает автоматическое разделение чтения и записи без каких-либо изменений на уровне приложения.

# /etc/haproxy/haproxy.cfg
global
    maxconn 2000
    log /dev/log local0
    stats socket /var/run/haproxy.sock mode 660 level admin

defaults
    mode tcp
    log global
    retries 3
    timeout client 30m
    timeout connect 4s
    timeout server 30m
    timeout check 5s
    maxconn 1000

listen pg_write
    bind *:5000
    option httpchk GET /primary
    http-check expect status 200
    default-server inter 3s fall 3 rise 2 on-marked-down shutdown-sessions
    server node1 10.0.1.10:5432 check port 8008
    server node2 10.0.1.11:5432 check port 8008
    server node3 10.0.1.12:5432 check port 8008

listen pg_read
    bind *:5001
    balance roundrobin
    option httpchk GET /replica
    http-check expect status 200
    default-server inter 3s fall 3 rise 2 on-marked-down shutdown-sessions
    server node1 10.0.1.10:5432 check port 8008
    server node2 10.0.1.11:5432 check port 8008
    server node3 10.0.1.12:5432 check port 8008

listen stats
    bind *:7000
    mode http
    stats enable
    stats uri /
    stats refresh 10s

Конечная точка/primaryвозвращает HTTP 200 только на текущем лидере Patroni. Конечная точка/replicaвозвращает 200 в исправных резервных системах. При возникновении отказа новый основной сервер начинает возвращать 200 на/primary, а HAProxy автоматически перенаправляет трафик записи — обычно в течение одного интервала проверки работоспособности (3 секунды). Директиваon-marked-down shutdown-sessionsнемедленно разрывает существующие соединения с вышедшим из строя первичным сервером, заставляя клиентов повторно подключиться к новому лидеру.

PgBouncer для пула соединений

PostgreSQL создает новый серверный процесс для каждого клиентского соединения. В масштабе — сотни или тысячи микросервисов, каждый из которых поддерживает пулы соединений — накладные расходы на создание процессов и потребление памяти становятся значительными. PgBouncer находится между приложением и PostgreSQL, поддерживая пул соединений на стороне сервера и мультиплексируя на них клиентские соединения.

# /etc/pgbouncer/pgbouncer.ini
[databases]
* = host=127.0.0.1 port=5432 dbname=appdb

[pgbouncer]
listen_addr = 0.0.0.0
listen_port = 6432
auth_type = scram-sha-256
auth_file = /etc/pgbouncer/userlist.txt

pool_mode = transaction
max_client_conn = 1000
default_pool_size = 50
min_pool_size = 10
reserve_pool_size = 10
reserve_pool_timeout = 3
server_lifetime = 3600
server_idle_timeout = 600
server_connect_timeout = 5
server_login_retry = 3

log_connections = 1
log_disconnections = 1
stats_period = 60

При использовании с Patroni PgBouncer обычно размещается на каждом узле PostgreSQL или на узлах HAProxy. Режим пулаtransaction— лучший выбор для большинства рабочих нагрузок — он назначает соединение с сервером на время транзакции и возвращает его в пул между транзакциями. Это гораздо более эффективно, чем режимsession, который поддерживает соединение на протяжении всего сеанса клиента.

Архивирование WAL и восстановление на определенный момент времени

Потоковая репликация

защищает от сбоя сервера, но не защищает от логических ошибок — случайныйDROP TABLEили неверная миграция приложения немедленно реплицируются на все резервные серверы. Архивирование WAL в сочетании с восстановлением на определенный момент времени (PITR) позволяет восстановить данные в любой момент до возникновения ошибки.

Архивация WAL копирует завершенные сегменты WAL в надежный архив — обычно в корзину S3, монтирование NFS или выделенный сервер резервного копирования. Такие инструменты, какpgBackRestиWAL-G, эффективно выполняют архивирование с помощью сжатия, шифрования и параллельной передачи.

# pgBackRest configuration — /etc/pgbackrest/pgbackrest.conf
[global]
repo1-type=s3
repo1-s3-bucket=pg-wal-archive
repo1-s3-endpoint=s3.eu-west-1.amazonaws.com
repo1-s3-region=eu-west-1
repo1-s3-key=AKIAIOSFODNN7EXAMPLE
repo1-s3-key-secret=wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY
repo1-path=/pgbackrest
repo1-retention-full=4
repo1-retention-diff=7
repo1-cipher-type=aes-256-cbc
repo1-cipher-pass=strong_encryption_passphrase
compress-type=zst
compress-level=3
process-max=4

[pg-ha-cluster]
pg1-path=/var/lib/postgresql/16/main
pg1-port=5432

# In postgresql.conf
archive_command = 'pgbackrest --stanza=pg-ha-cluster archive-push %p'
restore_command = 'pgbackrest --stanza=pg-ha-cluster archive-get %f "%p"'

Чтобы выполнить восстановление на определенный момент времени, укажите целевую временную метку.

# Restore to a specific point in time
pgbackrest --stanza=pg-ha-cluster --type=time \
  --target="2026-04-12 11:25:00" \
  --target-action=promote \
  restore

Логическая репликация для выборочной синхронизации данных

В то время как потоковая репликация создает точную физическую копию всего кластера базы данных, логическая репликация работает на уровне таблиц, реплицируя отдельные таблицы или подмножества данных между независимыми экземплярами PostgreSQL. Это полезно для обновлений основных версий без простоев, межрегиональных реплик чтения, которым нужны только определенные таблицы, загрузки хранилища данных и мультитенантного распределения данных.

# On the publisher (source database)
wal_level = logical  # must be 'logical' — higher than 'replica'

CREATE PUBLICATION app_pub FOR TABLE orders, customers, products;

# On the subscriber (target database)
CREATE SUBSCRIPTION app_sub
  CONNECTION 'host=publisher-host port=5432 dbname=appdb user=replicator password=pass'
  PUBLICATION app_pub;

Логическая репликация может работать параллельно с потоковой репликацией. Распространенным шаблоном является использование потоковой репликации для высокой доступности (быстрого физического переключения при сбое) и логической репликации для реплик межрегиональной аналитики, которым требуется только подмножество таблиц.

Многорегиональная потоковая репликация

Для аварийного восстановления и глобальной производительности чтения кластеры PostgreSQL могут охватывать несколько регионов. Стандартным шаблоном является синхронная репликация внутри региона (для нулевой потери данных при локальном переходе на другой ресурс) и асинхронная репликация между регионами (чтобы избежать штрафов за задержку между регионами при каждой записи). В каждом регионе есть собственный HAProxy для локальной маршрутизации чтения.

Многорегиональная потоковая репликация PostgreSQL— регион 1 — AWS eu-west-1Первичныйpg-узел 1 (RW)Режим ожидания синхронизацииpg-узел 2 (RO)СинхронизацияHAProxy (локальный)— регион 2 — Azure Западная ЕвропаАсинхронный режим ожиданияpg-узел 3 (RO)Асинхронный режим ожиданияpg-узел 4 (RO)СинхронизацияHAProxy (локальный)Регион 3 — GCP Европа-Запад1Асинхронный режим ожиданияpg-узел 5 (RO)Асинхронный режим ожиданияpg-узел 6 (RO)СинхронизацияHAProxy (локальный)асинхронный WALасинхронный WAL, доставкаОбщий архив WAL (S3/Blob/GCS)pgBackRest или WAL-G — межрегиональный PITRGlobal DNS (Route53/Менеджер трафика/Облачный DNS)Обозначение:Синхронная репликация (внутри региона)Асинхронная репликация (межрегиональная)Архивирование WAL в объектное хранилищеПервичныйРежим ожиданияПроцедуры аварийного переключения и переключения

Понимание разницы между аварийным переключением и переключением имеет решающее значение. Аварийное переключение— это незапланированное повышение уровня, вызванное сбоем текущего основного устройства. Переключение— это запланированное плавное изменение роли, которое обычно выполняется перед техническим обслуживанием. Патрони поддерживает обоих.

Плановое переключение

# List cluster members
patronictlctl -c /etc/patroni/patroni.yml list

# Perform switchover to a specific node
patronictlctl -c /etc/patroni/patroni.yml switchover \
  --master node1 --candidate node2 --force

# Or use the Patroni REST API
curl -s http://10.0.1.10:8008/switchover -XPOST \
  -d '{"leader": "node1", "candidate": "node2"}'

Во время переключения Patroni понижает статус текущего основного до резервного, повышает целевого кандидата и перенастраивает все остальные резервные устройства, чтобы они следовали за новым основным. Процесс занимает 5-15 секунд. HAProxy обнаруживает изменения посредством проверок работоспособности и автоматически перенаправляет трафик.

Автоматическая последовательность аварийного переключения

При неожиданном сбое основного устройства Patroni выполняет точную последовательность действий для восстановления обслуживания. Следующая диаграмма иллюстрирует эти шаги.

Автоматическая последовательность аварийного переключения Patroni1Первичный отказСбой, сетевой раздел илиСбой дискана узле 12Patroni обнаруживает через DCSСрок действия TTL ведущего ключаистек в etcd(по умолчанию TTL 30 с)3Выборы лидераРезервные компаниистремятся приобрестиБлокировка выноски в etcd4Повышенный режим ожиданияПобедительзапускает pg_ctl для продвиженияи становится новым основным5Обновления HAProxyПроверки работоспособностиобнаруживают новый основной/primary возвращает 200 на новом лидере6Клиентыповторно подключеныПриложения переподключаются через HAProxyк новой первичной прозрачнойТипичное общее время переключения при сбоеСрок действия TTL DCS: ~15–30 с.Выбор: ~2–5 сОбнаружение HAProxy: ~3–10 с.Повторное подключение≈ всего 20–45 секундКлючевые параметры, влияющие на скорость переключения при сбое:• ttl (по умолчанию 30 с) — через сколько времени истечет срок действия ведущего ключа в DCS.• Loop_wait (по умолчанию 10 с) — как часто Patroni проверяет состояние кластера• retry_timeout (по умолчанию 10 с) — тайм-аут для операций DCS и PostgreSQL• Maximum_lag_on_failover (1 МБ) — повышать резервные серверы только в пределах этой задержки репликации

Предотвращение разделения мозга

Split-brain — когда два узла одновременно считают себя основными — является наиболее опасным режимом отказа в любой системе высокой доступности. Patroni предотвращает разделение мозга с помощью нескольких механизмов:

  1. Блокировка лидера на основе DCS:Только один узел может хранить ключ лидера в etcd в любой момент времени. У ключа есть TTL, и лидер должен постоянно его продлевать. Если сетевой раздел изолирует лидера от etcd, срок действия ключа истекает, и лидер понижает себя в должности.
  2. Сторожевой таймер:Patroni может настроить сторожевой таймер Linux (/dev/watchdog). Если Patroni потеряет доступ к DCS и не сможет подтвердить, что он должен оставаться лидером, сторожевой таймер перезагрузит или отключит узел — механизм жесткого ограждения, гарантирующий, что старый основной узел не продолжит принимать записи.
  3. pg_rewind:Когда бывший основной сервер снова подключается к сети, он может иметь записи WAL, которые никогда не реплицировались.pg_rewindперематывает временную шкалу до точки расхождения, позволяя узлу снова присоединиться в качестве резервного без полной базовой резервной копии. Настройка Patroniuse_pg_rewind: trueавтоматизирует это.
# Enable watchdog in Patroni config
bootstrap:
  dcs:
    postgresql:
      use_pg_rewind: true
      parameters:
        wal_log_hints: 'on'  # required for pg_rewind

# Watchdog configuration
watchdog:
  mode: required        # 'off', 'automatic', or 'required'
  device: /dev/watchdog
  safety_margin: 5      # seconds before TTL expiry to trigger watchdog

Repmgr как альтернатива Patroni

Repmgr— еще один популярный инструмент высокой доступности для PostgreSQL. Он обеспечивает управление режимом ожидания, автоматическое переключение при сбое и возможности переключения. Однако здесь используется принципиально иной подход, чем у Патрони. Repmgr использует узел-свидетель и демон (repmgrd) для обнаружения сбоев, а не распределенное консенсусное хранилище. Это упрощает развертывание, но делает более уязвимым к разделению мозгов в сложных сценариях разделения сети.

# repmgr.conf on the primary
node_id=1
node_name='node1'
conninfo='host=10.0.1.10 user=repmgr dbname=repmgr connect_timeout=2'
data_directory='/var/lib/postgresql/16/main'
failover=automatic
promote_command='repmgr standby promote -f /etc/repmgr.conf --log-to-file'
follow_command='repmgr standby follow -f /etc/repmgr.conf --log-to-file --upstream-node-id=%n'
monitoring_history=yes
monitor_interval_secs=5
reconnect_attempts=6
reconnect_interval=10

Для новых развертываний рекомендуется использовать Patroni из-за более надежных гарантий предотвращения разделения мозгов и более активного сообщества разработчиков. Repmgr остается разумным вариантом для более простых установок или организаций, уже вложившихся в этот инструмент.

Шаблоны облачного развертывания

Развертывание AWS: EC2, EBS и Route53

На AWS разверните каждый узел PostgreSQL + Patroni на экземпляре EC2 с томами EBS gp3 или io2. Используйте отдельные экземпляры в нескольких зонах доступности для обеспечения высокой доступности. Узлы etcd также должны охватывать зоны доступности.

# Terraform sketch for PostgreSQL HA on AWS
resource "aws_instance" "pg_node" {
  count                = 3
  ami                  = "ami-0abcdef1234567890"  # Ubuntu 22.04
  instance_type        = "r6g.2xlarge"             # 8 vCPU, 64GB RAM
  subnet_id            = aws_subnet.private[count.index].id
  vpc_security_group_ids = [aws_security_group.pg_sg.id]
  availability_zone    = element(["eu-west-1a", "eu-west-1b", "eu-west-1c"], count.index)

  root_block_device {
    volume_size = 50
    volume_type = "gp3"
  }

  tags = {
    Name = "pg-node-${count.index + 1}"
    Role = "patroni"
  }
}

resource "aws_ebs_volume" "pg_data" {
  count             = 3
  availability_zone = element(["eu-west-1a", "eu-west-1b", "eu-west-1c"], count.index)
  size              = 500
  type              = "gp3"
  iops              = 6000
  throughput        = 250
  encrypted         = true

  tags = {
    Name = "pg-data-${count.index + 1}"
  }
}

resource "aws_route53_health_check" "pg_primary" {
  count             = 3
  ip_address        = aws_instance.pg_node[count.index].private_ip
  port              = 8008
  type              = "HTTP"
  resource_path     = "/primary"
  failure_threshold = 3
  request_interval  = 10
}

Используйте балансировщик сетевой нагрузки (NLB) вместо HAProxy, если вы предпочитаете решение под управлением AWS. NLB может использовать проверки работоспособности целевой группы на Patroni REST API для маршрутизации трафика к текущему основному устройству.

Развертывание Azure: виртуальные машины, управляемые диски и Azure LB

В Azure используйте виртуальные машины Standard_E8s_v5 (оптимизированные для памяти) с управляемыми твердотельными дисками премиум-класса для томов данных. Развертывание в зонах доступности. Azure Load Balancer обеспечивает эквивалент HAProxy с проверками работоспособности Patroni REST API.

# Azure CLI — create PostgreSQL VM with Managed Disk
az vm create \
  --resource-group pg-ha-rg \
  --name pg-node-1 \
  --image Canonical:0001-com-ubuntu-server-jammy:22_04-lts:latest \
  --size Standard_E8s_v5 \
  --zone 1 \
  --vnet-name pg-vnet \
  --subnet pg-subnet \
  --nsg pg-nsg \
  --admin-username pgadmin \
  --ssh-key-value ~/.ssh/id_rsa.pub

az disk create \
  --resource-group pg-ha-rg \
  --name pg-data-1 \
  --size-gb 512 \
  --sku Premium_LRS \
  --zone 1

az vm disk attach \
  --resource-group pg-ha-rg \
  --vm-name pg-node-1 \
  --name pg-data-1

# Azure Load Balancer health probe for Patroni
az network lb probe create \
  --resource-group pg-ha-rg \
  --lb-name pg-lb \
  --name patroni-primary-probe \
  --protocol Http \
  --port 8008 \
  --path /primary \
  --interval 5 \
  --threshold 3

Развертывание GCP: вычислительный механизм и балансировка облачной нагрузки

На GCP используйте экземпляры n2-highmem-8 (8 vCPU, 64 ГБ ОЗУ) с постоянными дисками SSD. Распределить по зонам внутри региона. Используйте внутренний балансировщик нагрузки TCP/UDP с проверками работоспособности Patroni.

# GCP — create instance and persistent disk
gcloud compute instances create pg-node-1 \
  --zone=europe-west1-b \
  --machine-type=n2-highmem-8 \
  --image-family=ubuntu-2204-lts \
  --image-project=ubuntu-os-cloud \
  --boot-disk-size=50GB \
  --network=pg-network \
  --subnet=pg-subnet

gcloud compute disks create pg-data-1 \
  --zone=europe-west1-b \
  --size=500GB \
  --type=pd-ssd

gcloud compute instances attach-disk pg-node-1 \
  --disk=pg-data-1 \
  --zone=europe-west1-b

# Health check for Patroni primary endpoint
gcloud compute health-checks create http patroni-primary-check \
  --port=8008 \
  --request-path=/primary \
  --check-interval=5s \
  --timeout=5s \
  --unhealthy-threshold=3 \
  --healthy-threshold=2

Развертывание k3s без операционной системы с помощью Rancher и Longhorn

Для организаций, которые используют собственное оборудование, развертывание PostgreSQL HA на «голом железе» с k3s, Rancher и Longhorn обеспечивает полностью открытую, независимую от облака инфраструктуру. k3s — это облегченный дистрибутив Kubernetes, который эффективно работает на «голых» серверах без накладных расходов, присущих полному дистрибутиву Kubernetes.

Bare Metal k3s HA — PostgreSQL с PatroniВиртуальный IP (Keepalived VRRP) — 10.0.0.100Плавающий VIP для активного/резервного HAProxyHAProxy (активный)голый металл-lb1HAProxy (режим ожидания)голый металл-lb2Кластер k3s (под управлением Rancher)k3s Узел 1 (сервер)PostgreSQL ПервичныйPatroni + PgBouncerТом Longhorn (500 ГБ)NVMe SSD — «голый металл-srv1»k3s Узел 2 (сервер)PostgreSQL Режим ожидания 1Patroni + PgBouncerТом Longhorn (500 ГБ)NVMe SSD — «голый металл-srv2»k3s Узел 3 (сервер)PostgreSQL Режим ожидания 2Patroni + PgBouncerТом Longhorn (500 ГБ)NVMe SSD — Bar-Metal-srv3Внешний кластер etcd (3 выделенных узла)etcd1 (10.0.3.10) · etcd2 (10.0.3.11) · etcd3 (10.0.3.12)Управление ранчоПользовательский интерфейс + жизненный цикл кластераПервичныйРежим ожиданияи т. д.ЛонгхорнHAProxyПотоковая репликация показана пунктирными зелеными стрелками

Настройка k3s и Longhorn

# Install k3s on the first server node
curl -sfL https://get.k3s.io | K3S_TOKEN=my-cluster-token \
  INSTALL_K3S_EXEC="server --cluster-init --disable traefik --disable servicelb" sh -

# Join additional server nodes
curl -sfL https://get.k3s.io | K3S_TOKEN=my-cluster-token \
  K3S_URL=https://10.0.0.1:6443 \
  INSTALL_K3S_EXEC="server" sh -

# Install Longhorn for distributed block storage
helm repo add longhorn https://charts.longhorn.io
helm install longhorn longhorn/longhorn \
  --namespace longhorn-system --create-namespace \
  --set defaultSettings.defaultDataPath=/mnt/longhorn \
  --set defaultSettings.replicaCount=3 \
  --set defaultSettings.storageMinimalAvailablePercentage=15

# Deploy PostgreSQL with Patroni using the Zalando Postgres Operator
helm repo add postgres-operator-charts https://opensource.zalando.com/postgres-operator/charts/postgres-operator
helm install postgres-operator postgres-operator-charts/postgres-operator \
  --namespace postgres-system --create-namespace
# PostgreSQL cluster manifest for the Zalando Postgres Operator
apiVersion: acid.zalan.do/v1
kind: postgresql
metadata:
  name: pg-ha-cluster
  namespace: production
spec:
  teamId: "platform"
  numberOfInstances: 3
  volume:
    size: 500Gi
    storageClass: longhorn
  users:
    appuser:
      - superuser
      - createdb
    replicator: []
  databases:
    appdb: appuser
  postgresql:
    version: "16"
    parameters:
      shared_buffers: "16GB"
      effective_cache_size: "48GB"
      work_mem: "256MB"
      maintenance_work_mem: "2GB"
      max_connections: "200"
      max_wal_senders: "10"
      wal_level: replica
      synchronous_commit: "on"
      wal_keep_size: "2GB"
      archive_mode: "on"
      track_commit_timestamp: "on"
  patroni:
    ttl: 30
    loop_wait: 10
    retry_timeout: 10
    maximum_lag_on_failover: 1048576
    synchronous_mode: true
  resources:
    requests:
      cpu: "4"
      memory: 32Gi
    limits:
      cpu: "8"
      memory: 64Gi
Поддержка активности

для HAProxy VIP

# /etc/keepalived/keepalived.conf on lb1
vrrp_script chk_haproxy {
    script "killall -0 haproxy"
    interval 2
    weight 2
}

vrrp_instance VI_PG {
    state MASTER
    interface eth0
    virtual_router_id 52
    priority 100
    advert_int 1
    authentication {
        auth_type PASS
        auth_pass pgha_vip_pass
    }
    virtual_ipaddress {
        10.0.0.100/24
    }
    track_script {
        chk_haproxy
    }
}

Мониторинг репликации PostgreSQL

Мониторинг работоспособности репликации не подлежит обсуждению в производственной среде. Для этой цели PostgreSQL предоставляет несколько встроенных представлений, а Prometheus с Grafana обеспечивает необходимую долгосрочную видимость и оповещение.

Встроенные запросы мониторинга

-- Check replication status on the primary
SELECT
    client_addr,
    application_name,
    state,
    sync_state,
    sent_lsn,
    write_lsn,
    flush_lsn,
    replay_lsn,
    (sent_lsn - replay_lsn) AS replication_lag_bytes,
    write_lag,
    flush_lag,
    replay_lag
FROM pg_stat_replication;

-- Check replication slot status
SELECT
    slot_name,
    slot_type,
    active,
    wal_status,
    pg_size_pretty(pg_wal_lsn_diff(pg_current_wal_lsn(), restart_lsn)) AS slot_lag
FROM pg_replication_slots;

-- Check standby recovery status (run on standby)
SELECT
    pg_is_in_recovery() AS is_standby,
    pg_last_wal_receive_lsn() AS last_received,
    pg_last_wal_replay_lsn() AS last_replayed,
    pg_last_xact_replay_timestamp() AS last_replayed_timestamp,
    EXTRACT(EPOCH FROM (now() - pg_last_xact_replay_timestamp()))::int AS replay_lag_seconds;

-- Monitor WAL generation rate
SELECT
    pg_size_pretty(pg_wal_lsn_diff(pg_current_wal_lsn(), '0/0')) AS total_wal_generated,
    pg_size_pretty(sum(size)) AS wal_directory_size
FROM pg_ls_waldir();

-- Check for long-running queries that could block replication
SELECT
    pid,
    now() - pg_stat_activity.query_start AS duration,
    query,
    state
FROM pg_stat_activity
WHERE (now() - pg_stat_activity.query_start) > interval '5 minutes'
  AND state != 'idle'
ORDER BY duration DESC;

Стек Prometheus и Grafana

postgres_exporterпредоставляет метрики PostgreSQL в формате Prometheus. В сочетании сPatoni_exporterвы получаете полную информацию как о производительности базы данных, так и о состоянии кластера высокой доступности.

# Deploy postgres_exporter as a sidecar or standalone
helm repo add prometheus-community https://prometheus-community.github.io/helm-charts

# Custom queries for postgres_exporter
# /etc/postgres_exporter/queries.yaml
pg_replication_lag:
  query: |
    SELECT
      CASE WHEN pg_is_in_recovery() THEN
        EXTRACT(EPOCH FROM (now() - pg_last_xact_replay_timestamp()))::float
      ELSE 0 END AS lag_seconds
  master: true
  metrics:
    - lag_seconds:
        usage: "GAUGE"
        description: "Replication lag in seconds"

pg_replication_slots:
  query: |
    SELECT
      slot_name,
      active::int AS active,
      pg_wal_lsn_diff(pg_current_wal_lsn(), restart_lsn)::float AS slot_lag_bytes
    FROM pg_replication_slots
  master: true
  metrics:
    - slot_name:
        usage: "LABEL"
    - active:
        usage: "GAUGE"
        description: "Whether the slot is active"
    - slot_lag_bytes:
        usage: "GAUGE"
        description: "Slot lag in bytes"
# PrometheusRule for PostgreSQL HA alerts
apiVersion: monitoring.coreos.com/v1
kind: PrometheusRule
metadata:
  name: postgresql-ha-alerts
  namespace: monitoring
spec:
  groups:
    - name: postgresql-replication
      rules:
        - alert: PostgreSQLReplicationLagHigh
          expr: pg_replication_lag_seconds > 30
          for: 5m
          labels:
            severity: warning
          annotations:
            summary: "PostgreSQL replication lag exceeds 30s on {{ $labels.instance }}"

        - alert: PostgreSQLReplicationSlotInactive
          expr: pg_replication_slots_active == 0
          for: 5m
          labels:
            severity: critical
          annotations:
            summary: "Replication slot {{ $labels.slot_name }} is inactive"

        - alert: PostgreSQLReplicationSlotLagHigh
          expr: pg_replication_slots_slot_lag_bytes > 1073741824
          for: 10m
          labels:
            severity: warning
          annotations:
            summary: "Replication slot lag exceeds 1GB on {{ $labels.slot_name }}"

        - alert: PatroniClusterUnhealthy
          expr: patroni_cluster_members_count < 3
          for: 2m
          labels:
            severity: critical
          annotations:
            summary: "Patroni cluster has fewer than 3 members"
Параметры настройки производства

Конфигурация PostgreSQL по умолчанию консервативна и настроена для небольшой среды общего хостинга. Производственные кластеры высокой доступности требуют тщательной настройки параметров репликации, памяти и WAL. В следующей таблице приведены наиболее важные настройки для сервера с оперативной памятью 64 ГБ и хранилищем NVMe.

# postgresql.conf — Production HA tuning

# === Replication ===
wal_level = replica
max_wal_senders = 10
max_replication_slots = 10
wal_keep_size = 4GB
synchronous_commit = on
synchronous_standby_names = 'ANY 1 (standby1, standby2)'
track_commit_timestamp = on
wal_log_hints = on

# === WAL ===
min_wal_size = 1GB
max_wal_size = 8GB
wal_buffers = 64MB
wal_compression = zstd
archive_mode = on
archive_timeout = 300
checkpoint_completion_target = 0.9
checkpoint_timeout = 15min

# === Memory ===
shared_buffers = 16GB              # 25% of RAM
effective_cache_size = 48GB        # 75% of RAM
work_mem = 256MB                   # per-operation sort/hash memory
maintenance_work_mem = 2GB         # for VACUUM, CREATE INDEX
huge_pages = try

# === Connections ===
max_connections = 200              # use PgBouncer for higher client counts
superuser_reserved_connections = 5

# === Query Performance ===
random_page_cost = 1.1             # SSD storage
effective_io_concurrency = 200     # NVMe SSD
default_statistics_target = 500
jit = on

# === Logging ===
log_min_duration_statement = 500   # log queries > 500ms
log_checkpoints = on
log_connections = on
log_disconnections = on
log_lock_waits = on
log_temp_files = 0
log_autovacuum_min_duration = 0

# === Autovacuum ===
autovacuum_max_workers = 4
autovacuum_naptime = 30s
autovacuum_vacuum_cost_limit = 2000
autovacuum_vacuum_scale_factor = 0.05
autovacuum_analyze_scale_factor = 0.02

Настройка Patroni DCS Параметры

Взаимосвязь между параметрами Patronittl,loop_waitиretry_timeoutнапрямую влияет на скорость аварийного переключения и риск ложноположительных результатов. Более короткий TTL означает более быстрое обнаружение аварийного переключения, но увеличивает риск ненужных аварийных переключений во время коротких сбоев в работе сети.

# Conservative (production default)
ttl: 30
loop_wait: 10
retry_timeout: 10
# Failover detection: ~30-40 seconds

# Aggressive (low-latency failover)
ttl: 15
loop_wait: 5
retry_timeout: 5
# Failover detection: ~15-20 seconds
# Warning: Higher risk of false failovers in unstable networks

Хаос-инжиниринг и тестирование аварийного переключения

Система аварийного переключения, которая никогда не тестировалась, — это система, которая не работает. Хаос-инжиниринг применяет контролируемые сбои для проверки правильности работы вашей установки высокой доступности в реальных условиях сбоя. Каждый кластер Patroni должен подвергаться регулярным тренировкам по отказоустойчивости.

Руководство по тестированию аварийного переключения

# 1. Verify cluster health before testing
patronictlctl -c /etc/patroni/patroni.yml list
+----------+---------+---------+----+-----------+
| Member   | Host    | Role    | TL | Lag in MB |
+----------+---------+---------+----+-----------+
| node1    | 10.0.1.10| Leader |  5 |           |
| node2    | 10.0.1.11| Replica |  5 |         0 |
| node3    | 10.0.1.12| Replica |  5 |         0 |
+----------+---------+---------+----+-----------+

# 2. Simulate primary crash (on node1)
sudo systemctl stop patroni
# Or more aggressive: sudo kill -9 $(pgrep -f patroni)

# 3. Monitor failover (from any node with patronictl)
watch -n 1 'patronictl -c /etc/patroni/patroni.yml list'

# 4. Verify new leader is elected (within 30-45 seconds)
# Expected: node2 or node3 promoted to Leader

# 5. Test write availability through HAProxy
PGPASSWORD=app_password psql -h haproxy-host -p 5000 -U appuser -d appdb \
  -c "INSERT INTO health_check (ts) VALUES (now()) RETURNING *;"

# 6. Restart the former primary
sudo systemctl start patroni
# Patroni will use pg_rewind to rejoin as a replica

# 7. Verify the former primary rejoins as replica
patronictlctl -c /etc/patroni/patroni.yml list

Тестирование сетевых разделов

# Simulate network partition on the primary using iptables
# Block all traffic to etcd from the primary
sudo iptables -A OUTPUT -d 10.0.2.10 -j DROP
sudo iptables -A OUTPUT -d 10.0.2.11 -j DROP
sudo iptables -A OUTPUT -d 10.0.2.12 -j DROP

# Expected behaviour:
# 1. Primary loses DCS access
# 2. Leader key TTL expires
# 3. Primary demotes itself (with watchdog, node may reboot)
# 4. Standby acquires leader lock and promotes
# 5. After clearing iptables rules, former primary rejoins as replica

# Clean up
sudo iptables -D OUTPUT -d 10.0.2.10 -j DROP
sudo iptables -D OUTPUT -d 10.0.2.11 -j DROP
sudo iptables -D OUTPUT -d 10.0.2.12 -j DROP

Автоматическое тестирование хаоса с помощью Toxiproxy

# Run Toxiproxy alongside your Patroni cluster
# Create proxies for etcd and replication connections
toxiproxy-cli create etcd_proxy -l 0.0.0.0:12379 -u 10.0.2.10:2379
toxiproxy-cli create pg_repl_proxy -l 0.0.0.0:15432 -u 10.0.1.10:5432

# Add latency to etcd connections (simulates degraded network)
toxiproxy-cli toxic add etcd_proxy -t latency -a latency=500 -a jitter=200

# Add bandwidth limit to replication (simulates WAN replication)
toxiproxy-cli toxic add pg_repl_proxy -t bandwidth -a rate=1024

# Completely sever the connection (simulates network partition)
toxiproxy-cli toxic add etcd_proxy -t timeout -a timeout=0

# Monitor Patroni behaviour and verify correct failover
watch -n 2 'curl -s http://10.0.1.10:8008/patroni | python3 -m json.tool'

Сценарий непрерывной проверки

#!/bin/bash
# continuous_ha_check.sh — Run during chaos tests to measure availability

HAPROXY_HOST="10.0.0.100"
WRITE_PORT=5000
READ_PORT=5001
DATABASE="appdb"
USER="appuser"
LOGFILE="/var/log/ha_test_$(date +%Y%m%d_%H%M%S).log"

write_count=0
write_fail=0
read_count=0
read_fail=0

while true; do
    ts=$(date '+%Y-%m-%d %H:%M:%S.%3N')

    # Test write path
    if PGPASSWORD=app_password psql -h $HAPROXY_HOST -p $WRITE_PORT \
       -U $USER -d $DATABASE -c "SELECT 1" &>/dev/null; then
        ((write_count++))
    else
        ((write_fail++))
        echo "$ts WRITE_FAIL total_fails=$write_fail" >> $LOGFILE
    fi

    # Test read path
    if PGPASSWORD=app_password psql -h $HAPROXY_HOST -p $READ_PORT \
       -U $USER -d $DATABASE -c "SELECT 1" &>/dev/null; then
        ((read_count++))
    else
        ((read_fail++))
        echo "$ts READ_FAIL total_fails=$read_fail" >> $LOGFILE
    fi

    total=$((write_count + write_fail))
    if (( total % 100 == 0 )); then
        write_avail=$(echo "scale=2; $write_count * 100 / $total" | bc)
        read_total=$((read_count + read_fail))
        read_avail=$(echo "scale=2; $read_count * 100 / $read_total" | bc)
        echo "$ts Writes: ${write_avail}% ($write_count/$total) Reads: ${read_avail}% ($read_count/$read_total)"
    fi

    sleep 0.5
done

Advanced: каскадная репликация и отложенные резервные системы

Для больших кластеров каскадная репликация снижает нагрузку на основной кластер. Вместо того, чтобы все резервные серверы реплицировались непосредственно с основного, некоторые резервные серверы реплицируются с других резервных серверов. Это создает древовидную топологию, в которой основной канал питает два резервных сервера, а эти резервные каналы подают дополнительные резервные устройства ниже по потоку.

# postgresql.auto.conf on a cascading standby
primary_conninfo = 'host=standby1-host port=5432 user=replicator application_name=cascade1'
primary_slot_name = 'cascade1_slot'

.в режиме ожидания с задержкой.намеренно применяет записи WAL с задержкой по времени — обычно 1–4 часа. Это обеспечивает защиту от логических ошибок (случайного удаления, неправильной миграции), которые немедленно реплицируются на синхронные резервные серверы. В случае сбоя вы можете остановить воспроизведение WAL в режиме ожидания с задержкой и восстановить данные, существовавшие до возникновения ошибки.

# postgresql.conf on delayed standby
recovery_min_apply_delay = '1h'

Стратегии строки подключения

Приложения, подключающиеся к кластеру, управляемому Patroni, всегда должны подключаться через HAProxy или использовать встроенную строку подключения к нескольким хостам PostgreSQL сtarget_session_attrs. Это обеспечивает аварийное переключение на стороне клиента без зависимости от балансировщика нагрузки.

# Multi-host connection string with target_session_attrs
# The client tries each host in order and connects to the one matching the target attribute
postgresql://appuser:password@node1:5432,node2:5432,node3:5432/appdb?target_session_attrs=read-write&sslmode=require

# For read-only connections
postgresql://appuser:password@node1:5432,node2:5432,node3:5432/appdb?target_session_attrs=prefer-standby&sslmode=require

Этот подход хорошо работает для приложений, которые нелегко перенастроить для указания на VIP-адрес HAProxy. Клиентская библиотека PostgreSQL (libpq) обеспечивает прозрачное переключение при сбое.

Усиление безопасности

Рабочий кластер высокой доступности PostgreSQL должен обеспечивать принудительное шифрование при передаче и хранении, использовать надежную аутентификацию и ограничивать воздействие сети.

# Enable TLS in postgresql.conf
ssl = on
ssl_cert_file = '/etc/postgresql/certs/server.crt'
ssl_key_file = '/etc/postgresql/certs/server.key'
ssl_ca_file = '/etc/postgresql/certs/ca.crt'
ssl_min_protocol_version = 'TLSv1.3'

# Require TLS for all connections in pg_hba.conf
hostssl replication replicator 10.0.0.0/16 scram-sha-256
hostssl all         all        10.0.0.0/16 scram-sha-256

# etcd TLS
# In Patroni config
etcd3:
  hosts:
    - 10.0.2.10:2379
    - 10.0.2.11:2379
    - 10.0.2.12:2379
  protocol: https
  cacert: /etc/patroni/certs/etcd-ca.crt
  cert: /etc/patroni/certs/etcd-client.crt
  key: /etc/patroni/certs/etcd-client.key

Стратегия резервного копирования для кластеров высокой доступности

Комплексная стратегия резервного копирования для кластеров Patroni должна включать непрерывное архивирование WAL, регулярное полное резервное копирование, а также дифференциальное или инкрементальное резервное копирование между полными резервными копиями. pgBackRest — рекомендуемый инструмент для управления резервным копированием в рабочей среде PostgreSQL.

# Schedule backups via cron
# Full backup weekly (Sunday 2 AM)
0 2 * * 0 pgbackrest --stanza=pg-ha-cluster --type=full backup

# Differential backup daily (2 AM, Mon-Sat)
0 2 * * 1-6 pgbackrest --stanza=pg-ha-cluster --type=diff backup

# Verify backup integrity
pgbackrest --stanza=pg-ha-cluster --set=latest info

# Verify backup can be restored (dry run)
pgbackrest --stanza=pg-ha-cluster --set=latest verify

# List all backups
pgbackrest --stanza=pg-ha-cluster info
            full backup: 20260412-020000F
                timestamp: 2026-04-12 02:00:00 +0000
                wal start/stop: 000000050000000000000040 / 000000050000000000000042
                database size: 150GB, backup size: 150GB
                repository size: 45GB (compressed)
            diff backup: 20260412-020000F_20260413-020000D
                timestamp: 2026-04-13 02:00:00 +0000
                database size: 151GB, backup size: 2.1GB
                repository size: 650MB (compressed)

Сводная информация о рабочем модуле Runbook

Каждая команда, работающая с кластером Patroni, должна иметь книгу Runbook, охватывающую следующие сценарии. Наличие документированных и проверенных процедур превращает стрессовый сбой в рутинную операцию.

# === Quick Reference Commands ===

# Cluster status
patronictlctl -c /etc/patroni/patroni.yml list
patronictlctl -c /etc/patroni/patroni.yml history

# Planned switchover
patronictlctl -c /etc/patroni/patroni.yml switchover --master node1 --candidate node2

# Restart PostgreSQL on a specific node (rolling restart)
patronictlctl -c /etc/patroni/patroni.yml restart pg-ha-cluster node2

# Reload PostgreSQL configuration without restart
patronictlctl -c /etc/patroni/patroni.yml reload pg-ha-cluster

# Pause automatic failover (during maintenance)
patronictlctl -c /etc/patroni/patroni.yml pause

# Resume automatic failover
patronictlctl -c /etc/patroni/patroni.yml resume

# Edit DCS configuration (applies to all nodes)
patronictlctl -c /etc/patroni/patroni.yml edit-config

# Reinitialise a failed replica
patronictlctl -c /etc/patroni/patroni.yml reinit pg-ha-cluster node3

# Check Patroni REST API directly
curl -s http://10.0.1.10:8008/patroni | python3 -m json.tool
curl -s http://10.0.1.10:8008/cluster | python3 -m json.tool
Тестирование производительности

Перед запуском в эксплуатацию протестируйте свой кластер высокой доступности, чтобы определить базовую производительность и убедиться, что задержка синхронной репликации приемлема для вашей рабочей нагрузки.

# Benchmark with pgbench — initialise test data
pgbench -i -s 100 -h haproxy-host -p 5000 -U appuser appdb

# Run write-heavy benchmark (measures sync replication impact)
pgbench -h haproxy-host -p 5000 -U appuser -c 32 -j 8 -T 300 appdb
# Compare with async: temporarily set synchronous_commit = off

# Run read-only benchmark through read replica port
pgbench -h haproxy-host -p 5001 -U appuser -c 64 -j 16 -T 300 -S appdb

# Measure failover impact on transactions
# Run pgbench in background, then trigger a failover
pgbench -h haproxy-host -p 5000 -U appuser -c 8 -j 4 -T 600 appdb &
sleep 60 && patronictl switchover --master node1 --candidate node2 --force

Заключение

Встроенная высокая доступность PostgreSQL с Patroni — это не решение, состоящее из одного инструмента. Это интегрированная система потоковой репликации, распределенного консенсуса, маршрутизации соединений, объединения пулов соединений, архивирования WAL, мониторинга и операционной дисциплины. Каждый уровень отвечает за определенный режим сбоя: потоковая репликация обеспечивает избыточность данных, Patroni обеспечивает автоматическую координацию аварийного переключения, etcd обеспечивает распределенный консенсус, необходимый для выбора лидера без разделения мозга, HAProxy направляет соединения к нужному лидеру, PgBouncer управляет накладными расходами на соединения в масштабе, а архивирование WAL с помощью pgBackRest обеспечивает последнюю линию защиты от логических ошибок и аварийного восстановления.

Шаблоны развертывания различаются в зависимости от среды — AWS с NLB и Route53, Azure с зонами доступности и балансировщиком нагрузки Azure, GCP с региональными управляемыми группами экземпляров или «голое железо» с k3s, Longhorn и Keepalived — но базовая архитектура остается той же. Три или более узлов PostgreSQL, управляемых Patroni, на базе кластера etcd из трех узлов, перед которым расположен балансировщик нагрузки, который следует за конечными точками проверки работоспособности Patroni.

Самая важная инвестиция, которую вы можете сделать, — это не настройка, а тестирование. Ежемесячно проводите тренировки по отказоустойчивости. Внедрить сетевые разделы. Неожиданно завершить процессы. Измерьте время восстановления и потерю данных. Создавайте информационные панели, которые в режиме реального времени отображают задержку репликации, скорость генерации WAL, насыщенность пула соединений и состояние DCS. Уверенность, которую вы получаете в результате систематического тестирования, — это то, что отличает кластер, переживший первый реальный сбой, от кластера, который превращает сбой сервера в инцидент, влияющий на бизнес.

PostgreSQL предоставляет вам все примитивы репликации. Патрони дает вам оркестровку. etcd дает вам консенсус. Ваша задача — правильно соединить их вместе, настроить под свою рабочую нагрузку и постоянно проверять. В этом руководстве представлены чертежи — теперь с уверенностью создавайте, тестируйте и работайте.