Отказоустойчивый кластер Postgresql
Отказоустойчивая кластерная архитектура для PostgreSQL на основе Patroni, PgBouncer, HAProxy и etcd состоит из нескольких компонентов, каждый из которых играет определенную роль для обеспечения высокодоступности, балансировки нагрузки, отказоустойчивости и упрощения управления подключениями. Вот описание основных компонентов и их взаимодействий:
Компоненты кластера:
- PostgreSQL — Основная база данных.
- Patroni — Управляет высокодоступным кластером PostgreSQL, следит за состоянием кластера и осуществляет failover (переключение на резервную копию) при необходимости.
- etcd (или Consul/Zookeeper) — Координирующая система распределенного консенсуса, которая помогает Patroni поддерживать распределенную информацию о состоянии кластера.
- PgBouncer — Легковесный пул соединений для PostgreSQL, который уменьшает накладные расходы на создание и уничтожение подключений.
- HAProxy — Балансировщик нагрузки, который управляет маршрутизацией трафика между PostgreSQL-кластером, следя за состоянием мастера и реплик.
Взаимодействие компонентов:
- PostgreSQL обеспечивает хранение данных и выполняет роль мастера (primary) или реплики (replica) в кластере.
- Patroni управляет состоянием узлов PostgreSQL. Используя данные от etcd (или другой системы координации), Patroni знает, какой узел является мастером, а какие — репликами. При отказе мастера Patroni автоматически переводит одну из реплик в роль мастера (failover).
- etcd хранит метаданные состояния кластера. Все узлы Patroni взаимодействуют с etcd для получения информации о текущем состоянии мастера, реплик и других метаданных, связанных с кластером.
- PgBouncer служит пулом соединений между приложениями и базой данных PostgreSQL. Он поддерживает устойчивые подключения, уменьшая нагрузку на базу данных при большом количестве подключений и переподключений.
- HAProxy действует как обратный прокси и балансировщик нагрузки. Он направляет трафик приложений к мастеру или репликам PostgreSQL, основываясь на метаданных о состоянии кластера от Patroni (через etcd).
Компоненты кластера и их функции:
1. PostgreSQL
- Основная база данных, которая выполняет хранение и обработку запросов.
- Мастер обрабатывает запросы на запись, а реплики могут обрабатывать запросы только на чтение (если настроено).
2. Patroni
- Функции:
- Контролирует работу PostgreSQL-кластера.
- Осуществляет автоматическое переключение на резервный узел при падении мастера (failover).
- Обеспечивает лидерство в кластере, используя систему координации (etcd).
- Поддерживает консистентное состояние кластера, автоматически поднимая новую реплику при добавлении нового узла или восстановлении упавшего.
- Основные задачи:
- Управление состоянием PostgreSQL (мастер или реплика).
- Мониторинг состояния PostgreSQL и координация через etcd.
- Переключение мастера при сбое.
3. etcd
- Функции:
- Распределённое хранилище для ключей/значений, обеспечивающее консенсус для Patroni.
- Обеспечивает согласованность данных и отказоустойчивость за счёт репликации и поддержания кворума.
- Основные задачи:
- Хранит метаданные кластера PostgreSQL, такие как текущий мастер, список реплик, их состояния.
- Помогает Patroni синхронизировать информацию между узлами.
4. PgBouncer
- Функции:
- Пул соединений, который уменьшает накладные расходы на подключение к PostgreSQL.
- Уменьшает нагрузку на PostgreSQL за счет управления соединениями и временного хранения подключений от клиентов.
- Основные задачи:
- Обеспечивает управление пулами соединений, поддерживая производительность кластера.
- Перенаправляет запросы приложений на соответствующий узел PostgreSQL (мастер или реплика).
5. HAProxy
- Функции:
- Обратный прокси-сервер и балансировщик нагрузки для PostgreSQL-кластера.
- Перенаправляет запросы от клиентов на нужные узлы кластера, в зависимости от состояния (мастер или реплика).
- Может использоваться для балансировки запросов на чтение между репликами.
- Основные задачи:
- Проверка доступности мастера и реплик.
- Маршрутизация запросов на запись к мастеру и запросов на чтение к репликам (если настроено).
- Поддержка отказоустойчивости — если мастер выходит из строя, HAProxy перенаправляет трафик на новый мастер, который был выбран Patroni.
Архитектура кластера:
- Кластер PostgreSQL:
- Несколько узлов PostgreSQL: один в роли мастера (primary), остальные — реплики (standby/replica).
- Мастер обрабатывает все запросы на запись, реплики могут обрабатывать запросы на чтение (опционально).
- Patroni:
- Запущен на каждом узле PostgreSQL.
- Проверяет состояние локального узла (мастер или реплика) и взаимодействует с etcd для синхронизации состояния кластера.
- При сбое мастера Patroni выбирает новую реплику для перевода в мастер.
- etcd:
- Кластер etcd (3 или более узлов для отказоустойчивости).
- Обеспечивает координацию между узлами Patroni.
- Хранит информацию о текущем мастере и репликах, что помогает Patroni координировать действия.
- PgBouncer:
- Локальный на каждом приложении или в отдельном узле между приложением и PostgreSQL.
- Управляет пулом соединений для уменьшения нагрузки на PostgreSQL.
- Может настроить маршрутизацию запросов на основе ролей узлов (мастер/реплика).
- HAProxy:
- Прокси-сервер, который передает запросы от приложения к нужному узлу PostgreSQL.
- Маршрутизирует запросы на запись к мастеру, а запросы на чтение — к репликам.
- Поддерживает отказоустойчивость, перенаправляя трафик на новый мастер при сбое предыдущего.
Пример архитектуры:
Основные процессы:
- Инициализация кластера:
- Patroni запускается на каждом узле PostgreSQL и взаимодействует с etcd для координации кластера.
- Один из узлов становится мастером (primary), остальные — репликами.
- Мониторинг состояния:
- Patroni периодически проверяет состояние каждого узла PostgreSQL и обновляет информацию в etcd.
- HAProxy использует эту информацию для маршрутизации запросов на мастер или реплики.
- Failover:
- Если мастер падает, Patroni выбирает новую реплику для его замены.
- HAProxy и PgBouncer перенаправляют запросы на нового мастера, обеспечивая отказоустойчивость без остановки системы.
- Балансировка запросов:
- HAProxy может балансировать запросы на чтение между репликами, если это настроено, и все запросы на запись отправляются на мастер.
Эта архитектура обеспечивает высокую доступность, масштабируемость, низкие задержки благодаря пулингу соединений и отказоустойчивость через механизмы автоматического failover и балансировку нагрузки.
Развертывание кластера с помощью Ansible
1. Развертываем кластер etcd + Etcdkeeper, как это сделать можно прочитать в следующих заметках:
- HAProxy + etcd + TLS
- Плейбук Ansible для установки HAProxy + TLS для работы с etcd
- Развертывание ETCD Keeper с помощью Ansible
2. Далее переходим к развертыванию кластера Postgresql + Patroni + Pgboucer.
Структура папок:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
postgres-cluster-ansible/ ├── files/ │ └── etcd.service ├── group_vars/ │ └── all.yaml ├── inventory/ │ └── hosts └── templates/ └── haproxy.cfg.j2 └── patroni.yml.j2 └── pgbouncer.ini.j2 ansible.cfg haproxy.yaml ppp.yaml |
files/etcd.service
Для запуска etcd как сервиса systemd
1 2 3 4 5 6 7 8 9 10 |
[Unit] Description=etcd distributed reliable key-value store [Service] Type=notify ExecStart=/opt/etcd/bin/etcd --config-file /etc/etcd/etcd.conf.yaml Restart=always [Install] WantedBy=multi-user.target |
group_vars/all.yaml
Переменные
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
postgresql_version: "14" patroni_config_dir: "/etc/patroni" postgresql_data_dir: "/var/lib/postgresql/{{ postgresql_version }}/data" pgbouncer_listen_port: 6432 pgbouncer_listen_addr: "0.0.0.0" pgbouncer_auth_type: "md5" pgbouncer_pool_mode: "session" cluster_name: "pg_cluster" node_name: "{{ inventory_hostname }}" patroni_etcd_url: "{{ inventory_hostname }}:2379" postgresql_superuser_password: "superpass" replication_password: "replpassword" etcd_username: "root" etcd_password: "etcdpassword" |
inventory/hosts
Группы хостов, к которым будут применены плейбуки
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 |
[etcd_nodes] node1 ansible_host=31.128.39.18 ansible_user=root ansible_ssh_private_key_file="/home/dmitry/.ssh/id_ed25519" node2 ansible_host=31.129.98.136 ansible_user=root ansible_ssh_private_key_file="/home/dmitry/.ssh/id_ed25519" node3 ansible_host=45.147.179.134 ansible_user=root ansible_ssh_private_key_file="/home/dmitry/.ssh/id_ed25519" [patroni_nodes] node1 ansible_host=31.128.39.18 ansible_user=root ansible_ssh_private_key_file="/home/dmitry/.ssh/id_ed25519" node2 ansible_host=31.129.98.136 ansible_user=root ansible_ssh_private_key_file="/home/dmitry/.ssh/id_ed25519" node3 ansible_host=45.147.179.134 ansible_user=root ansible_ssh_private_key_file="/home/dmitry/.ssh/id_ed25519" [postgresql_nodes] node1 ansible_host=31.128.39.18 ansible_user=root ansible_ssh_private_key_file="/home/dmitry/.ssh/id_ed25519" node2 ansible_host=31.129.98.136 ansible_user=root ansible_ssh_private_key_file="/home/dmitry/.ssh/id_ed25519" node3 ansible_host=45.147.179.134 ansible_user=root ansible_ssh_private_key_file="/home/dmitry/.ssh/id_ed25519" [pgbouncer_nodes] node1 ansible_host=31.128.39.18 ansible_user=root ansible_ssh_private_key_file="/home/dmitry/.ssh/id_ed25519" node2 ansible_host=31.129.98.136 ansible_user=root ansible_ssh_private_key_file="/home/dmitry/.ssh/id_ed25519" node3 ansible_host=45.147.179.134 ansible_user=root ansible_ssh_private_key_file="/home/dmitry/.ssh/id_ed25519" [haproxy_nodes] haproxy1 ansible_host=31.128.39.18 ansible_user=root ansible_ssh_private_key_file="/home/dmitry/.ssh/id_ed25519" haproxy2 ansible_host=31.129.98.136 ansible_user=root ansible_ssh_private_key_file="/home/dmitry/.ssh/id_ed25519" |
templates/
В этой папке находятся шаблоны конфигурационных файлов
patroni.yaml.j2
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 |
name: {{ node_name }} scope: {{ cluster_name }} restapi: listen: 0.0.0.0:8008 connect_address: {{ ansible_host }}:8008 # protocol: https # cacert: /etc/etcd/ssl/ca.crt # путь к корневому сертификату CA # cert: /etc/etcd/ssl/server.crt # путь к клиентскому сертификату Patroni # key: /etc/etcd/ssl/server.key # путь к приватному ключу Patroni # authentication: # username: patroni # password: patroni etcd3: hosts: {% for host in groups['etcd_nodes'] %}{{ hostvars[host].ansible_host }}:2379{% if not loop.last %},{% endif %}{% endfor %} version: v3 protocol: https cacert: /etc/etcd/ssl/ca.crt # путь к корневому сертификату CA cert: /etc/etcd/ssl/server.crt # путь к клиентскому сертификату Patroni key: /etc/etcd/ssl/server.key # путь к приватному ключу Patroni username: "{{ etcd_username }}" password: "{{ etcd_password }}" 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: - auth-host: md5 - auth-local: peer - encoding: UTF8 - data-checksums pg_hba: - host replication postgres ::1/128 md5 - host replication postgres 127.0.0.1/8 md5 {% for host in groups['etcd_nodes'] %} - host replication replicator {{ hostvars[host].ansible_host }}/24 md5 {% endfor %} - host all all 0.0.0.0/0 md5 - host replication all 127.0.0.1/32 md5 - host replication all ::1/128 md5 users: admin: password: '{{ postgresql_superuser_password }}' options: - superuser postgresql: listen: 0.0.0.0:5432 connect_address: {{ ansible_host }}:5432 #conf_dir: /etc/postgresql/11.9/main bin_dir: /usr/lib/postgresql/{{ postgresql_version }}/bin data_dir: "{{ postgresql_data_dir }}" pgpass: /tmp/pgpass authentication: superuser: username: postgres password: '{{ postgresql_superuser_password }}' replication: username: replicator password: '{{ replication_password }}' parameters: unix_socket_directories: '/tmp/pgsql/' tags: nofailover: false noloadbalance: false clonefrom: false nosync: false |
1. Основные параметры
- name: {{ node_name }}
- Задает имя узла, на котором будет работать Patroni. Значение подставляется из переменной
node_name
.
- Задает имя узла, на котором будет работать Patroni. Значение подставляется из переменной
- scope: {{ cluster_name }}
- Указывает область кластера (имя кластера), которое также генерируется на основе переменной
cluster_name
. Все узлы в кластере должны иметь одно и то же значениеscope
, чтобы Patroni понимал, что они принадлежат одному и тому же кластеру.
- Указывает область кластера (имя кластера), которое также генерируется на основе переменной
2. REST API
- restapi:
- listen: 0.0.0.0:8008 — указывает адрес и порт, на которых REST API Patroni будет прослушивать запросы. Здесь используется
0.0.0.0
, что позволяет принимать запросы со всех интерфейсов. - connect_address: {{ ansible_host }}:8008 — указывает IP-адрес и порт, по которым другие узлы могут обращаться к этому узлу через API Patroni.
ansible_host
— переменная Ansible, представляющая IP-адрес этого узла. - protocol, cacert, cert, key (закомментированные строки) — используются для настройки HTTPS и сертификатов, если нужно включить защищённый доступ к Patroni через API. Эти параметры позволяют установить клиентские сертификаты и ключи для зашифрованного взаимодействия.
- listen: 0.0.0.0:8008 — указывает адрес и порт, на которых REST API Patroni будет прослушивать запросы. Здесь используется
3. etcd
- etcd3:
- hosts: {% for host in groups[‘etcd_nodes’] %}…{% endfor %} — динамически генерирует список адресов и портов etcd-узлов, к которым Patroni будет подключаться для хранения информации о состоянии кластера. Используется цикл для сбора всех узлов из группы
etcd_nodes
. - protocol: https, cacert, cert, key — настройки для взаимодействия с etcd через зашифрованное соединение (HTTPS). Параметры указывают на пути к сертификатам и ключам для обеспечения безопасности.
- username, password — учётные данные для аутентификации в etcd, параметры задаются через переменные
etcd_username
иetcd_password
.
- hosts: {% for host in groups[‘etcd_nodes’] %}…{% endfor %} — динамически генерирует список адресов и портов etcd-узлов, к которым Patroni будет подключаться для хранения информации о состоянии кластера. Используется цикл для сбора всех узлов из группы
4. Bootstrap (Инициализация кластера)
- bootstrap.dcs:
- Параметры настройки кластерного распределённого консенсуса через etcd (или другой DCS).
- ttl: 100, loop_wait: 10, retry_timeout: 10 — определяют тайминги мониторинга кластера (время жизни сессии, время ожидания цикла и таймауты на повторные попытки).
- maximum_lag_on_failover: 1048576 — максимальный допустимый лаг для реплики, чтобы она могла стать новым мастером при отказе текущего.
- postgresql:
- use_pg_rewind: true — позволяет использовать
pg_rewind
для быстрой синхронизации реплики с мастером после failover. - parameters — задаёт важные параметры конфигурации PostgreSQL, такие как уровень журналирования (wal_level), включение горячей реплики (hot_standby), и другие параметры репликации.
- use_pg_rewind: true — позволяет использовать
- initdb — параметры для инициализации базы данных при первом запуске:
- auth-host: md5, auth-local: peer — задают типы аутентификации для хостов и локальных подключений.
- encoding: UTF8, data-checksums — задают кодировку базы данных и включают контрольные суммы данных.
- pg_hba — динамически генерирует правила доступа для PostgreSQL:
- host replication postgres ::1/128 md5 — доступ для репликации по IPv6.
- {% for host in groups[‘etcd_nodes’] %}…{% endfor %} — задаются динамические правила для каждого узла etcd для репликации через указанный IP-адрес.
- host all all 0.0.0.0/0 md5 — разрешает подключения ко всем пользователям с любых IP-адресов.
5. PostgreSQL
- postgresql:
- listen: 0.0.0.0:5432 — PostgreSQL будет прослушивать все IP-адреса на порту 5432.
- connect_address: {{ ansible_host }}:5432 — IP-адрес и порт для подключения к этому узлу в кластере.
- bin_dir — путь к исполняемым файлам PostgreSQL, зависит от версии (например,
11
,12
). - data_dir — путь к каталогу данных PostgreSQL, задаётся переменной
postgresql_data_dir
. - pgpass — путь к файлу паролей для автоматической аутентификации в PostgreSQL.
- authentication — параметры для подключения суперпользователя и репликатора:
- superuser — логин и пароль суперпользователя PostgreSQL, задаётся через переменные.
- replication — логин и пароль пользователя, ответственного за репликацию.
- parameters — дополнительные параметры конфигурации PostgreSQL, например, путь для сокетов.
6. Теги (Tags)
- nofailover, noloadbalance, clonefrom, nosync — параметры для управления поведением узлов:
- nofailover: false — узел может участвовать в failover.
- noloadbalance: false — узел участвует в балансировке нагрузки.
- clonefrom: false — узел не используется в качестве источника для клонирования.
- nosync: false — узел синхронизируется с кластером.
Заключение:
Шаблон описывает конфигурацию для узла Patroni, который управляет PostgreSQL-кластером.
- Jinja2 шаблон позволяет динамически генерировать конфигурации для каждого узла на основе переменных Ansible, таких как IP-адреса, имена узлов и группы узлов.
- Patroni обеспечивает высокую доступность за счёт координации с etcd, а также автоматического failover и управления кластером PostgreSQL.
- В конфигурации предусмотрены параметры для работы с пулом соединений, настройками репликации и безопасным взаимодействием с etcd через HTTPS.
pgbouncer.ini.js
1 2 3 4 5 6 7 8 9 10 11 12 |
[databases] mydb = host=localhost port=5432 [pgbouncer] listen_addr = 0.0.0.0 listen_port = 6432 auth_type = md5 auth_file = /etc/pgbouncer/userlist.txt pool_mode = transaction max_client_conn = 100 default_pool_size = 20 |
1. [databases] — Конфигурация подключаемых баз данных
Этот блок определяет, к каким базам данных PgBouncer будет подключаться и куда направлять запросы клиентов. В данном случае:
- mydb — это логическое имя базы данных, под которым клиенты будут подключаться через PgBouncer.
- host=localhost — указывает, что PgBouncer будет подключаться к базе данных PostgreSQL, работающей на локальном хосте.
- port=5432 — стандартный порт для PostgreSQL, на котором он слушает подключения.
Это означает, что все запросы, поступающие на PgBouncer
с указанием базы данных mydb
, будут перенаправляться на локальную базу данных PostgreSQL на порту 5432.
2. [pgbouncer] — Настройки самого PgBouncer
Этот блок содержит параметры работы самого PgBouncer.
- listen_addr = 0.0.0.0
- Указывает, на каких IP-адресах PgBouncer будет принимать подключения от клиентов. Значение
0.0.0.0
означает, что PgBouncer будет слушать на всех доступных сетевых интерфейсах.
- Указывает, на каких IP-адресах PgBouncer будет принимать подключения от клиентов. Значение
- listen_port = 6432
- Порт, на котором PgBouncer будет ожидать подключения клиентов. В данном случае это порт 6432 (отличается от стандартного порта PostgreSQL, чтобы избежать конфликтов).
- auth_type = md5
- Тип аутентификации, который будет использоваться PgBouncer для проверки пользователей. В данном случае используется MD5-аутентификация, которая защищает пароли хэшированием.
- auth_file = /etc/pgbouncer/userlist.txt
- Путь к файлу, где хранятся учетные данные пользователей для аутентификации. В файле
userlist.txt
содержатся парыимя пользователя:md5-хэш пароля
.
- Путь к файлу, где хранятся учетные данные пользователей для аутентификации. В файле
- pool_mode = transaction
- Режим работы пула соединений:
- transaction — PgBouncer будет выделять клиенту подключение к PostgreSQL только на время выполнения одного SQL-транзакции. После завершения транзакции соединение возвращается в пул для использования другим клиентом.
- Этот режим наиболее эффективен для высоконагруженных систем, так как он минимизирует время удержания соединения.
- Режим работы пула соединений:
- max_client_conn = 100
- Максимальное количество клиентских подключений, которые PgBouncer одновременно может принимать. Если лимит превышен, новые подключения будут отвергаться.
- default_pool_size = 20
- Количество соединений с PostgreSQL, которые PgBouncer будет поддерживать в пуле для базы данных по умолчанию. Например, для базы данных
mydb
PgBouncer будет поддерживать 20 соединений с PostgreSQL, которые будут повторно использоваться клиентами.
- Количество соединений с PostgreSQL, которые PgBouncer будет поддерживать в пуле для базы данных по умолчанию. Например, для базы данных
Общий принцип работы:
- Подключение клиента: Клиенты подключаются к PgBouncer на порту
6432
и указывают базу данныхmydb
. - Аутентификация: PgBouncer проверяет пользователя через файл
/etc/pgbouncer/userlist.txt
с использованием метода MD5. - Пул соединений: После аутентификации PgBouncer выделяет из пула одно из соединений с базой данных PostgreSQL, которое затем используется для выполнения запросов клиента.
- Режим работы: В режиме transaction после завершения транзакции соединение возвращается в пул для последующего использования другими клиентами, что уменьшает накладные расходы на создание новых подключений.
Этот файл конфигурации настраивает PgBouncer для эффективной работы с подключениями, обеспечивает безопасность с помощью MD5-аутентификации и поддерживает пул соединений, который оптимизирует использование ресурсов базы данных.
haproxy.cfg.j2
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 |
global log 127.0.0.1 local0 maxconn 4096 daemon defaults log global option tcplog retries 3 timeout connect 5000ms timeout client 500000ms timeout server 500000ms frontend pgbouncer_master bind *:6532 mode tcp default_backend pgbouncer_master_backend backend pgbouncer_master_backend mode tcp option httpchk GET /master HTTP/1.1\r\nHost:\ localhost http-check expect status 200 default-server inter 1s fall 3 rise 2 on-marked-down shutdown-sessions {% for host in groups['etcd_nodes'] %} server {{ host }} {{ hostvars[host].ansible_host }}:{{ pgbouncer_listen_port }} maxconn 10000 minconn 10000 check port 8008 {% endfor %} frontend pgbouncer_readonly bind *:6533 mode tcp default_backend pgbouncer_replica_backend backend pgbouncer_replica_backend mode tcp option httpchk GET /replica HTTP/1.1\r\nHost:\ localhost http-check expect status 200 default-server inter 1s fall 3 rise 2 on-marked-down shutdown-sessions {% for host in groups['etcd_nodes'] %} server {{ host }} {{ hostvars[host].ansible_host }}:{{ pgbouncer_listen_port }} maxconn 10000 minconn 10000 check port 8008 {% endfor %} |
1. Глобальная секция (global)
1 2 3 4 5 |
global log 127.0.0.1 local0 maxconn 4096 daemon |
- log 127.0.0.1 local0 — настраивает логирование на локальный syslog-сервер (
127.0.0.1
) с уровнем логированияlocal0
. Это стандартная практика для записи логов HAProxy. - maxconn 4096 — задаёт максимальное количество одновременных соединений, которые HAProxy может обрабатывать (4096 в данном случае).
- daemon — переводит HAProxy в режим фонового процесса (демона).
2. Секция defaults
1 2 3 4 5 6 7 8 |
defaults log global option tcplog retries 3 timeout connect 5000ms timeout client 500000ms timeout server 500000ms |
- log global — указывает, что настройки логирования будут использовать глобальные параметры из секции
global
. - option tcplog — включает запись логов для TCP-соединений.
- retries 3 — определяет количество попыток переподключения к бэкенду в случае неудачи.
- timeout connect 5000ms — время ожидания для установки соединения с бэкендом (5 секунд).
- timeout client 500000ms и timeout server 500000ms — время ожидания активности со стороны клиента и сервера (500 секунд), это важно для длительных транзакций.
3. Секция frontend pgbouncer_master
1 2 3 4 5 |
frontend pgbouncer_master bind *:6532 mode tcp default_backend pgbouncer_master_backend |
- *bind :6532 — связывает frontend с портом
6532
на всех доступных интерфейсах (*
), чтобы HAProxy мог принимать входящие подключения для подключения к мастеру через PgBouncer. - mode tcp — задаёт TCP-режим работы, так как HAProxy будет балансировать соединения на уровне TCP (PgBouncer работает на TCP).
- default_backend pgbouncer_master_backend — указывает, что все запросы, поступающие на этот frontend, будут перенаправляться в backend, который обрабатывает запросы к мастеру базы данных.
4. Секция backend pgbouncer_master_backend
1 2 3 4 5 6 |
backend pgbouncer_master_backend mode tcp option httpchk GET /master HTTP/1.1\r\nHost:\ localhost http-check expect status 200 default-server inter 1s fall 3 rise 2 on-marked-down shutdown-sessions |
- mode tcp — аналогично секции frontend, здесь балансировка также осуществляется на уровне TCP.
- option httpchk GET /master HTTP/1.1\r\nHost:\ localhost — активная проверка состояния (health check). HAProxy будет отправлять HTTP-запрос
/master
для проверки состояния мастера. Это полезно для определения доступности узла мастера PostgreSQL через API Patroni. - http-check expect status 200 — указывает, что узел считается здоровым, если возвращает HTTP-статус
200 OK
. - default-server inter 1s fall 3 rise 2 on-marked-down shutdown-sessions — определяет параметры проверки состояния узлов:
- inter 1s — интервал между проверками состояния (1 секунда).
- fall 3 — узел считается недоступным, если 3 проверки подряд завершатся неудачей.
- rise 2 — узел будет считаться снова доступным, если 2 проверки подряд будут успешными.
- on-marked-down shutdown-sessions — завершает все активные сессии, когда узел помечается как недоступный.
5. Цикл по etcd_nodes для backend pgbouncer_master_backend
1 2 3 4 |
{% for host in groups['etcd_nodes'] %} server {{ host }} {{ hostvars[host].ansible_host }}:{{ pgbouncer_listen_port }} maxconn 10000 minconn 10000 check port 8008 {% endfor %} |
- Это динамически генерируемый список серверов на основе узлов etcd, которые перечислены в группе
etcd_nodes
в инвентаре Ansible. - server {{ host }} — имя сервера (логический идентификатор).
- {{ hostvars[host].ansible_host }}:{{ pgbouncer_listen_port }} — IP-адрес и порт PgBouncer на каждом узле, берутся из переменных Ansible.
- maxconn 10000 minconn 10000 — задаёт минимальное и максимальное количество соединений, которые HAProxy может держать для этого узла.
- check port 8008 — указывает, что HAProxy должен проверять состояние узла через порт
8008
, который обычно используется Patroni для health check.
6. Секция frontend pgbouncer_readonly
1 2 3 4 5 |
frontend pgbouncer_readonly bind *:6533 mode tcp default_backend pgbouncer_replica_backend |
- *bind :6533 — связывает frontend с портом
6533
на всех доступных интерфейсах для обработки запросов к репликам базы данных через PgBouncer. - mode tcp — используется TCP-режим для балансировки.
- default_backend pgbouncer_replica_backend — все запросы будут направляться в backend, обрабатывающий реплики.
7. Секция backend pgbouncer_replica_backend
1 2 3 4 5 6 |
backend pgbouncer_replica_backend mode tcp option httpchk GET /replica HTTP/1.1\r\nHost:\ localhost http-check expect status 200 default-server inter 1s fall 3 rise 2 on-marked-down shutdown-sessions |
- mode tcp — режим работы на уровне TCP.
- option httpchk GET /replica HTTP/1.1\r\nHost:\ localhost — активная проверка для реплик, HAProxy будет отправлять запросы к API Patroni с URI
/replica
, чтобы убедиться, что узел является здоровой репликой. - http-check expect status 200 — узел считается здоровым, если возвращает HTTP-статус
200 OK
. - Остальные параметры идентичны секции
pgbouncer_master_backend
.
8. Цикл по etcd_nodes для backend pgbouncer_replica_backend
1 2 3 4 |
{% for host in groups['etcd_nodes'] %} server {{ host }} {{ hostvars[host].ansible_host }}:{{ pgbouncer_listen_port }} maxconn 10000 minconn 10000 check port 8008 {% endfor %} |
- Этот цикл аналогичен тому, что используется в секции
pgbouncer_master_backend
. Он динамически создает список серверов для реплик, используя те же узлы etcd и параметры соединений.
Заключение:
Этот шаблон конфигурации HAProxy, сгенерированный с помощью Jinja2, позволяет динамически настраивать балансировку нагрузки для кластера PostgreSQL, используя PgBouncer для двух целей:
- Балансировка подключений к мастеру через frontend на порту
6532
. - Балансировка подключений к репликам через frontend на порту
6533
.
API Patroni используется для активной проверки состояния узлов как мастера, так и реплик, что обеспечивает высокую доступность кластера.
Основной плейбук по развертыванию кластера Postgresql + Patroni + Pgbouncer
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 |
--- - hosts: postgresql_nodes become: yes tasks: # Установка необходимых пакетов - name: Install necessary packages for PostgreSQL, Patroni, and PgBouncer apt: name: - postgresql-{{ postgresql_version }} - postgresql-client-{{ postgresql_version }} - postgresql-contrib-{{ postgresql_version }} - pgbouncer - python3-pip - etcd state: present update_cache: yes - name: Установка psycopg>=3.0.0 через pip (альтернатива) pip: name: psycopg version: ">=3.0.0" state: present executable: /usr/bin/pip3 # Укажите путь к вашему pip # Установка Patroni - name: Install Patroni pip: name: patroni[etcd3] state: present - name: Install python-etcd pip: name: python-etcd state: present # Конфигурация Patroni - name: Create Patroni configuration directory file: path: "{{ patroni_config_dir }}" state: directory mode: '0755' - name: Create Patroni configuration file template: src: patroni.yml.j2 dest: "{{ patroni_config_dir }}/patroni.yml" mode: '0644' - name: Создание юнит-файла systemd для Patroni copy: dest: /etc/systemd/system/patroni.service content: | [Unit] Description=Patroni High Availability PostgreSQL After=network.target [Service] Type=simple User=postgres ExecStart=/usr/local/bin/patroni /etc/patroni/patroni.yml Restart=always LimitNOFILE=1024 [Install] WantedBy=multi-user.target - name: Перезагрузка systemd для загрузки нового юнит-файла command: systemctl daemon-reload - name: Ensure /var/lib/postgresql/.ansible/tmp exists with correct permissions file: path: /var/lib/postgresql/.ansible/tmp state: directory owner: postgres group: postgres mode: '0755' - name: Stop PostgreSQL service systemd: name: postgresql state: stopped enabled: yes - name: Enable and start Patroni service systemd: name: patroni enabled: yes state: restarted # Конфигурация PgBouncer - name: Установка PgBouncer apt: name: pgbouncer state: present - name: Настройка PgBouncer copy: dest: /etc/pgbouncer/pgbouncer.ini content: | [databases] * = host=localhost port=5432 [pgbouncer] listen_addr = {{ pgbouncer_listen_addr }} listen_port = {{ pgbouncer_listen_port }} auth_type = {{ pgbouncer_auth_type }} auth_file = /etc/pgbouncer/userlist.txt pool_mode = {{ pgbouncer_pool_mode }} max_client_conn = 1000 default_pool_size = 20 - name: Добавление пользователей PgBouncer copy: dest: /etc/pgbouncer/userlist.txt content: | "postgres" "{{ postgresql_superuser_password }}" # "postgres" "md5{{ postgresql_superuser_password | password_hash('md5') }}" - name: Configure systemd service for PgBouncer copy: content: | [Unit] Description=PgBouncer After=network.target [Service] Type=simple ExecStart=/usr/sbin/pgbouncer /etc/pgbouncer/pgbouncer.ini User=postgres Group=postgres Restart=always [Install] WantedBy=multi-user.target dest: /etc/systemd/system/pgbouncer.service owner: root group: root mode: '0644' - name: Reload systemd daemon systemd: daemon_reload: yes # - name: Запуск PgBouncer # systemd: # name: pgbouncer # state: started # enabled: yes - name: Запуск PgBouncer systemd: name: pgbouncer enabled: yes state: restarted # handlers: # - name: Перезапустить PostgreSQL # service: # name: postgresql # state: restarted |
1. Инициализация и установка пакетов
Установка необходимых пакетов
1 2 3 4 5 6 7 8 9 10 11 12 |
- name: Install necessary packages for PostgreSQL, Patroni, and PgBouncer apt: name: - postgresql-{{ postgresql_version }} - postgresql-client-{{ postgresql_version }} - postgresql-contrib-{{ postgresql_version }} - pgbouncer - python3-pip - etcd state: present update_cache: yes |
- Устанавливаются основные пакеты, необходимые для PostgreSQL, Patroni, PgBouncer и Python.
{{ postgresql_version }}
— переменная, которая определяет, какую версию PostgreSQL нужно установить.- pgbouncer — это легковесный пул соединений для PostgreSQL.
- etcd — необходим для координации кластера Patroni.
Установка Python-зависимостей через pip
1 2 3 4 5 6 7 |
- name: Установка psycopg>=3.0.0 через pip (альтернатива) pip: name: psycopg version: ">=3.0.0" state: present executable: /usr/bin/pip3 |
- Устанавливается модуль psycopg, который используется для взаимодействия с PostgreSQL через Python.
Установка Patroni и зависимостей для Etcd
1 2 3 4 5 6 7 8 9 10 |
- name: Install Patroni pip: name: patroni[etcd3] state: present - name: Install python-etcd pip: name: python-etcd state: present |
- Patroni — это инструмент для управления высокодоступными кластерами PostgreSQL.
- Устанавливается с поддержкой etcd3 как одного из backend для хранения данных кластера.
2. Настройка Patroni
Создание каталога конфигурации и конфигурационного файла Patroni
1 2 3 4 5 6 7 8 9 10 11 12 |
- name: Create Patroni configuration directory file: path: "{{ patroni_config_dir }}" state: directory mode: '0755' - name: Create Patroni configuration file template: src: patroni.yml.j2 dest: "{{ patroni_config_dir }}/patroni.yml" mode: '0644' |
- Создаётся директория для хранения конфигурации Patroni.
- Конфигурационный файл создаётся на основе Jinja2-шаблона
patroni.yml.j2
, который содержит настройки для каждого узла.
Настройка и запуск Patroni через systemd
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
- name: Создание юнит-файла systemd для Patroni copy: dest: /etc/systemd/system/patroni.service content: | [Unit] Description=Patroni High Availability PostgreSQL After=network.target [Service] Type=simple User=postgres ExecStart=/usr/local/bin/patroni /etc/patroni/patroni.yml Restart=always LimitNOFILE=1024 [Install] WantedBy=multi-user.target - name: Перезагрузка systemd для загрузки нового юнит-файла command: systemctl daemon-reload |
- Создаётся юнит-файл для systemd, чтобы можно было управлять сервисом Patroni как обычным демоном.
- После этого производится перезагрузка демонов systemd для учёта нового сервиса.
Старт Patroni и остановка PostgreSQL
1 2 3 4 5 6 7 8 9 10 11 12 |
- name: Stop PostgreSQL service systemd: name: postgresql state: stopped enabled: yes - name: Enable and start Patroni service systemd: name: patroni enabled: yes state: restarted |
- Сначала останавливается сервис PostgreSQL, так как теперь за его управление будет отвечать Patroni.
- Patroni запускается и активируется для автозапуска при загрузке системы.
Конфигурация PgBouncer
- Устанавливается и настраивается PgBouncer — это пулер соединений для PostgreSQL:
12345- name: Установка PgBouncerapt:name: pgbouncerstate: present - Создаётся конфигурационный файл для PgBouncer:
12345678910111213141516- name: Настройка PgBouncercopy:dest: /etc/pgbouncer/pgbouncer.inicontent: |[databases]* = host=localhost port=5432[pgbouncer]listen_addr = {{ pgbouncer_listen_addr }}listen_port = {{ pgbouncer_listen_port }}auth_type = {{ pgbouncer_auth_type }}auth_file = /etc/pgbouncer/userlist.txtpool_mode = {{ pgbouncer_pool_mode }}max_client_conn = 1000default_pool_size = 20 - Этот файл определяет параметры подключения к базам данных PostgreSQL и настройки самого PgBouncer, такие как режим пула соединений, тип аутентификации и максимальное количество подключений.
- {{ pgbouncer_listen_addr }}, {{ pgbouncer_listen_port }} и другие — это переменные, которые могут быть заданы в Ansible для динамического управления параметрами PgBouncer.
- Добавляются пользователи для PgBouncer:
1 2 3 4 5 6 |
- name: Добавление пользователей PgBouncer copy: dest: /etc/pgbouncer/userlist.txt content: | "postgres" "{{ postgresql_superuser_password }}" |
- Этот файл содержит список пользователей PostgreSQL и их пароли для аутентификации через PgBouncer.
- Создаётся юнит-файл systemd для PgBouncer:
12345678910111213141516171819202122- name: Configure systemd service for PgBouncercopy:content: |[Unit]Description=PgBouncerAfter=network.target[Service]Type=simpleExecStart=/usr/sbin/pgbouncer /etc/pgbouncer/pgbouncer.iniUser=postgresGroup=postgresRestart=always[Install]WantedBy=multi-user.targetdest: /etc/systemd/system/pgbouncer.serviceowner: rootgroup: rootmode: '0644' - И наконец, перезагружается systemd и запускается PgBouncer:
12345678910- name: Reload systemd daemonsystemd:daemon_reload: yes- name: Запуск PgBouncersystemd:name: pgbouncerenabled: yesstate: restarted
Этот плейбук Ansible полностью автоматизирует процесс развертывания кластера PostgreSQL с Patroni и PgBouncer:
- Устанавливаются необходимые пакеты для PostgreSQL, Patroni и PgBouncer.
- Конфигурируются Patroni и PgBouncer, обеспечивая высокую доступность и пул соединений.
- Настраивается управление через systemd для Patroni и PgBouncer, что упрощает управление сервисами.
Развертывание Haproxy
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
--- - hosts: haproxy_nodes become: yes tasks: - name: Установка HAProxy apt: name: haproxy state: present - name: Настройка HAProxy template: src: haproxy.cfg.j2 dest: "/etc/haproxy/haproxy.cfg" mode: '0644' - name: Перезапуск HAProxy systemd: name: haproxy state: restarted |
1. Хосты и привилегии
1 2 3 |
- hosts: haproxy_nodes become: yes |
- hosts: haproxy_nodes — плейбук выполняется на всех хостах, указанных в группе haproxy_nodes в инвентаре Ansible.
- become: yes — все команды будут выполняться с правами суперпользователя (root), что необходимо для установки пакетов и изменения системных конфигураций.
2. Установка HAProxy
1 2 3 4 5 |
- name: Установка HAProxy apt: name: haproxy state: present |
- Эта задача устанавливает пакет haproxy через пакетный менеджер apt (для систем на основе Debian/Ubuntu).
- state: present — гарантирует, что HAProxy установлен на всех узлах (если он еще не установлен).
3. Настройка конфигурации HAProxy
1 2 3 4 5 6 |
- name: Настройка HAProxy template: src: haproxy.cfg.j2 dest: "/etc/haproxy/haproxy.cfg" mode: '0644' |
- Эта задача использует модуль template для копирования файла конфигурации HAProxy на каждый узел.
- src: haproxy.cfg.j2 — путь к шаблону Jinja2, который хранится в локальной директории templates Ansible. Шаблон конфигурации динамически генерируется, подставляя переменные Ansible.
- dest: «/etc/haproxy/haproxy.cfg» — целевой путь для конфигурационного файла HAProxy на узлах.
- mode: ‘0644’ — устанавливает разрешения на файл, что позволяет чтение всем пользователям, но запись только владельцу.
4. Перезапуск HAProxy
1 2 3 4 5 |
- name: Перезапуск HAProxy systemd: name: haproxy state: restarted |
- Эта задача перезапускает сервис haproxy с помощью systemd после изменения конфигурационного файла.
- state: restarted — сервис будет перезапущен, что позволяет применить новые настройки.
Заключение
Этот плейбук выполняет следующие действия:
- Устанавливает HAProxy на узлах.
- Загружает шаблон конфигурации haproxy.cfg.j2 и генерирует окончательный файл конфигурации на основе переменных.
- Перезапускает HAProxy, чтобы применить новую конфигурацию.
Такой плейбук автоматизирует развёртывание HAProxy, что полезно для настройки балансировки нагрузки в кластере.