Использование Patroni для создания кластера высокой доступности Postgres — Часть 3: HAProx
Автор: Shaun Thomas, Using Patroni to Build a Highly Available Postgres Cluster—Part 3: HAProxy
Статьи серии:
- Как Patroni обеспечивает высокую доступность PostgreSQL
- Использование Patroni для создания кластера высокой доступности Postgres — Часть 1: etcd
- Использование Patroni для создания кластера высокой доступности Postgres — Часть 2: Postgres и Patroni
- Использование Patroni для создания кластера высокой доступности Postgres — Часть 3: HAProx
Добро пожаловать в третью часть нашей серии по созданию высокодоступного кластера Postgres с помощью Patroni! Часть первая была полностью посвящена созданию DCS с использованием etcd для обеспечения критически важного уровня DCS для кластера, а часть вторая добавила Patroni и Postgres в программный стек. Хотя на этом этапе можно остановиться и использовать кластер как есть, есть ещё один компонент, который сделает его гораздо более функциональным в целом.
Новым соединениям нужен способ легко и надёжно достигать основного узла. Patroni предоставляет REST-интерфейс для опроса каждого узла о его состоянии, что делает его идеальным решением для любого программного обеспечения или уровня балансировки нагрузки, совместимого с HTTP-проверками. Часть третья посвящена добавлению HAProxy для выполнения этой роли, завершая кластер уровнем маршрутизации.
Надеюсь, у вас всё ещё есть три виртуальные машины, на которых вы установили etcd, Postgres и Patroni. Они нам понадобятся для финального этапа, так что если вы ещё не прошли шаги из частей первой и второй, вернитесь, когда будете готовы.
В противном случае, давайте завершим кластер!
Что добавляет HAProxy
HAProxy — один из самых распространённых HTTP-прокси, но у него также есть скрытая суперспособность: он может прозрачно перенаправлять и сырые TCP-соединения. Это означает, что он также может выступать в качестве прокси для любого вида сервисов, таких как Postgres. Вот как это работает:
- HAProxy подключается к REST-интерфейсу Patroni и получает статус по URL "/".
- Patroni будет отвечать статусом "200 OK" только на основном узле (primary). Все остальные узлы будут выдавать ошибку "500" того или иного рода.
- HAProxy помечает узлы, отвечающие ошибками, как нездоровые.
- Все соединения направляются на единственный "здоровый" узел: основной узел кластера.
Конечно, это ещё не всё; REST API Patroni невероятно мощный и предоставляет несколько дополнительных конечных точек. Например, проверка:
/replicaзавершится успехом, если узел является здоровой потоковой репликой основного узла — хороший вариант для разгрузки интенсивных запросов на чтение от основного узла./read-onlyработает на любом здоровом узле кластера — идеально для соединений, которым не важно, как они взаимодействуют с базой данных./synchronousзавершается успехом только на здоровых синхронных потоковых репликах для операций, где важна надёжность чтения.
Существует также HTTP-параметр lag, который ограничивает успех на репликах указанным максимумом. Хотите направлять трафик только на реплики с задержкой репликации менее 1 МБ? Просто добавьте этот параметр к HTTP-проверке в HAProxy. Это позволяет создавать несколько определений прокси для каждой специализированной потребности, и HAProxy автоматически поддерживает всё на основе кодов состояния Patroni — никакого ручного вмешательства не требуется.
Установка HAProxy
К счастью, установить HAProxy довольно просто, потому что он широко распространён. Он должен быть доступен в стандартных репозиториях каждой крупной Linux-платформы без каких-либо специальных шагов. В случае Debian достаточно одной команды:
sudo apt install -y haproxyСоздание полезной конфигурации HAProxy
В отличие от Patroni, настройка HAProxy — гораздо более простое дело. Файл конфигурации по умолчанию haproxy.cfg должен уже существовать в каталоге /etc/haproxy после установки. Поскольку значения по умолчанию полностью зависят от версии HAProxy и целевого дистрибутива, давайте заменим его на то, что должно работать везде и подходить для создаваемого кластера Patroni.
Начнём с преамбулы всех настроек сервиса по умолчанию. Существуют глобальные настройки, применяемые ко всем определённым слушателям, и значения по умолчанию для стандартной работы. Например:
global
maxconn 100
defaults
log global
mode tcp
retries 2
timeout client 30m
timeout connect 4s
timeout server 30m
timeout check 5sВ данном случае HAProxy перестанет разрешать соединения после 100 — идеальное количество для демо-кластера, но, возможно, вы захотите увеличить его в производственной системе.
Остальные параметры определяют вывод журнала, устанавливают тип соединения как TCP, а не HTTP, гарантируют повторение проверок дважды для предотвращения ложных срабатываний и устанавливают несколько базовых тайм-аутов, чтобы избежать устаревших соединений или состояний сервера.
Следующий шаг — определить блок listen. Это фактически привязывает порт к группе серверов и определяет проверку работоспособности (health check) против REST API Patroni. Основываясь на виртуальных машинах, которые мы создали до сих пор, это должно выглядеть примерно так:
listen pg-cluster
bind *:6543
mode tcp
option httpchk
http-check expect status 200
default-server inter 3s fall 3 rise 2 on-marked-down shutdown-sessions
server postgres1 pg1:5432 check port 8008
server postgres2 pg2:5432 check port 8008
server postgres3 pg3:5432 check port 8008Порт службы Postgres 5432 уже используется на созданных нами виртуальных машинах, поэтому прокси должен использовать другой порт. Вероятно, не обязательно явно устанавливать режим TCP снова в блоке listen, но это хорошая практика на всякий случай. Проверки могут быть как TCP, так и HTTP, и несмотря на то, что соединения должны быть TCP по своей природе, сама проверка работоспособности является HTTP благодаря удобному REST-интерфейсу Patroni. И, конечно, мы хотим считать успешным только статус 200.
Строка default-server имеет здесь особое значение. В переводе она означает:
- Выполнять проверку работоспособности каждые три секунды.
- Требовать три неудачных проверки, прежде чем пометить хост как недоступный.
- Считать нерабочий хост здоровым после двух успешных проверок.
- Когда узел помечен как недоступный, разорвать все установленные сеансы.
Это необходимо, потому что у HAProxy много режимов работы, многие из которых являются разрешительными или оптимистичными. Тот факт, что новые соединения не должны направляться на определённый сервер, не означает, что старые соединения внезапно становятся недействительными. Однако в случае с Patroni это означает именно это!
Если Patroni выходит из строя, REST-интерфейс также исчезает, и HAProxy интерпретирует это как отказ узла. Но в случае, если Patroni выходит из строя до того, как успеет правильно остановить Postgres, Postgres останется в сети, возможно, принимая записи в случае основного узла. Нам нужно завершить все соединения на всякий случай, чтобы предотвратить сценарий "выноса мозга" (split-brain).
Это одна из ситуаций, когда простое использование параметров протокола подключения Postgres, таких как target_session_attrs, обнаруживает скрытую слабость. Этот параметр гарантирует только то, что соединения достигают узла, доступного для записи, когда установлено значение read-write, но не гарантирует, что только один узел в указанном списке хостов доступен для записи! Если два узла повышаются до статуса read-write, это просто неудача, и последующая очистка ложится на вас.
Каждая мера предосторожности имеет значение, и поэтому самый безопасный способ действий — немедленно завершать соединения с хостами, которые не прошли три последовательные проверки работоспособности.
Следующий блок состоит просто из одной строки для каждого сервера, включая порт, на который направлять соединения, и сам порт проверки. Теперь соединения с портом 6543 на этом сервере будут перенаправляться на порт 5432 того узла, который является текущим основным. Запуская HAProxy на каждой виртуальной машине, соединения с любым узлом будут направляться на основной узел без какой-либо дополнительной конфигурации.
Запуск и тестирование HAProxy
После завершения файла конфигурации запустите HAProxy, чтобы активировать новый уровень маршрутизации:
sudo systemctl enable haproxy
sudo systemctl start haproxyВот и всё. Но мы также хотим протестировать прокси, чтобы убедиться, что он работает должным образом. Самый простой способ сделать это — подключиться с любого узла, кроме основного, а затем проверить, куда ушло соединение.
Используйте patronictl, чтобы найти текущий основной узел:
patronictl -c /etc/patroni/18-demo.yml list+ Cluster: 18-demo (7606465692216410488) -+-----------+----+-------------+-----+------------+-----+
| Member | Host | Role | State | TL | Receive LSN | Lag | Replay LSN | Lag |
+----------------+--------------+---------+-----------+----+-------------+-----+------------+-----+
| patroni-demo-1 | 192.168.6.10 | Leader | running | 2 | | | | |
| patroni-demo-2 | 192.168.6.11 | Replica | streaming | 2 | 0/4001C38 | 0 | 0/4001C38 | 0 |
| patroni-demo-3 | 192.168.6.12 | Replica | streaming | 2 | 0/4001C38 | 0 | 0/4001C38 | 0 |
+----------------+--------------+---------+-----------+----+-------------+-----+------------+-----+Этот вывод показывает, что узел 1 всё ещё является основным в кластере. В этом случае подключитесь с узла 2 или 3 через порт прокси и проверьте IP-адрес сервера:
psql -h pg3 -p 6543 -U postgres -c "SELECT inet_server_addr();" postgres
inet_server_addr
------------------
192.168.6.10Это определённо узел 1. Успех!
Добавление конечных точек
Помните, как мы упоминали возможность добавления дополнительных конечных точек? Вот пример, где соединения будут направляться только на реплики с задержкой репликации менее 1 МБ:
listen pg-cluster-ro
bind *:6544
mode tcp
option httpchk /replica?lag=1MB
http-check expect status 200
default-server inter 3s fall 3 rise 2 on-marked-down shutdown-sessions
server postgres1 pg1:5432 check port 8008
server postgres2 pg2:5432 check port 8008
server postgres3 pg3:5432 check port 8008После перезапуска HAProxy он также будет слушать порт 6544 и направлять соединения только на одну из реплик. Эти серверы простаивают, поэтому задержка должна отсутствовать, что позволяет нам протестировать соединение таким образом:
psql -h pg1 -p 6544 -U postgres -c "SELECT inet_server_addr();" postgres
inet_server_addr
------------------
192.168.6.11Выполняйте эту команду сколько угодно раз, она никогда не выполнится на узле 1. Как вам такое удобство?
Завершение
Мы завершили создание так называемого «сэндвича из HAProxy и DCS» для кластера Postgres, ставшего возможным благодаря Patroni. Вот он во всей красе:

И HAProxy, и etcd (DCS) работают на тех же узлах, что и Postgres и Patroni, для целей этой демонстрации, но это вряд ли стандартная конфигурация. Мы сделали это только для упрощения примера и минимального количества виртуальных машин. Более типичный кластер, скорее всего, отделяет уровень HAProxy на отдельную систему, позволяя ему действовать как выделенная конечная точка. Обычно проще подключаться к "postgres-proxy.company.net", чем произвольно назначать виртуальные машины Postgres различным приложениям или использовать строки подключения с несколькими хостами. Размещение HAProxy на собственном хосте также позволяет ему вернуться к стандартному порту Postgres 5432 и просто маскироваться под Postgres.
Ещё один интересный вариант включает запуск HAProxy на уровне самого приложения. Приложения подключаются к локальному серверу или группе серверов и прозрачно направляют трафик к текущему основному узлу Postgres на основе проверок HAProxy.
DCS (в нашем случае etcd) также, вероятно, будет существовать на отдельном наборе хостов. Это позволяет нескольким кластерам Patroni использовать один и тот же уровень консенсуса, а наличие отдельного слоя DCS делает двухузловые кластеры Postgres жизнеспособным решением. Для кластеров, которым нужно экономить ресурсы хранения или вычислений, сокращение избыточных развёртываний Postgres — отличный способ. Более крупные или устоявшиеся организации могут уже иметь существующую систему консенсуса (такую как ZooKeeper или Consul), поэтому имеет смысл повторно использовать эти ресурсы.
Независимо от того, на какую модель развёртывания вы похожи, Patroni выступает в роли связующего звена, объединяющего всё: все узлы Postgres, DCS и систему маршрутизации. Всё работает как единый согласованный кластер, возможно, несмотря на то, как каждый компонент может вести себя по отдельности. В итоге вы получите лучшее решение высокой доступности для Postgres, не считая полноценного решения на Kubernetes, но в гораздо более простом пакете.
Trackbacks
The author does not allow comments to this entry
Comments
Display comments as Linear | Threaded