Перейти к основному содержимому

Поддержка Citus

примечание

Эта страница переведена при помощи нейросети GigaChat.

Patroni делает развертывание кластеров Multi-Node Citus чрезвычайно простым.

TL;DR

Есть всего несколько простых правил, которым нужно следовать:

  1. Расширение базы данных Citus для PostgreSQL должно быть доступно на всех узлах. Абсолютная минимальная поддерживаемая версия Citus — 10.0, но, чтобы получить все преимущества от прозрачных переключений и перезапусков рабочих узлов, рекомендуется использовать как минимум Citus 11.2.

  2. Имя кластера (scope) должно быть одинаковым для всех узлов Citus!

  3. Учетные данные суперпользователя должны быть одинаковыми на координаторе и всех рабочих узлах, а pg_hba.conf должен разрешать доступ суперпользователя между всеми узлами.

  4. Доступ к REST API должен быть разрешен с рабочих узлов на координатор. Например, учетные данные должны быть одинаковыми, и если настроено, клиентские сертификаты с рабочих узлов должны приниматься координатором.

  5. Добавьте следующий раздел в patroni.yaml:

    citus:
    group: X # 0 для координатора и 1, 2, 3 и так далее для рабочих узлов
    database: citus # должно быть одинаковым на всех узлах

После этого потребуется просто запустить Patroni, и он позаботится об остальном:

  1. Patroni установит bootstrap.dcs.synchronous_mode в режим quorum, если он явно не установлен в другое значение.
  2. Расширение citus будет автоматически добавлено в shared_preload_libraries.
  3. Если max_prepared_transactions явно не задан в глобальной динамической конфигурации, Patroni автоматически установит его в 2*max_connections.
  4. Значение GUC citus.local_hostname будет изменено с localhost на значение, которое Patroni использует для подключения к локальному экземпляру PostgreSQL. Это значение иногда должно отличаться от localhost, потому что PostgreSQL может не прослушивать этот адрес.
  5. База данных citus.database будет автоматически создана, за ней последует выполнение CREATE EXTENSION citus.
  6. Текущие учетные данные суперпользователя будут добавлены в таблицу pg_dist_authinfo для обеспечения межузловой связи. Необходимо обновить их, если позже будет решено изменить имя пользователя, пароль, sslcert или sslkey суперпользователя!
  7. Первичный узел координатора автоматически обнаружит первичные рабочие узлы и добавит их в таблицу pg_dist_node с помощью функции citus_add_node().
  8. Patroni также будет поддерживать актуальность pg_dist_node в случае, если на кластерах координатора или рабочих узлов происходит failover/switchover.

patronictl

Кластеры координатора и рабочих узлов физически представляют собой разные кластеры PostgreSQL/Patroni, которые логически объединены с помощью расширения базы данных Citus для PostgreSQL. Поэтому в большинстве случаев управлять ими как единым целым невозможно.

Это приводит к двум основным отличиям в поведении patronictl, когда в patroni.yaml присутствует раздел citus, по сравнению с обычным случаем:

  1. Команды list и topology по умолчанию выводят всех участников формирования Citus (координаторы и рабочие узлы). Новый столбец Group указывает, к какой группе Citus они принадлежат.
  2. Для всех команд patronictl введена новая опция --group. Для некоторых команд значение группы по умолчанию может браться из patroni.yaml. Например, :ref:patronictl_pause по умолчанию включит режим обслуживания для group, заданного в разделе citus, но, например, для :ref:patronictl_switchover или :ref:patronictl_remove группа должна быть указана явно.

Пример вывода patronictl_list для кластера Citus:

