Вот так вот сложилось, что я последнее время плотно работаю с PostgreSQL, в связи с чем, очередная противозабывательная статья. Всё, здесь описанное, опирается на два великолепных документа: https://its.1c.ru/db/metod8dev/content/5971/hdoc и https://habr.com/ru/post/504044/
В качестве ОС используется Ubuntu 20.04LTS. Сам наш кластер будет состоять из трех серверов ETCD (распределенное хранилище ключ:значение), которые будут хранить состояние кластера patroni и двух серверов PostgreSQL13 (active/standby).
Server name | IP |
etcd-tst-01 | 10.232.84.241 |
etcd-tst-02 | 10.232.84.242 |
etcd-tst-03 | 10.232.84.243 |
pgsql-tst-01 (active) | 10.232.84.244 |
pgsql-tst-02 (standby) | 10.232.84.245 |
Настройка etcd довольна проста, но есть один нюанс — не забудьте после установки первой ноды подключать к ней последующие.
apt-get install etcd
Создаем на etcd-tst-01 файл конфигурации:
sudo vi /etc/default/etcd
Следующего содержания:
#[Member]
ETCD_NAME="etcd-tst-01"
ETCD_DATA_DIR="/var/lib/etcd/default"
ETCD_LISTEN_PEER_URLS="http://0.0.0.0:2380"
ETCD_LISTEN_CLIENT_URLS="http://0.0.0.0:2379"
ETCD_HEARTBEAT_INTERVAL="1000"
ETCD_ELECTION_TIMEOUT="5000"
#[Clustering]
ETCD_INITIAL_ADVERTISE_PEER_URLS="http://10.232.84.241:2380"
ETCD_ADVERTISE_CLIENT_URLS="http://10.232.84.241:2379"
ETCD_INITIAL_CLUSTER="core=http://10.232.84.241:2380"
ETCD_INITIAL_CLUSTER_TOKEN="patroni-cluster"
ETCD_INITIAL_CLUSTER_STATE="new"
Добавляем членов кластера на ноде etcd-tst-01 (просто добавляем, конфигурируем их чуть ниже):
sudo etcdctl member add etcd-tst-02 http://10.232.84.242:2380
sudo etcdctl member add etcd-tst-03 http://10.232.84.243:2380
Устанавливаем etcd на ноды etcd-tst-02 и etcd-tst-03 так же, как указано для. Файл конфигурации для etcd-tst-02:
#[Member]
ETCD_NAME="etcd-tst-02"
ETCD_DATA_DIR="/var/lib/etcd/default"
ETCD_LISTEN_PEER_URLS="http://0.0.0.0:2380"
ETCD_LISTEN_CLIENT_URLS="http://0.0.0.0:2379"
ETCD_HEARTBEAT_INTERVAL="1000"
ETCD_ELECTION_TIMEOUT="5000"
#[Clustering]
ETCD_INITIAL_ADVERTISE_PEER_URLS="http://10.232.84.242:2380"
ETCD_ADVERTISE_CLIENT_URLS="http://10.232.84.242:2379"
ETCD_INITIAL_CLUSTER="etcd-tst-01=http://10.232.84.241:2380,etcd-tst-02=http://10.232.84.242:2380"
ETCD_INITIAL_CLUSTER_TOKEN="patroni-cluster"
ETCD_INITIAL_CLUSTER_STATE="existing"
Файл конфигурации для etcd-tst-03:
#[Member]
ETCD_NAME="etcd-tst-03"
ETCD_DATA_DIR="/var/lib/etcd/default"
ETCD_LISTEN_PEER_URLS="http://0.0.0.0:2380"
ETCD_LISTEN_CLIENT_URLS="http://0.0.0.0:2379"
ETCD_HEARTBEAT_INTERVAL="1000"
ETCD_ELECTION_TIMEOUT="5000"
#[Clustering]
ETCD_INITIAL_ADVERTISE_PEER_URLS="http://10.232.84.243:2380"
ETCD_ADVERTISE_CLIENT_URLS="http://10.232.84.243:2379"
ETCD_INITIAL_CLUSTER="etcd-tst-01=http://10.232.84.241:2380,etcd-tst-02=http://10.232.84.242:2380,etcd-tst-03=http://10.232.84.243:2380"
ETCD_INITIAL_CLUSTER_TOKEN="patroni-cluster"
ETCD_INITIAL_CLUSTER_STATE="existing"
Перезапускаем etcd на нодах etcd-tst-02 и etcd-tst-03:
sudo systemctl restart etcd
В финале изменяем конфиг на etcd-tst-01:
ETCD_INITIAL_CLUSTER="etcd-tst-01=http://10.232.84.241:2380,etcd-tst-02=http://10.232.84.242:2380,etcd-tst-03=http://10.232.84.243:2380"
#ETCD_INITIAL_CLUSTER="etcd-tst-01=http://10.232.84.241:2380"
#ETCD_INITIAL_CLUSTER_STATE="new"
ETCD_INITIAL_CLUSTER_STATE="existing"
Проверяем состояние кластера:
root@etcd-tst-01:/home/mixa# etcdctl cluster-health
member 3e4a8c288c26ac61 is healthy: got healthy result from http://10.232.84.242:2379
member 5cb2247b564d43de is healthy: got healthy result from http://10.232.84.243:2379
member cfe2543272e6a6bb is healthy: got healthy result from http://10.232.84.241:2379
cluster is healthy
Перезапускаем etcd на etcd-tst-01:
sudo systemctl restart etcd
Далее, поднимем на одном из серверов PostgreSQL13 и сделаем вид, что это standalone база, которую впоследствии мы «обернем» кластером patroni.
Добавляем репозиторий PostgreSQL и устанавливаем СУБД:
sudo apt -y install vim bash-completion wget
wget --quiet -O - https://www.postgresql.org/media/keys/ACCC4CF8.asc | sudo apt-key add -
echo "deb http://apt.postgresql.org/pub/repos/apt/ `lsb_release -cs`-pgdg main" |sudo tee /etc/apt/sources.list.d/pgdg.list
sudo apt-get update && apt-get upgrade
sudo apt install postgresql
Ставим pip3, patroni и проверяем версию:
sudo apt install python3-pip
sudo apt-get install patroni
mixa@pgsql-tst-01:/home/mixa# patroni --version
patroni 2.0.1
При необходимости добавляем локали:
sudo locale-gen en_US.UTF-8
sudo locale-gen ru_RU.UTF-8
Подходим к самому интересному, устанавливаем зависимости для работы patroni:
root@pgsql-tst-01:# su - postgres
postgres@pgsql-tst-01:# pip3 install psycopg2-binary
postgres@pgsql-tst-01:# pip3 install python-etcd
Создаем инстанс СУБД. Patroni может и сам его сделать, но мы эмулируем ситуацию конвертации существующей СУБД.
Возможно это мне не повезло, но столкнулся с тем, что в имени инстанса лучше избегать «-«, так как у меня неправильно отрабатывает макрос I% в скрипте systemd для PostgreSQL и портятся пути к файлам конфигурации.
su - postgres
mkdir 13/clust_tst
pg_createcluster -u postgres -p 5434 -d /var/lib/postgresql/13/clust_tst --start-conf=auto -l /var/log/postgresql/clust_tst.log 13 clust_tst
systemctl daemon-reload
systemctl start postgresql@13-clust_tst.service
Нам надо слушать все интерфейсы:
ALTER SYSTEM SET listen_addresses TO '*';
ALTER SYSTEM SET track_commit_timestamp TO 'on';
SELECT pg_reload_conf();
SHOW listen_addresses;
Создаем необходимых для работы patroni пользователей:
CREATE USER patroni_superuser WITH SUPERUSER ENCRYPTED PASSWORD 'patroni_superuser';
CREATE USER patroni_replica WITH REPLICATION ENCRYPTED PASSWORD 'patroni_replica';
Правим pg_hba.conf:
#Patroni
host all postgres 10.232.84.244/24 trust
host postgres patroni_superuser 10.232.84.244/24 md5
host replication patroni_replica 10.232.84.244/24 md5
host replication patroni_replica 10.232.84.245/24 md5
Останавливаем кластер:
systemctl stop postgresql@13-clust_tst.service
Правим /etc/patroni/config.yml:
scope: clust_tst
namespace: /clust_tst/
name: pgsql-tst-01
restapi:
listen: 10.232.84.244:8008
connect_address: 10.232.84.244:8008
etcd:
hosts: 10.232.84.241:2379,10.232.84.242:2379,10.232.84.243:2379
bootstrap:
dcs:
ttl: 100
loop_wait: 10
retry_timeout: 10
maximum_lag_on_failover: 1048576
postgresql:
use_pg_rewind: true
use_slots: true
parameters:
wal_level: replica
hot_standby: "on"
wal_keep_segments: 5120
max_wal_senders: 5
max_replication_slots: 5
checkpoint_timeout: 30
initdb:
- encoding: UTF8
- data-checksums
- locale: en_US.UTF8
- directory: /etc/postgresql/13/clust_tst
- pgdata: /var/lib/postgresql/13/clust_tst
pg_hba:
- local all all md5
- host all all 0.0.0./0 md5
- host all postgres 10.232.84.245/24 trust
- host postgres patroni_superuser 10.232.84.245/24 md5
- host replication patroni_replica 10.232.84.244/24 md5
- host replication patroni_replica 10.232.84.245/24 md5
postgresql:
listen: 10.232.84.244:5434
connect_address: 10.232.84.244:5434
data_dir: /var/lib/postgresql/13/clust_tst
bin_dir: /usr/lib/postgresql/13/bin
config_dir: /etc/postgresql/13/clust_tst
pgpass: /tmp/pgpass
authentication:
replication:
username: patroni_replica
password: patroni_replica
superuser:
username: patroni_superuser
password: patroni_superuser
create_replica_methods:
basebackup:
checkpoint: 'fast'
parameters:
unix_socket_directories: '.'
tags:
nofailover: false
noloadbalance: false
clonefrom: false
nosync: false
Обратите внимание, что etcd может обслуживать много кластеров patroni. Для этого, в конфигурации patroni разные кластера должны иметь разные scope.
Для контроля кластера мы будем использовать patronictl. Нужно ее настроить:
postgres@pgsql-tst-01:~$ vi .config/patroni/patronictl.yaml
dcs_api: etcd://10.232.84.241:2379
namespace: /clust_tst/
scope: clust_tst
Запускаем patroni и проверяем фунционирование пока только одной ноды:
sudo systemctl start patroni
postgres@pgsql-tst-01:~$ patronictl list
+ Cluster: clust_tst (6923040161394500652) --+---------+----+-----------+-----------------+
| Member | Host | Role | State | TL | Lag in MB | Pending restart |
+--------------+--------------------+--------+---------+----+-----------+-----------------+
| pgsql-tst-01 | 10.232.84.244:5434 | Leader | running | 3 | | * |
+--------------+--------------------+--------+---------+----+-----------+-----------------+
Переходим на вторую ноду. Тут patroni должен создать инстанс самостоятельно и завести его в кластер. Для этого /etc/patroni/config.yml в части bootstrap должен максимально соответствовать первому инстансу.
Я сделал базу как и для первой ноды, с помощью pg_createcluster, но удалил содержимое каталога базы данных:
postgres@pgsql-tst-02:~$ rm -rf 13/clust_tst/*
Как говорил выше, база для второй ноды создается с помощью initdb (если не хотите пойти путём пунктом выше). Файл конфигурации для второй ноды:
scope: clust_tst
namespace: /clust_tst/
name: pgsql-tst-02
restapi:
listen: 10.232.84.245:8008
connect_address: 10.232.84.245:8008
etcd:
hosts: 10.232.84.241:2379,10.232.84.242:2379,10.232.84.243:2379
bootstrap:
dcs:
ttl: 100
loop_wait: 10
retry_timeout: 10
maximum_lag_on_failover: 1048576
postgresql:
use_pg_rewind: true
use_slots: true
parameters:
wal_level: replica
hot_standby: "on"
wal_keep_segments: 5120
max_wal_senders: 5
max_replication_slots: 5
checkpoint_timeout: 30
initdb:
- encoding: UTF8
- data-checksums
- locale: en_US.UTF8
- directory: /etc/postgresql/13/clust_tst
- pgdata: /var/lib/postgresql/13/clust_tst
pg_hba:
- local all all md5
- host all all 0.0.0./0 md5
- host all postgres 10.232.84.245/24 trust
- host postgres patroni_superuser 10.232.84.245/24 md5
- host replication patroni_replica 10.232.84.244/24 md5
- host replication patroni_replica 10.232.84.245/24 md5
postgresql:
listen: 10.232.84.245:5434
connect_address: 10.232.84.245:5434
data_dir: /var/lib/postgresql/13/clust_tst
bin_dir: /usr/lib/postgresql/13/bin
config_dir: /etc/postgresql/13/clust_tst
pgpass: /tmp/pgpass
authentication:
replication:
username: patroni_replica
password: patroni_replica
superuser:
username: patroni_superuser
password: patroni_superuser
create_replica_methods:
basebackup:
checkpoint: 'fast'
parameters:
unix_socket_directories: '.'
tags:
nofailover: false
noloadbalance: false
clonefrom: false
nosync: false
log:
dir: /var/log/postgresql
level: INFO
Обратите внимание на раздел log. Если он присутствует, то вся информация о состоянии patroni записывается туда.
Запускаем, проверяем:
systemctl start patroni.service
systemctl status patroni.service
Проверяем состояние кластера:
postgres@pgsql-tst-01:~$ patronictl list
+ Cluster: clust_tst (6923040161394500652) ---+---------+----+-----------+-----------------+
| Member | Host | Role | State | TL | Lag in MB | Pending restart |
+--------------+--------------------+---------+---------+----+-----------+-----------------+
| pgsql-tst-01 | 10.232.84.244:5434 | Leader | running | 3 | | * |
| pgsql-tst-02 | 10.232.84.245:5434 | Replica | running | 3 | 0 | * |
+--------------+--------------------+---------+---------+----+-----------+-----------------+
Вот и все, дальше настройка haproxy или любого другого балансировщика. Учтите, то это ни в коем разе не multimaster решение.