Настройка высоко доступного кластера 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 обеспечивает автоматическое переключение при сбоях.

Схема работы:

  1. Приложение подключается к HAProxy.
  2. HAProxy направляет запросы на запись к лидеру PostgreSQL, а запросы на чтение — к репликам.
  3. 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

1it.pro SQL DevOps*

🇺🇦 Stop Russian Aggression!
See what you can do →

Выберите мессенджер

Telegram Email Forms