postgres@coord1:~$ patronictl list demo
+ Citus cluster: demo -----------+----------------+---------+----+-------------+-----+------------+-----+
| Group | Member | Host | Role | State | TL | Receive LSN | Lag | Replay LSN | Lag |
+-------+---------+--------------+----------------+---------+----+-------------+-----+------------+-----+
| 0 | coord1 | <IP-address> | Replica | running | 1 | 0/41C0368 | 0 | 0/41C0368 | 0 |
| 0 | coord2 | <IP-address> | Quorum Standby | running | 1 | 0/41C0368 | 0 | 0/41C0368 | 0 |
| 0 | coord3 | <IP-address> | Leader | running | 1 | | | | |
| 1 | work1-1 | <IP-address> | Quorum Standby | running | 1 | 0/31D3198 | 0 | 0/31D3198 | 0 |
| 1 | work1-2 | <IP-address> | Leader | running | 1 | | | | |
| 2 | work2-1 | <IP-address> | Quorum Standby | running | 1 | 0/31CDFC0 | 0 | 0/31CDFC0 | 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 | Receive LSN | Lag | Replay LSN | Lag |
+--------+--------------+----------------+---------+----+-------------+-----+------------+-----+
| coord1 | <IP-address> | Replica | running | 1 | 0/41C0368 | 0 | 0/41C0368 | 0 |
| coord2 | <IP-address> | Quorum Standby | running | 1 | 0/41C0368 | 0 | 0/41C0368 | 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 | Receive LSN | Lag | Replay LSN | Lag |
+---------+--------------+----------------+---------+----+-------------+-----+------------+-----+
| work1-1 | <IP-address> | Quorum Standby | running | 1 | 0/31D3198 | 0 | 0/31D3198 | 0 |
| work1-2 | <IP-address> | Leader | running | 1 | | | | |
+---------+--------------+----------------+---------+----+-------------+-----+------------+-----+

Переключение (switchover) рабочего узла Citus

Когда переключение организуется для рабочего узла Citus, Citus предоставляет возможность сделать переключение практически прозрачным для приложения. Поскольку приложение подключается к координатору, который, в свою очередь, подключается к рабочим узлам, в Citus возможно приостановить SQL-трафик на координаторе для шардов, размещенных на рабочем узле. Переключение затем происходит, пока трафик удерживается на координаторе, и возобновляется, как только новый первичный рабочий узел готов принимать запросы на чтение-запись.

Пример patronictl_switchover на кластере рабочих узлов:

postgres@coord1:~$ patronictl switchover demo
+ Citus cluster: demo -----------+----------------+---------+----+-------------+-----+------------+-----+
| Group | Member | Host | Role | State | TL | Receive LSN | Lag | Replay LSN | Lag |
+-------+---------+--------------+----------------+---------+----+-------------+-----+------------+-----+
| 0 | coord1 | <IP-address> | Replica | running | 1 | 0/41C0368 | 0 | 0/41C0368 | 0 |
| 0 | coord2 | <IP-address> | Quorum Standby | running | 1 | 0/41C0368 | 0 | 0/41C0368 | 0 |
| 0 | coord3 | <IP-address> | Leader | running | 1 | | | | |
| 1 | work1-1 | <IP-address> | Leader | running | 1 | | | | |
| 1 | work1-2 | <IP-address> | Quorum Standby | running | 1 | 0/31D3198 | 0 | 0/31D3198 | 0 |
| 2 | work2-1 | <IP-address> | Quorum Standby | running | 1 | 0/31CDFC0 | 0 | 0/31CDFC0 | 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. 2024-08-26T08:02 ) [now]:
Current cluster topology
+ Citus cluster: demo (group: 2, 7179854924063375386) ---+-------------+-----+------------+-----+
| Member | Host | Role | State | TL | Receive LSN | Lag | Replay LSN | Lag |
+---------+--------------+----------------+---------+----+-------------+-----+------------+-----+
| work2-1 | <IP-address> | Quorum Standby | running | 1 | 0/31CDFC0 | 0 | 0/31CDFC0 | 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
2024-08-26 07:02:40.33003 Successfully switched over to "work2-1"
+ Citus cluster: demo (group: 2, 7179854924063375386) ----------+---------+------------+---------+
| Member | Host | Role | State | TL | Receive LSN | Lag | Replay LSN | Lag |
+---------+--------------+---------+---------+----+-------------+---------+------------+---------+
| work2-1 | <IP-address> | Leader | running | 1 | | | | |
| work2-2 | <IP-address> | Replica | stopped | | unknown | unknown | unknown | unknown |
+---------+--------------+---------+---------+----+-------------+---------+------------+---------+

postgres@coord1:~$ patronictl list demo
+ Citus cluster: demo -----------+----------------+---------+----+-------------+-----+------------+-----+
| Group | Member | Host | Role | State | TL | Receive LSN | Lag | Replay LSN | Lag |
+-------+---------+--------------+----------------+---------+----+-------------+-----+------------+-----+
| 0 | coord1 | <IP-address> | Replica | running | 1 | 0/41C0368 | 0 | 0/41C0368 | 0 |
| 0 | coord2 | <IP-address> | Quorum Standby | running | 1 | 0/41C0368 | 0 | 0/41C0368 | 0 |
| 0 | coord3 | <IP-address> | Leader | running | 1 | | | | |
| 1 | work1-1 | <IP-address> | Leader | running | 1 | | | | |
| 1 | work1-2 | <IP-address> | Quorum Standby | running | 1 | 0/31D3198 | 0 | 0/31D3198 | 0 |
| 2 | work2-1 | <IP-address> | Leader | running | 2 | | | | |
| 2 | work2-2 | <IP-address> | Quorum Standby | running | 2 | 0/31CDFC0 | 0 | 0/31CDFC0 | 0 |
+-------+---------+--------------+----------------+---------+----+-------------+-----+------------+-----+

