Поддержка Citus
Эта страница переведена при помощи нейросети GigaChat.
Patroni чрезвычайно упрощает развертывание многоузловых кластеров Citus.
TL;DR
Есть всего несколько простых правил, которым необходимо следовать:
-
Расширение базы данных Citus для PostgreSQL должно быть доступно на всех узлах. Минимальная поддерживаемая версия Citus составляет 10.0, но чтобы воспользоваться всеми преимуществами прозрачных переключений и перезапусков рабочих процессов, рекомендуется использовать хотя бы Citus 11.2.
-
Имя кластера (
scope
) должно быть одинаковым для всех узлов Citus! -
Учетные данные суперпользователя должны быть одинаковыми на координаторе и всех рабочих узлах, а
pg_hba.conf
должен разрешать доступ суперпользователю между всеми узлами. -
Доступ к REST API должен быть разрешен от рабочих узлов до координатора. Например, учетные данные должны быть одинаковыми и если настроены, клиентские сертификаты с рабочих узлов должны приниматься координатором.
-
Добавьте следующий раздел в
patroni.yaml
:citus:
group: X # 0 for coordinator and 1, 2, 3, etc for workers
database: citus # must be the same on all nodes
После этого нужно запустить Patroni, и он будет обрабатывать остальное:
- Расширение
citus
будет автоматически добавлено вshared_preload_libraries
. - Если
max_prepared_transactions
явно не задано в глобальной динамической конфигурации, Patroni автоматически установит его значение равным2*max_connections
. - Значение GUC
citus.local_hostname
будет скорректировано сlocalhost
до значения, которое Patroni использует для подключения к локальному экземпляру PostgreSQL. Иногда это значение должно отличаться отlocalhost
, потому что PostgreSQL может не прослушивать его. citus.database
будет автоматически создан послеCREATE EXTENSION citus
.- Текущие учетные данные суперпользователя будут добавлены в таблицу
pg_dist_authinfo
, чтобы разрешить межузловое общение. Не забудьте обновить их, если позже решите изменить имя пользователя/пароль/SSL-сертификат/SSL-ключ суперпользователя! - Координатор первичного узла автоматически обнаружит рабочие первичные узлы и добавит их в таблицу с помощью функции
pg_dist_node
. - Patroni также будет поддерживать
pg_dist_node
, если произойдет отказ или переключение на координационном узле или кластере рабочих узлов.
patronictl
Кластеры координаторов и рабочих узлов представляют собой физически разные кластеры PostgreSQL/Patroni, которые просто логически объединены с использованием расширения базы данных Citus для PostgreSQL. Поэтому в большинстве случаев невозможно управлять ими как единым целым.
Это приводит к двум основным различиям в поведении patronictl при наличии раздела patroni.yaml
по сравнению с обычным:
- По умолчанию
list
иtopology
выводят все члены группы Citus (координаторы и рабочие). Новая колонкаGroup
указывает, к какой группе Citus они принадлежат. - Для всех команд
patronictl
вводится новый параметр, называемый--group
. Для некоторых команд значение по умолчанию для группы может быть взято изpatroni.yaml
. Например, patronictl pause по умолчанию включит режим обслуживания дляgroup
, который установлен в разделеcitus
, но, например, для patronictl switchover или patronictl remove группа должна быть указана явно.
Пример вывода команды patronictl list для кластера Citus:
postgres@coord1:~$ patronictl list demo
+ Citus cluster: demo ----------+--------------+---------+----+-----------+
| Group | Member | Host | Role | State | TL | Lag in MB |
+-------+---------+-------------+--------------+---------+----+-----------+
| 0 | coord1 | {IP-Address} | Replica | running | 1 | 0 |
| 0 | coord2 | {IP-Address} | Sync Standby | running | 1 | 0 |
| 0 | coord3 | {IP-Address} | Leader | running | 1 | |
| 1 | work1-1 | {IP-Address} | Sync Standby | running | 1 | 0 |
| 1 | work1-2 | {IP-Address} | Leader | running | 1 | |
| 2 | work2-1 | {IP-Address} | Sync Standby | running | 1 | 0 |
| 2 | work2-2 | {IP-Address} | Leader | running | 1 | |
+-------+---------+-------------+--------------+---------+----+-----------+
Если добавить опцию --group
, то вывод изменится на:
postgres@coord1:~$ patronictl list demo --group 0
+ Citus cluster: demo (group: 0, 7179854923829112860) -----------+
| Member | Host | Role | State | TL | Lag in MB |
+--------+-------------+--------------+---------+----+-----------+
| coord1 | {IP-Address} | Replica | running | 1 | 0 |
| coord2 | {IP-Address} | Sync Standby | running | 1 | 0 |
| coord3 | {IP-Address} | Leader | running | 1 | |
+--------+-------------+--------------+---------+----+-----------+
postgres@coord1:~$ patronictl list demo --group 1
+ Citus cluster: demo (group: 1, 7179854923881963547) -----------+
| Member | Host | Role | State | TL | Lag in MB |
+---------+------------+--------------+---------+----+-----------+
| work1-1 | {IP-Address} | Sync Standby | running | 1 | 0 |
| work1-2 | {IP-Address} | Leader | running | 1 | |
+---------+------------+--------------+---------+----+-----------+
Переключение рабочих процессов Citus
Когда для рабочего узла Citus организуется переключение, Citus предоставляет возможность сделать это переключение почти незаметным для приложения. Поскольку приложение подключается к координатору, который, в свою очередь, подключается к рабочим узлам, то с помощью Citus можно приостановить трафик SQL на координаторе для фрагментов, размещенных на рабочем узле. Затем происходит переключение при сохранении трафика на координаторе и возобновляется сразу же после того, как новый основной рабочий узел готов принимать запросы чтения-записи.
Пример patronictl switchover в кластере рабочих узлов:
postgres@coord1:~$ patronictl switchover demo
+ Citus cluster: demo ----------+--------------+---------+----+-----------+
| Group | Member | Host | Role | State | TL | Lag in MB |
+-------+---------+-------------+--------------+---------+----+-----------+
| 0 | coord1 | {IP-Address} | Replica | running | 1 | 0 |
| 0 | coord2 | {IP-Address} | Sync Standby | running | 1 | 0 |
| 0 | coord3 | {IP-Address} | Leader | running | 1 | |
| 1 | work1-1 | {IP-Address} | Leader | running | 1 | |
| 1 | work1-2 | {IP-Address} | Sync Standby | running | 1 | 0 |
| 2 | work2-1 | {IP-Address} | Sync Standby | running | 1 | 0 |
| 2 | work2-2 | {IP-Address} | Leader | running | 1 | |
+-------+---------+-------------+--------------+---------+----+-----------+
Citus group: 2
Primary [work2-2]:
Candidate ['work2-1'] []:
When should the switchover take place (e.g. 2022-12-22T08:02 ) [now]:
Current cluster topology
+ Citus cluster: demo (group: 2, 7179854924063375386) -----------+
| Member | Host | Role | State | TL | Lag in MB |
+---------+------------+--------------+---------+----+-----------+
| work2-1 | {IP-Address} | Sync Standby | running | 1 | 0 |
| work2-2 | {IP-Address} | Leader | running | 1 | |
+---------+------------+--------------+---------+----+-----------+
Are you sure you want to switchover cluster demo, demoting current primary work2-2? [y/N]: y
2022-12-22 07:02:40.33003 Successfully switched over to "work2-1"
+ Citus cluster: demo (group: 2, 7179854924063375386) ------+
| Member | Host | Role | State | TL | Lag in MB |
+---------+------------+---------+---------+----+-----------+
| work2-1 | {IP-Address} | Leader | running | 1 | |
| work2-2 | {IP-Address} | Replica | stopped | | unknown |
+---------+------------+---------+---------+----+-----------+
postgres@coord1:~$ patronictl list demo
+ Citus cluster: demo ----------+--------------+---------+----+-----------+
| Group | Member | Host | Role | State | TL | Lag in MB |
+-------+---------+-------------+--------------+---------+----+-----------+
| 0 | coord1 | {IP-Address} | Replica | running | 1 | 0 |
| 0 | coord2 | {IP-Address} | Sync Standby | running | 1 | 0 |
| 0 | coord3 | {IP-Address} | Leader | running | 1 | |
| 1 | work1-1 | {IP-Address} | Leader | running | 1 | |
| 1 | work1-2 | {IP-Address} | Sync Standby | running | 1 | 0 |
| 2 | work2-1 | {IP-Address} | Leader | running | 2 | |
| 2 | work2-2 | {IP-Address} | Sync Standby | running | 2 | 0 |
+-------+---------+-------------+--------------+---------+----+-----------+
И вот как это выглядит со стороны координатора:
# The worker primary notifies the coordinator that it is going to execute "pg_ctl stop".
2022-12-22 07:02:38,636 DEBUG: query("BEGIN")
2022-12-22 07:02:38,636 DEBUG: query("SELECT pg_catalog.citus_update_node(3, '{IP-Address}-demoted', 5432, true, 10000)")
# From this moment all application traffic on the coordinator to the worker group 2 is paused.
# The future worker primary notifies the coordinator that it acquired the leader lock in DCS and about to run "pg_ctl promote".
2022-12-22 07:02:40,085 DEBUG: query("SELECT pg_catalog.citus_update_node(3, '{IP-Address}', 5432)")
# The new worker primary just finished promote and notifies coordinator that it is ready to accept read-write traffic.
2022-12-22 07:02:41,485 DEBUG: query("COMMIT")
# From this moment the application traffic on the coordinator to the worker group 2 is unblocked.
Внутри DCS
Кластер Citus (координатор и рабочие узлы) хранится в DCS как парк кластеров Patroni, логически объединенных вместе:
/service/batman/ # scope=batman
/service/batman/0/ # citus.group=0, coordinator
/service/batman/0/initialize
/service/batman/0/leader
/service/batman/0/members/
/service/batman/0/members/m1
/service/batman/0/members/m2
/service/batman/1/ # citus.group=1, worker
/service/batman/1/initialize
/service/batman/1/leader
/service/batman/1/members/
/service/batman/1/members/m3
/service/batman/1/members/m4
...
Такой подход был выбран потому, что для большинства DCS становится возможным получить весь кластер Citus с помощью одного рекурсивного запроса на чтение. Только узлы-координаторы Citus читают все дерево, поскольку они должны обнаруживать рабочие узлы. Рабочие узлы читают только поддерево для своей собственной группы, а в некоторых случаях они могут читать поддерево группы координаторов.
Citus на Kubernetes
Поскольку Kubernetes не поддерживает иерархические структуры, пришлось включить группу citus во все объекты K8s, которые создает Patroni:
batman-0-leader # the leader config map for the coordinator
batman-0-config # the config map holding initialize, config, and history "keys"
...
batman-1-leader # the leader config map for worker group 1
batman-1-config
...
То есть шаблон именования следующий: ${scope}-${citus.group}-${type}
.
Все объекты Kubernetes обнаруживаются Patroni с помощью селектора меток, поэтому все Pod с Patroni/Citus и Endpoints/ConfigMaps должны иметь аналогичные метки, а Patroni должен быть настроен на их использование с помощью настроек Kubernetes или переменных окружения.
Несколько примеров настройки Patroni с использованием переменных среды Pod:
-
Для кластера координатора:
apiVersion: v1
kind: Pod
metadata:
labels:
application: patroni
citus-group: "0"
citus-type: coordinator
cluster-name: citusdemo
name: citusdemo-0-0
namespace: default
spec:
containers:
- env:
- name: PATRONI_SCOPE
value: citusdemo
- name: PATRONI_NAME
valueFrom:
fieldRef:
apiVersion: v1
fieldPath: metadata.name
- name: PATRONI_KUBERNETES_POD_IP
valueFrom:
fieldRef:
apiVersion: v1
fieldPath: status.podIP
- name: PATRONI_KUBERNETES_NAMESPACE
valueFrom:
fieldRef:
apiVersion: v1
fieldPath: metadata.namespace
- name: PATRONI_KUBERNETES_LABELS
value: '{application: patroni}'
- name: PATRONI_CITUS_DATABASE
value: citus
- name: PATRONI_CITUS_GROUP
value: "0" -
Для рабочего кластера из группы 2:
apiVersion: v1
kind: Pod
metadata:
labels:
application: patroni
citus-group: "2"
citus-type: worker
cluster-name: citusdemo
name: citusdemo-2-0
namespace: default
spec:
containers:
- env:
- name: PATRONI_SCOPE
value: citusdemo
- name: PATRONI_NAME
valueFrom:
fieldRef:
apiVersion: v1
fieldPath: metadata.name
- name: PATRONI_KUBERNETES_POD_IP
valueFrom:
fieldRef:
apiVersion: v1
fieldPath: status.podIP
- name: PATRONI_KUBERNETES_NAMESPACE
valueFrom:
fieldRef:
apiVersion: v1
fieldPath: metadata.namespace
- name: PATRONI_KUBERNETES_LABELS
value: '{application: patroni}'
- name: PATRONI_CITUS_DATABASE
value: citus
- name: PATRONI_CITUS_GROUP
value: "2"
Как можно заметить, оба примера имеют установленную метку citus-group
. Эта метка позволяет Patroni идентифицировать объект как принадлежащий к определенной группе Citus. В дополнение к этому, существует также переменная окружения PATRONI_CITUS_GROUP
, которая имеет то же значение, что и метка citus-group
. Когда Patroni создает новые объекты Kubernetes ConfigMaps или конечные точки, он автоматически помещает на них метку citus-group: ${env.PATRONI_CITUS_GROUP}
:
apiVersion: v1
kind: ConfigMap
metadata:
name: citusdemo-0-leader # Is generated as ${env.PATRONI_SCOPE}-${env.PATRONI_CITUS_GROUP}-leader
labels:
application: patroni # Is set from the ${env.PATRONI_KUBERNETES_LABELS}
cluster-name: citusdemo # Is automatically set from the ${env.PATRONI_SCOPE}
citus-group: '0' # Is automatically set from the ${env.PATRONI_CITUS_GROUP}
Полный пример развертывания Patroni на Kubernetes с поддержкой Citus находится в папке Kubernetes репозитория Patroni. Там есть два важных файла:
Dockerfile.citus
;citus_k8s.yaml
.
Обновления Citus и мажорные обновления PostgreSQL
Сначала прочитайте об обновлении версии Citus в документации. Есть одно незначительное изменение в процессе. При выполнении обновления необходимо использовать patronictl restart вместо systemctl restart
для перезагрузки PostgreSQL.
Основное обновление PostgreSQL с Citus немного сложнее. Придется комбинировать методы, используемые в документации Citus по основным обновлениям и документации Patroni по основному обновлению PostgreSQL. Имейте в виду, что кластер Citus состоит из множества кластеров Patroni (координатор и рабочие), и все они должны быть обновлены независимо.