Кластер PostgreSQL с использованием patroni

Опубликовано 15.02.2021

Вот так вот сложилось, что я последнее время плотно работаю с 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 nameIP
etcd-tst-0110.232.84.241
etcd-tst-0210.232.84.242
etcd-tst-0310.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 решение.

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *

*