А вот как это выглядит со стороны координатора:

# Первичный рабочий узел уведомляет координатор, что собирается выполнить "pg_ctl stop".
2024-08-26 07:02:38,636 DEBUG: query(BEGIN, ())
2024-08-26 07:02:38,636 DEBUG: query(SELECT pg_catalog.citus_update_node(%s, %s, %s, true, %s), (3, '172.19.0.7-demoted', 5432, 10000))
# С этого момента весь трафик приложения на координаторе к рабочей группе 2 приостанавливается.

# Старый первичный рабочий узел назначается как вторичный.
2024-08-26 07:02:40,084 DEBUG: query(SELECT pg_catalog.citus_update_node(%s, %s, %s, true, %s), (7, '172.19.0.7', 5432, 10000))

# Будущий первичный рабочий узел уведомляет координатор, что он захватил блокировку лидера в DCS и собирается выполнить "pg_ctl promote".
2024-08-26 07:02:40,085 DEBUG: query(SELECT pg_catalog.citus_update_node(%s, %s, %s, true, %s), (3, '172.19.0.5', 5432, 10000))

# Новый первичный рабочий узел только что завершил promote и уведомляет координатор, что готов принимать трафик на чтение-запись.
2024-08-26 07:02:41,485 DEBUG: query(COMMIT, ())
# С этого момента трафик приложения на координаторе к рабочей группе 2 разблокируется.

Вторичные узлы

Начиная с Patroni v4.0.0, вторичные узлы Citus без тега noloadbalance также регистрируются в pg_dist_node. Однако для использования вторичных узлов для запросов только на чтение приложениям необходимо изменить GUC citus.use_secondary_nodes.

Внутри DCS

Кластер Citus (координатор и рабочие узлы) хранится в DCS как флот кластеров Patroni, логически сгруппированных вместе:

/service/batman/              # scope=batman
/service/batman/0/ # citus.group=0, координатор
/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, рабочий узел
/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  # ConfigMap лидера для координатора
batman-0-config # ConfigMap, содержащий "ключи" initialize, config и history

batman-1-leader # ConfigMap лидера для рабочей группы 1
batman-1-config

То есть шаблон именования: ${scope}-${citus.group}-${type}.

Все объекты Kubernetes обнаруживаются Patroni с помощью label selector, поэтому все 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
    meta
    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 или Endpoints — он автоматически добавляет на них метку citus-group: ${env.PATRONI_CITUS_GROUP}:

apiVersion: v1
kind: ConfigMap
meta
name: citusdemo-0-leader # Генерируется как ${env.PATRONI_SCOPE}-${env.PATRONI_CITUS_GROUP}-leader
labels:
application: patroni # Устанавливается из ${env.PATRONI_KUBERNETES_LABELS}
cluster-name: citusdemo # Автоматически устанавливается из ${env.PATRONI_SCOPE}
citus-group: '0' # Автоматически устанавливается из ${env.PATRONI_CITUS_GROUP}

Полный пример развертывания Patroni в Kubernetes с поддержкой Citus можно найти в папке kubernetes репозитория Patroni.

Особое внимание следует уделить двум файлам:

  1. Dockerfile.citus
  2. citus_k8s.yaml

Обновления Citus и мажорные обновления PostgreSQL

Для обновлений Citus и мажорных обновлений PostgreSQL:

Сначала, пожалуйста, прочтите об обновлении версии Citus в документации. В процессе есть одно незначительное изменение. При выполнении обновления следует использовать patronictl_restart вместо systemctl restart для перезапуска PostgreSQL.

Мажорное обновление PostgreSQL с Citus немного сложнее. Потребуется объединить техники, используемые в документации Citus о мажорных обновлениях, и документацию Patroni о мажорном обновлении PostgreSQL. Пожалуйста, имейте в виду, что кластер Citus состоит из множества кластеров Patroni (координатор и рабочие узлы), и все они должны быть обновлены независимо.