Настройка кластера высокой доступности: PostgreSQL + (Patroni и etcd)

от автора

Хабр, привет!

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

Немного теории. Patroni — это инструмент для управления высокодоступными кластерами PostgreSQL. Он упрощает настройку и управление репликацией благодаря автоматическому переключению на резервные узлы и восстановлению после сбоев.

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

Зачем мы это делаем?

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

Итак, приступим.

Систему можно использовать любую, автору захотелось Centos 8 (личное предпочтение автора, не более — прим. ред.). Совет: лучше заранее описать, какие будут имя сервера и IP-адреса. Сетевая структура у нас такая:

etcd1 192.168.60.141 etcd2 192.168.60.142 etcd3 192.168.60.143 node1 192.168.60.131 node2 192.168.60.132

У вас могут быть и другие адреса, поэтому не забудьте их поменять в конфигах. 

Установка и настройка etcd

Шаг 1: скачиваем etcd

Для этого качаем пакет с оф. гита etcd вот отсюда:  https://github.com/etcd-io/etcd/releases/download/v3.5.15/etcd-v3.5.15-linux-amd64.tar.gz

Затем распаковываем архив и перемещаем файлы:

tar -xzvf etcd-v3.5.15-linux-amd64.tar.gz sudo mv etcd-v3.5.15-linux-amd64/etcd* /usr/bin/

Шаг 2: создаем пользователей и директории

1. Выполняем следующие команды:

sudo groupadd --system etcd sudo useradd -s /sbin/nologin --system -g etcd etcd

2. Теперь создаем необходимые директории и устанавливаем права доступа с помощью:

sudo mkdir -p /var/lib/etcd /etc/default/etcd/.tls sudo chown -R etcd:etcd /var/lib/etcd /etc/default/etcd ```

Шаг 3: генерируем сертификаты

Если у вас нет возможности использовать собственные сертификаты, то берем самописные. Небольшой скрипт ниже в этом нам поможет:

Скрытый текст
#!/bin/bash   # Директория для сертификатов: CERT_DIR="/etc/default/etcd/.tls" mkdir -p ${CERT_DIR} cd ${CERT_DIR}   # Создание CA-сертификата: openssl genrsa -out ca.key 4096 openssl req -x509 -new -key ca.key -days 10000 -out ca.crt -subj "/C=RU/ST=Moscow Region/L=Moscow/O=MyOrg/OU=MyUnit/CN=myorg.com"   # Функция для генерации сертификатов для нод: generate_cert() {     NODE_NAME=$1     NODE_IP=$2       cat <<EOF > ${CERT_DIR}/${NODE_NAME}.san.conf [ req ] default_bits       = 4096 distinguished_name = req_distinguished_name req_extensions     = req_ext [ req_distinguished_name ] countryName                 = RU stateOrProvinceName         = Moscow Region localityName                = Moscow organizationName            = MyOrg commonName                  = ${NODE_NAME} [ req_ext ] subjectAltName = @alt_names [ alt_names ] DNS.1   = ${NODE_NAME} IP.1    = ${NODE_IP} IP.2    = 127.0.0.1 EOF   openssl genrsa -out ${NODE_NAME}.key 4096 openssl req -config ${NODE_NAME}.san.conf -new -key ${NODE_NAME}.key -out ${NODE_NAME}.csr -subj "/C=RU/ST=Moscow Region/L=Moscow/O=MyOrg/CN=${NODE_NAME}"     openssl x509 -extfile ${NODE_NAME}.san.conf -extensions req_ext -req -in ${NODE_NAME}.csr -CA ca.crt -CAkey ca.key -CAcreateserial -out ${NODE_NAME}.crt -days 10000       rm -f ${NODE_NAME}.san.conf ${NODE_NAME}.csr }   # Список нод etcd и их IP-адресов: ETCD_NODES=("etcd01" "etcd02" "etcd03") ETCD_IPS=("192.168.60.141" "192.168.60.142" "192.168.60.143")   # Список нод Patroni и их IP-адресов: PATRONI_NODES=("node01" "node02") PATRONI_IPS=("192.168.60.131" "192.168.60.132")     # Генерация сертификатов для нод etcd: for i in "${!ETCD_NODES[@]}"; do generate_cert "${ETCD_NODES[$i]}" "${ETCD_IPS[$i]}" done   # Генерация сертификатов для нод Patroni: for i in "${!PATRONI_NODES[@]}"; do generate_cert "${PATRONI_NODES[$i]}" "${PATRONI_IPS[$i]}" done     chown -R etcd:etcd ${CERT_DIR} chmod 600 ${CERT_DIR}/*.key chmod 644 ${CERT_DIR}/*.crt   echo "Сертификаты успешно сгенерированы и сохранены в ${CERT_DIR}"

Запускаем скрипт:

chmod +x generate_etcd_certs.sh sudo ./generate_etcd_certs.sh

Сделали. Чтобы узлы etcd взаимодействовали между собой, копируем ca.crt и node[01 или 03] на остальные узлы.

Теперь меняем права на всех узлах etcd:

chown -R etcd:etcd /etc/default/etcd/.tls chmod -R 744 /etc/default/etcd/.tls chmod 600 /etc/default/etcd/.tls/*.key

Шаг 4: определяем, как должен работать etcd (параметры)

На каждой ноде создаем конфиг, который минимально отличается от ноды к ноде (подсветили комментариями):

# /etc/etcd/etcd.conf.yml name: etcd01 # Изменить на других нодах data-dir: /var/lib/etcd/default listen-peer-urls: https://0.0.0.0:2380 listen-client-urls: https://0.0.0.0:2379 advertise-client-urls: https://etcd01:2379 # Изменить на других нодах initial-advertise-peer-urls: https://etcd01:2380 # Изменить на других нодах initial-cluster-token: etcd_scope initial-cluster: etcd01=https://etcd01:2380,etcd02=https://etcd02:2380,etcd03=https://etcd03:2380 initial-cluster-state: new election-timeout: 5000 heartbeat-interval: 500   client-transport-security:   cert-file: /etc/default/etcd/.tls/etcd01.crt # Изменить на других нодах   key-file: /etc/default/etcd/.tls/etcd01.key   client-cert-auth: true   trusted-ca-file: /etc/default/etcd/.tls/ca.crt   peer-transport-security:   cert-file: /etc/default/etcd/.tls/etcd01.crt # Изменить на других нодах   key-file: /etc/default/etcd/.tls/etcd01.key   client-cert-auth: true   trusted-ca-file: /etc/default/etcd/.tls/ca.crt

Шаг 5: для запуска etcd cоздаем Systemd-Unit-файл

Как правило, сервис создается по следующему пути (мы использовали его): /etc/systemd/system/etcd.service

[Unit] Description=etcd key-value store Documentation=https://etcd.io/docs/ Wants=network-online.target After=network-online.target   [Service] User=etcd Type=notify ExecStart=/usr/bin/etcd --config-file=/etc/etcd/etcd.conf.yml Restart=always RestartSec=5 LimitNOFILE=40000   [Install] WantedBy=multi-user.target

Шаг 6: применяем следующие команды

sudo systemctl daemon-reload sudo systemctl enable etcd

Заметка на полях: Помним, что любое изменение конфига Systemd для новых настроек требует выполнения команд выше. А также не забываем, что поезд etcd ждать не будет 🙂 (у нас есть только 30 секунд, чтобы запустить другие ноды кластера).

На этом этапе поочередно запускаем сервис с 1-й по 3-ю ноду с помощью команды: sudo systemctl start etcd

Шаг 7: настраиваем alias для etcdctl

Для этого добавляем alias в наш `~/.bashrc` или `~/.zshrc`:

echo ‘alias ectl=»etcdctl —cacert=/etc/default/etcd/.tls/ca.crt —cert=/etc/default/etcd/.tls/$(hostname).crt —key=/etc/default/etcd/.tls/$(hostname).key —endpoints=https://etcd01:2379,https://etcd02:2379,https://etcd03:2379«‘ >> ~/.bashrc
source ~/.bashrc

Благодаря этому мы получаем статус кластера etcd, не прописывая каждый раз сертификаты.

Примечание. Учтите, что имя crt и key нод должно быть аналогично hostname. Иначе команда выше не сработает и придется все настраивать вручную.

Получаем удобную таблицу:

После того как кластер запустился, на всех узлах редактируем конфиг `/etc/default/etcd` и устанавливаем параметр:

ETCD_INITIAL_CLUSTER_STATE="existing" ```

Перезапускаем службу:

systemctl restart etcd

Фух! С etcd закончили, 🙂 приступаем к Postgres и Patroni.

Установка PostgreSQL

Шаг 1: скачиваем и устанавливаем пакеты из оф. репозитория

Делаем это отсюда: https://download.postgresql.org/pub/repos/

NB: В системе по умолчанию создается daemon PostgreSQL. Его требуется отключить. Применяем команду:

