Классическая связка HA для PostgreSQL — Patroni + etcd. Работает, проверена временем, но у неё есть свойство, которое раздражает всех, кто её эксплуатировал: вы добавили ещё один распределённый кластер, чтобы обслуживать свой распределённый кластер. etcd нужно разворачивать, мониторить, бэкапить, обновлять и чинить — а когда он деградирует, Patroni теряет способность принимать решения.

GFM (Gorgona Failover Manager) предлагает убрать это звено: узлы договариваются напрямую через P2P-меш, без etcd, Consul и ZooKeeper.

Факты ниже — из README проекта и репозитория psqlmaster/gorgona (BSD-3-Clause). Разбор мой. Проект молодой — выводы делайте с поправкой на это, см. раздел про ограничения.

Что это такое

Два слоя:

Шифрование двухслойное: команды защищены end-to-end через RSA-OAEP для обмена ключами и AES-256-GCM для содержимого, а служебный трафик управления — кластерным PSK. Сервер хранит только зашифрованные сообщения и не может прочитать содержимое. От повторов защищает фильтр по возрасту пакета (окно 120 секунд) и дедупликация по скользящему окну из 50 пакетов.

  flowchart LR
    subgraph P["Patroni + etcd"]
        direction TB
        N1["Postgres+Patroni"] --> E[("etcd<br/>внешний DCS")]
        N2["Postgres+Patroni"] --> E
        N3["Postgres+Patroni"] --> E
    end
    subgraph G["GFM: P2P-меш"]
        direction TB
        G1["Postgres+GFM"] <--> G2["Postgres+GFM"]
        G2 <--> G3["Postgres+GFM"]
        G1 <--> G3
    end

Слева состояние живёт во внешнем кластере, справа — распределено между самими узлами.

Как выбирается лидер

Здесь принципиальное отличие от Patroni, где лидерство — это аренда ключа в DCS.

Критерий выбора — LSN. Узел с наибольшим Log Sequence Number становится кандидатом в лидеры: побеждает тот, кто дальше всех продвинулся по WAL, то есть потерял меньше всех данных. При равных LSN тай-брейк детерминированный — по алфавитному порядку имён хостов, чтобы результат был однозначным.

Порог на участие. Чтобы инициировать выборы или стать кандидатом, узлу нужна TCP-связность минимум с (N/2)+1 узлами из списка кворума.

Защита от split-brain: самофенсинг

Самая интересная часть. Лидер каждые 5 секунд проверяет доступность пиров. Как только он теряет кворумную связность — он немедленно останавливает свой PostgreSQL. В документации это прямо называется «suicide».

Логика жёсткая и правильная: изолированный мастер, который продолжает принимать запись, — это гарантированное расхождение данных. Проще убить себя, чем разгребать split-brain.

Обратная сторона очевидна: сетевая проблема приводит к остановке базы, даже если сам PostgreSQL полностью здоров. Это осознанный размен доступности на консистентность, и о нём нужно знать заранее.

Witness-узлы. Чтобы набрать кворум без разворачивания лишних баз, поддерживаются узлы-свидетели: они отличаются портами (кворумный порт задан, а PostgreSQL — нет), дают голос, но не хранят данные. Полезно для схемы «два ЦОДа + арбитр».

Failover и самовосстановление

Сценарий отказа лидера:

  1. Здоровые узлы фиксируют пропажу heartbeat после max_missing_heartbeats циклов.
  2. Узлы, имеющие кворумную связность, запускают выборы.
  3. Узел с наибольшим LSN становится лидером, остальные — standby.
  4. Упавший узел при возвращении автоматически уходит в rebuild.

Восстановление делает скрипт gfm_rebuild.sh: сначала пробует pg_rewind (откатывает разошедшийся timeline с минимальным трафиком), а если локальная база пуста или повреждена — падает обратно на полный pg_basebackup.

Требования к PostgreSQL для этого:

INI
wal_level = replica
wal_log_hints = on
Нажмите, чтобы развернуть и увидеть больше

wal_log_hints обязателен для работы pg_rewind — без него восстановление всегда будет идти долгим путём через полную копию.

Конфигурация и запуск

Единственный источник правды — gfm.conf. Основные секции:

СекцияКлючевые параметры
[cluster]my_pub_hash, admin_pub_hash, quorum_total_nodes, cluster_id, quorum_nodes (в формате IP:PORT)
[postgresql]service_name, pg_version, pg_instance_name, port
[timings]heartbeat_interval, max_missing_heartbeats, monitor_interval, TTL для heartbeat и событий
[paths]base_dir, gorgona_bin, psql_bin, pg_ctl_bin, rebuild_script

Установка:

BASH
chmod +x install.sh
./install.sh ./gfm_prod_5432.conf
Нажмите, чтобы развернуть и увидеть больше

Удаление — с возможностью сохранить данные:

BASH
./uninstall.sh ./gfm.conf                 # снести всё
./uninstall.sh ./gfm.conf --exclude-db    # оставить данные PostgreSQL
Нажмите, чтобы развернуть и увидеть больше

Наблюдение за кластером:

BASH
journalctl -u gfm@CLUSTER_ID -f                  # логи демона
cat /etc/gorgona/status_CLUSTER_ID.json          # состояние кластера
tail -f /var/log/gorgona/rebuild_CLUSTER_ID.log  # ход восстановления
Нажмите, чтобы развернуть и увидеть больше

На одной машине можно держать несколько независимых кластеров: они разделяются по cluster_id, шаблонам systemd и per-cluster блокировкам через fcntl.flock.

Сборка самого Gorgona:

BASH
sudo apt install -y libssl-dev git gcc make
git clone https://github.com/psqlmaster/gorgona.git
cd gorgona && make clean && make
Нажмите, чтобы развернуть и увидеть больше

Поддерживаются Linux (Debian, Fedora, CentOS, Red OS), macOS и даже OpenWrt (x86_64, aarch64).

Чем отличается от Patroni + etcd

Patroni + etcdGFM
Хранилище состоянияВнешний DCS (etcd/Consul/ZK)P2P-меш между узлами
Компонентов обслуживатьPostgreSQL + Patroni + кластер DCSPostgreSQL + GFM
Выбор лидераАренда ключа в DCSПо наибольшему LSN
Защита от split-brainПотеря аренды → демоутПотеря кворума → остановка PostgreSQL
РеализацияPythonC (Gorgona) + демон GFM
ЗрелостьГоды в проде, огромное сообществоМолодой проект

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

Честные ограничения

Прежде чем нести это в прод:

Кому стоит посмотреть

Разумные сценарии:

Где я бы пока остался на Patroni: критичный прод с жёстким SLA, инсталляции в Kubernetes (там свои операторы вроде CloudNativePG, закрывающие ту же задачу иначе) и команды, которым важна предсказуемость и большое сообщество.

Разумный путь — поднять GFM на тестовом стенде, устроить учения (убить лидера, разорвать сеть, проверить rebuild) и сравнить поведение со своим текущим решением. Идея убрать внешний DCS хороша; проверять её лучше на своём железе, а не по статье.

Ссылки: репозиторий · README плагина GFM · статья автора на Хабре

Авторские права

Автор: Vasiliy Koshkin

Ссылка: https://notes.melancholic.tech/posts/gfm-postgresql-ha/

Лицензия: CC BY-NC-SA 4.0

Использование материалов блога разрешается при условии: указания авторства/источника, некоммерческого использования и сохранения лицензии.

Начать поиск

Введите ключевые слова для поиска статей

↑↓
ESC
⌘K Горячая клавиша