Вдохновлено этой статьей и адаптировано под свои нужды
В данной инструкции мы рассмотрим процесс установки и настройки Patroni с Consul.
Предварительная настройка
Прежде чем перейти к настройке patroni и кластера, подготовим наши серверы баз данных.
У нас есть 3 хоста, и 2 сети.
Сеть 192.168.251.0/24 - является виртуальной локалкой для сервисных подключений, по ней мы и буем пускать трафик кластера, чтобы он передавал данные по нему
Сеть 192.168.99.0/24 - тоже локалка, но представим, что она публичная
Настройка синронизации времени
По умолчанию в Debian 12 используется systemd-timesyncd
для синхронизации времени. Чтобы отключить его:
sudo systemctl stop systemd-timesyncd
sudo systemctl disable systemd-timesyncd
sudo systemctl mask systemd-timesyncd # (опционально) блокирует повторный запуск
sudo systemctl stop ntp
sudo systemctl disable ntp
Проверьте статус:
timedatectl status
apt update && apt install -y chrony
systemctl enable chrony --now
systemctl status chrony
Просмотр источников времени
chronyc sources -v
Проверка синхронизации
chronyc tracking
timedatectl
Настройка брандмауэра
Вводим команды:
iptables -A INPUT -p tcp --dport 5432 -j ACCEPT && \
iptables -A INPUT -p tcp --dport 8008 -j ACCEPT && \
iptables -A INPUT -p tcp --dport 8300 -j ACCEPT && \
iptables -A INPUT -p tcp --dport 8301 -j ACCEPT && \
iptables -A INPUT -p udp --dport 8301 -j ACCEPT && \
iptables -A INPUT -p tcp --dport 8302 -j ACCEPT && \
iptables -A INPUT -p udp --dport 8302 -j ACCEPT && \
iptables -A INPUT -p tcp --dport 8500 -j ACCEPT && \
iptables -A INPUT -p tcp --dport 8600 -j ACCEPT
Для сохранения правил вводим:
apt install iptables-persistent
netfilter-persistent save
Настройка Consul
sudo apt install -y curl wget gnupg2 software-properties-common
curl -fsSL https://apt.releases.hashicorp.com/gpg | sudo apt-key add -
sudo apt-add-repository "deb [arch=amd64] https://apt.releases.hashicorp.com $(lsb_release -cs) main"
sudo apt update
sudo apt install -y consul
cd /etc/consul.d/
mv consul.hcl consul.hcl.bak
/etc/consul.d/consul.hcl
- node1, для остальных нод описываем по аналогии
datacenter = "dc1"
data_dir = "/opt/consul"
server = true
bootstrap_expect = 3
client_addr = "0.0.0.0"
bind_addr = "192.168.251.13"
retry_join = ["192.168.251.14", "192.168.251.15"]
ui = true
bind_addr
- адрес ноды
retry_join
- список адресов других код кластера консул
sudo systemctl enable consul --now
ui будет доступен по адресу http://192.168.251.21:8500
Проверяем участников
consul members
Подготовка СУБД
sudo sh -c 'echo "deb http://apt.postgresql.org/pub/repos/apt $(lsb_release -cs)-pgdg main" > /etc/apt/sources.list.d/pgdg.list'
wget --quiet -O - https://www.postgresql.org/media/keys/ACCC4CF8.asc | sudo apt-key add -
sudo apt update
sudo apt install -y postgresql-17
Patroni самостоятельно инициализирует базу и настройки, по этому ему небходимо удалить все данные и создать их заново. Все данные, на сервере Postgres будут удалены. Помните об этом и переносите базу в кластер уже после его деплоя.
На наших серверах смотрим путь до каталога с данными Postgres:
su - postgres -c "psql -c 'SHOW data_directory;'"
В моем случае для обоих серверов путь был /var/lib/postgresql/17/main
— сохраняем путь.
Необходимо выключить автостарт Postgres, его запуском будет заниматься Patroni
systemctl disable postgresql --now
Удолям данные Postgres
rm -rf /var/lib/postgresql/17/main/*
Установка и настройка Patroni
Данное приложение разработано на Python и требует установки как последнего, так и некоторых его компонентов.
Выполняем команду:
sudo apt install python3 python3-pip python3-psycopg2
* в нашем примере мы будем работать с python версии 3. Также нам нужна библиотека для работы с postgresql python3-psycopg2.
Теперь ставим сам patroni, как приложение python:
pip3 install patroni[consul] --break-system-packages
Установка завершена. Переходим к настройке.
Создадим каталог для хранения конфигурации:
mkdir /etc/patroni
И сам конфигурационный файл:
nano /etc/patroni/patroni.yml
name: deb-postgres-node-1
scope: pgdb
watchdog:
mode: off
consul:
host: "localhost:8500"
register_service: true
restapi:
listen: 0.0.0.0:8008
connect_address: "192.168.251.13:8008"
auth: 'patrest:oeledEEW32'
bootstrap:
dcs:
ttl: 30
loop_wait: 10
maximum_lag_on_failover: 1048576
postgresql:
use_pg_rewind: true
use_slots: true
parameters:
archive_mode: "on"
wal_level: hot_standby
max_wal_senders: 10
wal_keep_segments: 8
archive_timeout: 1800s
max_replication_slots: 5
hot_standby: "on"
wal_log_hints: "on"
initdb:
- encoding: UTF8
- data-checksums
pg_hba:
- local all all trust
- host replication replicator 192.168.251.13/32 md5
- host replication replicator 192.168.251.14/32 md5
- host replication replicator 192.168.251.15/32 md5
- host replication replicator 127.0.0.1/32 trust
- host all all 0.0.0.0/0 md5
postgresql:
pgpass: /var/lib/postgresql/.pgpass
listen: 0.0.0.0:5432
connect_address: "192.168.99.13:5432"
data_dir: /var/lib/postgresql/17/main/
bin_dir: /usr/lib/postgresql/17/bin/
pg_rewind:
username: postgres
password: fijofpbFR42
replication:
username: replicator
password: cbzxZvv24
superuser:
username: postgres
password: fijofpbFR42
- данные конфигурационные файлы на разных серверах будут немного различаться. Опишем подробнее опции, на которые нам нужно обратить внимание:
- name — имя узла, на котором настраивается данный конфиг.
- scope — имя кластера. Его мы будем использовать при обращении к ресурсу, а также под этим именем будет зарегистрирован сервис в consul.
- consul - token — если наш кластер consul использует ACL, необходимо указать токен.
- restapi - connect_address — адрес на настраиваемом сервере, на который будут приходить подключения к patroni.
- restapi - auth — логин и пароль для аутентификации на интерфейсе API.
- pg_hba — блок конфигурации pg_hba для разрешения подключения к СУБД и ее базам. Необходимо обратить внимание на подсеть для строки host replication replicator. Она должна соответствовать той, которая используется в вашей инфраструктуре.
- postgresql - pgpass — путь до файла, который создаст патрони. В нем будет храниться пароль для подключения к postgresql. Желательно, чтобы это выл домашний каталог пользователя postgres — eval echo ~postgres.
- postgresql - connect_address — адрес и порт, которые будут использоваться для подключения к СУДБ.
- postgresql - data_dir — путь до файлов с данными базы.
- postgresql - bin_dir — путь до бинарников postgresql.
- pg_rewind, replication, superuser — логины и пароли, которые будут созданы для базы.
Создадим юнит в systemd для patroni:
nano /lib/systemd/system/patroni.service
[Unit]
Description=Patroni service
After=syslog.target network.target
[Service]
Type=simple
User=postgres
Group=postgres
ExecStart=/usr/local/bin/patroni /etc/patroni/patroni.yml
ExecReload=/bin/kill -s HUP $MAINPID
KillMode=process
TimeoutSec=30
Restart=no
[Install]
WantedBy=multi-user.target
* обратите внимание, что в официальной документации предлагается не перезапускать автоматически службу (Restart=no). Это дает возможность разобраться в причине падения базы.
Перечитываем конфигурацию systemd:
systemctl daemon-reload
Разрешаем автозапуск и стартуем сервис:
systemctl enable patroni --now
Проверяем статусы сервиса на обоих серверах:
ТУТ ВСТАЛ
systemctl status patroni
Посмотреть список нод
patronictl -c /etc/patroni/patroni.yml list pgdb
* где pgdb — имя нашей базы (указана с использованием директивы scope в конфигурационном файле patroni).
Мы должны увидеть что-то на подобие:
+ Cluster: pgdb (7493223099775198374) -+---------+-----------+----+-----------+
| Member | Host | Role | State | TL | Lag in MB |
+---------------------+----------------+---------+-----------+----+-----------+
| deb-postgres-node-1 | 192.168.251.13 | Replica | streaming | 2 | 0 |
| deb-postgres-node-2 | 192.168.251.14 | Leader | running | 2 | |
| deb-postgres-node-3 | 192.168.251.15 | Replica | streaming | 2 | 0 |
+---------------------+----------------+---------+-----------+----+-----------+
Можно проверить, что сервер консул разрешает имена кластера:
nslookup -port=8600 master.pgdb.service.consul 127.0.0.1
nslookup -port=8600 replica.pgdb.service.consul 127.0.0.1
* где consul — настроенный в consul домен.
Команды нам должны вернуть адреса соответствующих серверов — лидера и реплики.
Наш кластер в рабочем состоянии.
Использование ACL на сервере Consul
Как было сказано выше, если сервер консула использует проверку доступа на основе ACL, необходимо прописать token. Рассмотрим процесс подробнее.
Сначала на сервере консул создаем файл для политики:
cd /var/lib/consul
vi patroni-policy.json
service_prefix "" {
policy = "write"
}
session_prefix "" {
policy = "write"
}
key_prefix "" {
policy = "write"
}
node_prefix "" {
policy = "read"
}
agent_prefix "" {
policy = "read"
}
Для ее применения вводим команду:
consul acl policy create -name "patroni-policy" -rules @patroni-policy.json
Теперь создаем токен с привязкой к созданной политике:
consul acl token create -description "Token for Patroni" -policy-name patroni-policy
Мы должны увидеть что-то на подобие:
...
SecretID: 5adb466f-b6ee-9048-6458-e8edbffc42a3
...
Это и есть токен, который нужно прописать в конфигурационном файле patroni:
nano /etc/patroni/patroni.yml
...
consul:
...
token: 5adb466f-b6ee-9048-6458-e8edbffc42a3
Установка patroni в своем окружении
Мы можем захотеть установить приложение patroni в отдельном окружении python. Для этого выполняем команду, чтобы создать его:
python3 -m venv --copies /opt/patroni
* в данном примере будет создано окружение в каталоге /opt/patroni.
Запустим виртуальное окружение:
source /opt/patroni/bin/activate
Установим необходимые модули python внутри созданного окружения:
pip3 install patroni[consul] psycopg[binary,pool]
* в процессе установки модулей мы можем получить ошибки выполнения команды. Как правило, они связаны с отсутствием необходимых пакетов в системе. Следуйте подсказкам в сообщениях, чтобы решить проблему самостоятельно.
Создадим конфигурационный файл по пути, где находится окружение:
mkdir /opt/patroni/etc
vi /opt/patroni/etc/patroni.yml
Само содержимое файла рассмотрено выше
Файл для systemd немного отличается от того, что мы разбирали ранее:
vi /lib/systemd/system/patroni.service
[Unit]
Description=Patroni service
After=syslog.target network.target
[Service]
Type=simple
User=postgres
Group=postgres
ExecStart=/bin/bash -c "source /opt/patroni/bin/activate && patroni /opt/patroni/etc/patroni.yml"
WorkingDirectory=/opt/patroni
ExecReload=/bin/kill -s HUP $MAINPID
KillMode=process
TimeoutSec=30
Restart=no
[Install]
WantedBy=multi-user.target
* в данном примере мы сначала активируем окружение, в котором установлен patroni, после запускаем его. Также обратите внимание на опцию WorkingDirectory, в которой указан рабочий каталог (созданное окружение).
Остальные дейсвия по запуску описаны выше.
Примеры некоторых команд patronictl
Рассмотрим несколько полезных команд для работы с patroni при помощью patronictl.
1. Настройка системной переменной.
По умолчанию, для ввода команд patronictl необходимо указывать путь до конфигурационного файла, например, мы вводили команду:
patronictl -c /etc/patroni/config.yml list
Чтобы этого не делать, мы можем указать путь до конфига с помощью системной переменной PATRONICTL_CONFIG_FILE:
export PATRONICTL_CONFIG_FILE=/etc/patroni/config.yml
Если нам нужно, чтобы системная переменная задавалась автоматически при входе пользователя в консоль, открываем файл:
vi ~/.bashrc
И добавляем:
export PATRONICTL_CONFIG_FILE=/etc/patroni/config.yml
Теперь можно вводить команду patronictl без указания пути до конфигурационного файла.
2. Список кластеров и узлов.
Команда позволяет увидеть кластеры, их узлы со статусами и сводной информацией. Смотрим с помощью аргумента list.
patronictl list
Или если мы не выполнили рекомендации пункта 1:
patronictl -c /etc/patroni/config.yml list
Если у нас несколько кластеров, то можно указать конкретный, чтобы получить список узлов только для него, например:
patronictl list pgb
3. Смена лидера.
Может быть выполнена командой patronictl с аргументом failover. Например:
patronictl failover pgdb
* где pgdb — имя кластера патрони.
Система покажет список узлов, на которые можно передать функцию лидера — указываем его:
Candidate ['postgresql2'] []: postgresql2
И подтверждаем наш выбор:
Are you sure you want to failover cluster pgdb, demoting current master postgresql2? [y/N]: y
В нашем примере, роли лидера переедет на сервер postgresql2.
4. Повторная инициализация узла кластера.
Бывает так, что один из участников кластера подвисает и репликация может работать неправильно. Мы можем увидеть в статусе большое значение для колонки Lag in MB, например:
| Member | Host | Role | State | TL | Lag in MB |
| postgresql1 | 192.168.1.10 | Leader | running | 1 | |
| postgresql2 | 192.168.1.20 | Replica | running | 1 | 1054 |
В этом случае нам может понадобиться повторно инициализировать узел для кластера. Это выполняется с помощью аргумента reinit:
patronictl reinit pgdb
Система задаст вопрос, для какого узла выполнить операцию — вписываем имя узла:
Which member do you want to reinitialize [postgresql1, postgresql2]? []: postgresql1
* в нашем примере мы выполним реинициализацию на сервере postgresql1.
Подтверждаем наше действие:
Are you sure you want to reinitialize members postgresql1? [y/N]: y
Посмотреть результат можно командой:
patronictl list
Возможные ошибки
Рассмотрим некоторые ошибки, с которым пришлось столкнуться автору.
password authentication failed
При запуске службы на вторичном сервере (slave) не выполняется репликация и мы можем увидеть в логе сообщение:
pg_basebackup: error: FATAL: password authentication failed for user "replicator"
Причина: не создана учетная запись replicator на сервере postgresql.
Решение: переходим на master сервер. Заходим под пользователем postgres:
su - postgres
Создаем учетную запись для репликации:
createuser --replication -P replicator
Система потребует ввести пароль, который будет использоваться для учетной записи. В нашем примере в конфигурационном файле мы используем password, поэтому нужно ввести именно его.
Проверка подключений
psql -h 192.168.99.14 -p 5432 -U postgres -d postgres
и так подключается к каждому хосту для теста
Как клиенту определить роль
В кластере Patroni клиент может определить, какой узел является мастером, а какие — репликами, несколькими способами:
1. Через REST API Patroni (рекомендуемый способ)
Каждый узел Patroni предоставляет HTTP API (restapi
в конфиге), через который можно запросить его статус.
Запрос статуса узла
curl -s http://192.168.251.15:8008 | jq .
(Замените 192.168.251.15:8008
на адрес любого узла Patroni)
Пример ответа для мастера
{
"state": "running",
"role": "master",
"postgresql": {
"state": "running",
"timeline": 5,
"xlog": {
"location": 12345678
}
}
}
Пример ответа для реплики
{
"state": "running",
"role": "replica",
"postgresql": {
"state": "running",
"timeline": 5,
"xlog": {
"location": 12345678
}
}
}
2. Через Consul
Если Patroni зарегистрирован в Consul (consul.host
в конфиге), мастер будет зарегистрирован как лидер.
Запрос через Consul API
curl -s http://localhost:8500/v1/kv/service/pgdb/leader | jq -r '.[0].Value' | base64 --decode
(Замените pgdb
на ваш scope
из конфига Patroni)
Вывод (IP мастера)
192.168.251.15
3. Через PostgreSQL напрямую
Если подключиться к любому узлу через psql
, можно выполнить запрос:
На мастере
SELECT pg_is_in_recovery();
→ false
(мастер)
На реплике
SELECT pg_is_in_recovery();
→ true
(реплика)
Дополнительные полезные запросы
– Показать текущую роль (PostgreSQL 10+)
SELECT CASE WHEN pg_is_in_recovery() THEN 'replica' ELSE 'master' END AS role;
– Показать статус репликации (только на мастере)
SELECT client_addr, state, sync_state FROM pg_stat_replication;
Как клиентское приложение может автоматически находить мастер?
Через DSN с
target_session_attrs=read-write
(PostgreSQL 10+)psql "postgres://user:pass@host1:5432,host2:5432,host3:5432/postgres?target_session_attrs=read-write"
→ Клиент сам подключится к мастеру.
Через Consul DNS
psql -h pgdb.service.consul -p 5432 -U postgres
(Consul автоматически направляет запросы к мастеру)
Через HAProxy/Keepalived (если настроен балансировщик):
psql -h vip-pg-master -p 5432 -U postgres