Настройка высоко доступного кластера PostgreSQL с Patroni, HAProxy и Docker Compose
Введение
В этом руководстве мы разберем, как настроить высокодоступный (High Availability, HA) кластер PostgreSQL с использованием Patroni, HAProxy и Docker Compose. Этот подход позволяет минимизировать время простоя базы данных, автоматически управляя репликацией и переключением при сбоях. Мы настроим:
- 1 узел HAProxy для балансировки нагрузки.
- 3 узла ETCD для распределенной координации и хранения конфигурации.
- 3 узла PostgreSQL под управлением Patroni для репликации и автоматического переключения.
Эта статья написана для блога 1it.pro SQL DevOps и предполагает базовое знакомство с Docker и PostgreSQL.
Зачем нужен кластер Patroni?
Patroni — это инструмент с открытым исходным кодом, который автоматизирует управление кластерами PostgreSQL. Он обеспечивает:
- Высокую доступность (HA): Автоматически выбирает новую ведущую ноду (лидера) при сбое основной.
- Репликацию: Гарантирует согласованность данных между узлами с помощью потоковой репликации PostgreSQL.
- Управление переключением (failover): Использует ETCD для распределенного консенсуса и выбора лидера.
- Балансировку нагрузки: HAProxy направляет запросы на запись к лидеру, а запросы на чтение — к репликам.
Такой подход идеально подходит для продакшен-сред, где недопустимы простои, например, в интернет-магазинах, финансовых системах или любых приложениях, требующих надежного доступа к данным.
Обзор архитектуры
Архитектура включает:
- HAProxy: Балансировщик нагрузки, направляющий запросы на запись к лидеру PostgreSQL и запросы на чтение к репликам.
- Кластер ETCD (3 узла): Распределенное хранилище ключ-значение, используемое Patroni для выбора лидера и управления конфигурацией.
- Patroni с PostgreSQL (3 узла): Управляет экземплярами PostgreSQL, где одна нода — лидер (master), а остальные — реплики (slaves). Patroni обеспечивает автоматическое переключение при сбоях.
Схема работы:
- Приложение подключается к HAProxy.
- HAProxy направляет запросы на запись к лидеру PostgreSQL, а запросы на чтение — к репликам.
- Patroni использует ETCD для мониторинга состояния кластера и управления переключением.
Предварительные требования
Перед началом убедитесь, что у вас есть:
- Установленные Docker и Docker Compose.
- Хост на базе Linux (например, Ubuntu 22.04).
- Базовые знания PostgreSQL и работы с YAML-файлами.
- Доступ к сети для загрузки Docker-образов.
Пошаговая настройка
Шаг 1: Создание рабочей директории
Начнем с создания директории для проекта:
mkdir patroni-cluster && cd patroni-cluster
Шаг 2: Создание кастомного Docker-образа для Patroni
Нам нужен кастомный Docker-образ с Patroni и PostgreSQL. Вот минимальный Dockerfile
для его создания.
Dockerfile
Создайте файл Dockerfile
:
ARG PG_VERSION=14
FROM postgres:${PG_VERSION}-bookworm
ENV DEBIAN_FRONTEND=noninteractive
ARG PATRONI_DIR=/etc/patroni
ARG PG_DATA_DIR=/var/lib/postgresql/data
RUN apt-get update \
&& apt-get install -y --no-install-recommends \
python3 \
python3-pip \
python3-psycopg2 \
python3-dev \
libpq-dev \
curl \
vim \
&& pip3 install --no-cache-dir patroni[etcd]==3.2.0 --break-system-packages \
&& apt-get clean \
&& rm -rf /var/lib/apt/lists/*
RUN mkdir -p ${PG_DATA_DIR} ${PATRONI_DIR} \
&& chown -R postgres:postgres ${PATRONI_DIR} ${PG_DATA_DIR} \
&& chmod -R 700 ${PG_DATA_DIR} \
&& chmod -R 755 ${PATRONI_DIR}
RUN groupmod -g 2000 postgres && usermod -u 2000 -g 2000 postgres
USER postgres
WORKDIR ${PATRONI_DIR}
CMD ["/usr/local/bin/patroni", "/etc/patroni/patroni.yml"]
Сборка образа
Выполните команду для сборки образа:
docker build -t custom_postgres:pg14 .
Этот Dockerfile:
- Использует PostgreSQL 14 как базовый образ.
- Устанавливает Patroni и необходимые зависимости.
- Создает директории для данных PostgreSQL и конфигурации Patroni.
- Настраивает пользователя
postgres
и права доступа.
Шаг 3: Создание конфигурационного файла Patroni
Patroni требует файл конфигурации (patroni.yml
) для определения параметров кластера. Создайте файл patroni.yml
:
patroni.yml
scope: patroni
name: postgresql-{{ inventory_hostname }}
restapi:
listen: 0.0.0.0:8008
connect_address: {{ ansible_default_ipv4.address }}:8008
etcd:
hosts: {{ hostvars[groups['pt_cluster'][0]]['ansible_host'] }}:2379,{{ hostvars[groups['pt_cluster'][1]]['ansible_host'] }}:2379,{{ hostvars[groups['pt_cluster'][2]]['ansible_host'] }}:2379
postgresql:
listen: 0.0.0.0:5432
connect_address: {{ ansible_default_ipv4.address }}:5432
data_dir: /var/lib/postgresql/data
pgpass: /tmp/pgpass
authentication:
superuser:
username: postgres
password: supersecret
replication:
username: replicator
password: repsecret
Этот файл определяет:
- Имя и область действия кластера (
patroni
). - Настройки REST API для Patroni.
- Параметры подключения к ETCD.
- Настройки подключения и аутентификации PostgreSQL.
Шаг 4: Создание конфигурационного файла HAProxy
HAProxy требуется файл конфигурации для маршрутизации трафика. Создайте директорию для HAProxy и файл конфигурации:
mkdir -p haproxy && touch haproxy/haproxy.cfg
haproxy.cfg
global
maxconn 4096
defaults
mode tcp
timeout connect 5s
timeout client 30s
timeout server 30s
frontend stats
bind *:8404
stats enable
stats uri /stats
stats refresh 10s
frontend psql-front
bind *:5432
default_backend psql-back
backend psql-back
option httpchk GET /leader
server patroni1 {{ hostvars[groups['pt_cluster'][0]]['ansible_host'] }}:8008 check port 8008
server patroni2 {{ hostvars[groups['pt_cluster'][1]]['ansible_host'] }}:8008 check port 8008
server patroni3 {{ hostvars[groups['pt_cluster'][2]]['ansible_host'] }}:8008 check port 8008
Эта конфигурация:
- Настраивает интерфейс статистики на порту 8404.
- Определяет бэкенд для маршрутизации трафика PostgreSQL к лидеру через REST API Patroni.
Шаг 5: Определение файла Docker Compose
Теперь определим сервисы в файле docker-compose.yml
.
docker-compose.yml
services:
etcd:
image: quay.io/coreos/etcd:v3.5.9
hostname: etcd1
container_name: etcd1
network_mode: host
restart: unless-stopped
environment:
ETCD_NAME: etcd1
ETCD_LISTEN_CLIENT_URLS: http://0.0.0.0:2379
ETCD_ADVERTISE_CLIENT_URLS: http://127.0.0.1:2379
ETCD_LISTEN_PEER_URLS: http://0.0.0.0:2380
ETCD_INITIAL_ADVERTISE_PEER_URLS: http://127.0.0.1:2380
ETCD_INITIAL_CLUSTER_TOKEN: patroni-cluster
ETCD_INITIAL_CLUSTER: etcd1=http://127.0.0.1:2380,etcd2=http://127.0.0.2:2380,etcd3=http://127.0.0.3:2380
ETCD_DATA_DIR: /etcd_data
ETCD_INITIAL_CLUSTER_STATE: new
ETCD_ELECTION_TIMEOUT: 1000
ETCD_HEARTBEAT_INTERVAL: 100
volumes:
- etcd1-data:/etcd_data
extra_hosts:
- "etcd1:127.0.0.1"
- "etcd2:127.0.0.2"
- "etcd3:127.0.0.3"
patroni:
image: custom_postgres:pg14
hostname: postgresql-patroni1
container_name: patroni1
network_mode: host
restart: unless-stopped
user: "2000:2000"
environment:
PATRONI_NAME: postgresql-patroni1
PATRONI_POSTGRESQL_CONNECT_ADDRESS: 127.0.0.1:5432
PATRONI_RESTAPI_CONNECT_ADDRESS: 127.0.0.1:8008
PATRONI_RESTAPI_LISTEN: 0.0.0.0:8008
PATRONI_POSTGRESQL_LISTEN: 0.0.0.0:5432
PATRONI_ETCD_HOSTS: '127.0.0.1:2379','127.0.0.2:2379','127.0.0.3:2379'
PATRONI_SCOPE: patroni
PATRONI_POSTGRESQL_PGPASS: /tmp/pgpass
PATRONI_SUPERUSER_USERNAME: postgres
PATRONI_SUPERUSER_PASSWORD: supersecret
PATRONI_REPLICATION_USERNAME: replicator
PATRONI_REPLICATION_PASSWORD: repsecret
volumes:
- patroni1-data:/var/lib/postgresql/data
- ./patroni.yml:/etc/patroni/patroni.yml
extra_hosts:
- "etcd1:127.0.0.1"
- "etcd2:127.0.0.2"
- "etcd3:127.0.0.3"
haproxy:
image: haproxy:2.8
container_name: haproxy
hostname: haproxy
network_mode: host
restart: unless-stopped
volumes:
- ./haproxy/haproxy.cfg:/usr/local/etc/haproxy/haproxy.cfg:ro
ports:
- "5432:5432"
- "8404:8404"
extra_hosts:
- "etcd1:127.0.0.1"
- "etcd2:127.0.0.2"
- "etcd3:127.0.0.3"
volumes:
etcd1-data:
patroni1-data:
Этот docker-compose.yml
:
- Определяет 3 узла ETCD для распределенного консенсуса.
- Определяет 3 узла Patroni с PostgreSQL.
- Определяет 1 узел HAProxy для балансировки нагрузки.
- Использует
network_mode: host
для упрощения (в продакшене настройте сеть).
Шаг 6: Запуск кластера
Запустите все сервисы командой:
docker-compose up -d
Шаг 7: Проверка состояния кластера
Проверьте состояние кластера Patroni с помощью команды patronictl
:
docker exec -it patroni1 patronictl -c /etc/patroni/patroni.yml list patroni
Вы должны увидеть что-то вроде:
+ Cluster: patroni (6893104757524385823) --+----+-----------+
| Member | Host | Role | State | TL | Lag in MB |
+----------+-----------+---------+---------+----+-----------+
| patroni1 | 127.0.0.1 | Replica | running | 8 | 0 |
| patroni2 | 127.0.0.2 | Replica | running | 8 | 0 |
| patroni3 | 127.0.0.3 | Leader | running | 8 | |
+----------+-----------+---------+---------+----+-----------+
Шаг 8: Подключение к PostgreSQL
Для подключения к лидеру через HAProxy выполните:
docker run --rm -ti --network=host postgres:14 psql --host 127.0.0.1 --port 5432 -U postgres -d postgres
Введите пароль supersecret
, когда будет запрошен. Теперь можно выполнять SQL-запросы.
Доступ к кластеру
- Статистика HAProxy: Откройте
http://localhost:8404/stats
в браузере для мониторинга HAProxy. - Прямой доступ к узлу: Подключитесь к конкретному узлу (например,
patroni3
):psql --host 127.0.0.3 --port 5432 -U postgres -d postgres
Устранение неполадок
- ETCD не запускается: Проверьте логи с помощью
docker logs etcd1
. Убедитесь, что порты 2379 и 2380 свободны. - Проблемы с переключением в Patroni: Проверьте подключение к ETCD и логи Patroni (
docker logs patroni1
). - Проблемы с маршрутизацией HAProxy: Убедитесь, что файл
haproxy.cfg
правильно указывает на REST API Patroni.
Заключение
Теперь у вас есть полностью рабочий кластер PostgreSQL с высокой доступностью, автоматическим переключением и балансировкой нагрузки. Этот сетап можно улучшить для продакшена:
- Настройте полноценную сеть вместо
network_mode: host
. - Добавьте мониторинг и алерты.
- Обезопасьте учетные данные и сетевой трафик.
Следите за второй частью, где мы разберем продвинутые настройки и мониторинг!
Теги
- кластер
- pos
- devops
- docker-compose
- patroni