systemctl --now disable postgresql-16

Затем меняем настройки пользователя Postgres при необходимости:

mkdir /home/postgres chown postgres:postgres /home/postgres usermod --home /home/postgres postgres

Шаг 2: готовим каталог PGDATA

Для этого делаем:

mkdir -p /data/16 chmod -R 700 /data mkdir /data/log chown -R postgres:postgres /data mkdir -p /var/run/postgresql chown postgres:postgres /var/run/postgresql

Важное примечание на полях: на astra linux встречали удаление директории  /var/run/postgresql для сокетов, если ставить Postgres из оф. репозитория. Это можно поправить, если изменить сервис Postgres и добавить RuntimeDirectory=postgresql

Теперь нам понадобится каталог для сертификатов:
mkdir -p /opt/patroni/.tls

Помните, как мы сгенерировали сертификаты для etcd? Там же есть сертификаты для Patroni. Забираем их оттуда на узлы Patroniи назначаем права:

chmod -R 744 /opt/patroni/.tls chmod 600 /opt/patroni/.tls/*.key

Установка Patroni: подготовительные работы

Шаг 1: устанавливаем свежую версию Python

Если вдруг ее не оказалось:

yum install openssl-devel libffi-devel bzip2-devel tar -xzvf Python-3.12.4.tgz cd Python-3.12.4/ ./configure --enable-optimizations --with-ssl make altinstall sudo ln -sf /usr/local/bin/python3.12 /usr/bin/python3 sudo ln -sf /usr/local/bin/pip3.12 /usr/bin/pip3

Зачем? Так мы можем установить Patroni, не повредив систему, а также получить очень удобный каталог с бинарями.

Шаг 2: создаем виртуальную среду

Для этого используем команды:

python3 –m venv /opt/patroni source /opt/patroni/bin/activate mkdir –p /opt/patroni/packages cd /opt/patroni/packages

Сама установка

Копируем whl и архивы в /opt/patroni/packages. Затем скачиваем следующие пакеты (примерный список):

click-8.1.7-py3-none-any.whl dnspython-2.6.1-py3-none-any.whl patroni-3.3.2-py3-none-any.whl prettytable-3.10.2-py3-none-any.whl psutil-6.0.0-cp36-abi3-manylinux_2_12_x86_64.manylinux2010_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl python_dateutil-2.9.0.post0-py2.py3-none-any.whl python-etcd-0.4.5.tar.gz PyYAML-6.0.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl setuptools-72.1.0-py3-none-any.whl six-1.16.0-py2.py3-none-any.whl urllib3-2.2.2-py3-none-any.whl wcwidth-0.2.13-py2.py3-none-any.whl ydiff-1.3.tar.gz psycopg2_binary-2.9.9-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl

Применяем команды:

pip3 install --no-index --find-links=/opt/patroni/packages patroni[etcd3] pip3 install --no-index --find-links=/opt/patroni/packages psycopg2-binary chown -R postgres:postgres /opt/patroni

Patroni есть! Теперь добавляем в профайл PostgreSQL параметры:

export PG_CONFIG=/usr/pgsql-16/bin/pg_config export PATRONI_CONFIG_FILE=/etc/patroni/config.yml

А теперь активируем виртуальную среду: source /opt/patroni/bin/activate

Настройка Patroni

Cоздаем конфигурационный файл /etc/patroni/config.yml на нодах кластера Patroni:

Скрытый текст
patroni scope: patroni_cluster namespace: /patroni name: patroni_node01 # Изменить на 2 ноде log:   level: INFO   dir: /data/log/patroni   file_size: 50000000   file_num: 10 restapi:   listen: 0.0.0.0:8008   connect_address: node01:8008 # Изменить на 2 ноде   verify_client: optional   cafile: /opt/patroni/.tls/ca.crt   certfile: /opt/patroni/.tls/node01.crt # Не забыть изменить сертификаты на 2 ноде   keyfile: /opt/patroni/.tls/node01.key ctl:   cacert: /opt/patroni/.tls/ca.crt # Не забыть изменить сертификаты на 2 ноде   certfile: /opt/patroni/.tls/node01.crt   keyfile: /opt/patroni/.tls/node01.key etcd3:   hosts: ["etcd01:2379", "etcd02:2379", "etcd03:2379"]   protocol: https   cacert: /opt/patroni/.tls/ca.crt   cert: /opt/patroni/.tls/node01.crt # Не забыть изменить сертификаты на 2 ноде   key: /opt/patroni/.tls/node01.key watchdog:   mode: off # Если настроен, можно включить bootstrap:   dcs:     failsafe_mode: true     ttl: 30     loop_wait: 10     retry_timeout: 10     maximum_lag_on_failover: 1048576     synchronous_mode: true     synchronous_mode_strict: true     synchronous_mode_count: 1     master_start_timeout: 30     slots:       prod_replica1:         type: physical     postgresql:       use_pg_rewind: true       use_slots: true       parameters:         shared_buffers: '512MB'         wal_level: 'replica'         wal_keep_size: '512MB'         max_connections: 100         effective_cache_size: '1GB'         maintenance_work_mem: '256MB'         max_wal_senders: 5         max_replication_slots: 5         checkpoint_completion_target: 0.7         log_connections: 'on'         log_disconnections: 'on'         log_statement: 'ddl'         log_line_prefix: '%m [%p] %q%u@%d '         logging_collector: 'on'         log_destination: 'stderr'         log_directory: '/data/log'         log_filename: 'postgresql-%Y-%m-%d.log'         log_rotation_size: '100MB'         log_rotation_age: '1d'         log_min_duration_statement: -1         log_min_error_statement: 'error'         log_min_messages: 'warning'         log_error_verbosity: 'verbose'         log_hostname: 'off'         log_duration: 'off'         log_timezone: 'Europe/Moscow'         timezone: 'Europe/Moscow'         lc_messages: 'C.UTF-8'         password_encryption: 'scram-sha-256'         debug_print_parse: 'off'         debug_print_rewritten: 'off'         debug_print_plan: 'off'         superuser_reserved_connections: 3         synchronous_commit: 'on'         synchronous_standby_names: '*'         hot_standby: 'on'         compute_query_id: 'on'       pg_hba:         - local all all peer         - host all all 127.0.0.1/32 scram-sha-256         - host all all 0.0.0.0/0 md5         - host replication replicator 127.0.0.1/32 scram-sha-256         - host replication replicator 192.168.60.0/24 scram-sha-256   pg_hba:     - local all all peer     - host all all 127.0.0.1/32 scram-sha-256     - host all all 0.0.0.0/0 md5     - host replication replicator 127.0.0.1/32 scram-sha-256     - host replication replicator 192.168.60.0/24 scram-sha-256   initdb: ["encoding=UTF8", "data-checksums", "username=postgres", "auth=scram-sha-256"]   users:     admin:       password: 'new_secure_password1'       options: ["createdb"] postgresql:   listen: 0.0.0.0   connect_address: 192.168.60.131:5432 # не забываем заменить адрес на 2 ноде.   use_unix_socket: true   data_dir: /data/16   config_dir: /data/16   bin_dir: /usr/pgsql-16/bin   pgpass: /home/postgres/.pgpass_patroni   authentication:     replication:       username: replicator       password: 'new_repl_password'     superuser:       username: postgres       password: 'new_superuser_password'     rewind:       username: postgres       password: 'new_superuser_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

Чтобы не пропустить ошибки, проверяем валидность конфига:

patroni --validate-config /etc/patroni/config.yml

Patroni требуется журналирование. Для этого создаем подкаталог для логов:

mkdir /data/log/patroni chown -R postgres:postgres /data/log/patroni

Теперь — Systemd-Unit-файл для Patroni:

service patroni [Unit] Description=Patroni high-availability PostgreSQL After=network.target   [Service] User=postgres Type=simple ExecStart=/opt/patroni/bin/patroni /etc/patroni/config.yml Restart=always RestartSec=5 LimitNOFILE=1024   [Install] WantedBy=multi-user.target

Перезапустите Systemd и запустите Patroni:

sudo systemctl daemon-reload sudo systemctl enable patroni sudo systemctl start patroni

Проверяем список узлов в кластере: patronictl list

Проверяем переключение (switchover)*:
patronictl  switchover
и убеждаемся, что Мастер переехал: у БД есть мастер-нода и slave-нода. Для просмотра подробной работы Patroni и PostgreSQL в арсенале есть логи:

Лог Patroni: /data/log/patroni/patroni.log

Логи экземпляра СУБД: /data/log/postgresql-*.log

Теперь у вас настроен высокодоступный кластер PostgreSQL с использованием Patroni и etcd. В следующей части мы расскажем, как интегрировать балансировщик и пулер pgbouncer в кластер, созданный в этой статье. До встречи!


ссылка на оригинал статьи https://habr.com/ru/articles/847872/


Комментарии

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

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