Перейти к содержанию

Поддержка Citus

Сведения об интеграции Patroni для координатора и рабочих групп Citus.

Patroni делает развёртывание кластеров многоузлового Citus чрезвычайно простым.


Кратко

Существует лишь несколько простых правил, которым необходимо следовать:

  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 for coordinator and 1, 2, 3, etc for workers
  database: citus  # must be the same on all nodes

После этого вам нужно просто запустить Patroni, и он сам займётся остальным:

  1. Patroni установит bootstrap.dcs.synchronous_mode в quorum , если этому параметру явно не задано другое значение.
  2. Расширение citus будет автоматически добавлено в shared_preload_libraries.
  3. Если max_prepared_transactions не задано явно в глобальной динамической конфигурации , Patroni автоматически установит его в 2*max_connections.
  4. Значение citus.local_hostname GUC будет скорректировано с 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 в случае переключения при отказе/планового переключения в координирующем или рабочем кластерах.

patronictl

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

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

  1. Команды list и topology по умолчанию выводят всех участников кластера Citus (координаторов и рабочих узлов). Новая колонка Group указывает, к какой группе Citus они принадлежат.
  2. Для всех команд 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 | Receive LSN | Lag | Replay LSN | Lag |
+-------+---------+-------------+----------------+---------+----+-------------+-----+------------+-----+
|     0 | coord1  | 172.27.0.10 | Replica        | running |  1 |   0/41C0368 |   0 |  0/41C0368 |   0 |
|     0 | coord2  | 172.27.0.6  | Quorum Standby | running |  1 |   0/41C0368 |   0 |  0/41C0368 |   0 |
|     0 | coord3  | 172.27.0.4  | Leader         | running |  1 |             |     |            |     |
|     1 | work1-1 | 172.27.0.8  | Quorum Standby | running |  1 |   0/31D3198 |   0 |  0/31D3198 |   0 |
|     1 | work1-2 | 172.27.0.2  | Leader         | running |  1 |             |     |            |     |
|     2 | work2-1 | 172.27.0.5  | Quorum Standby | running |  1 |   0/31CDFC0 |   0 |  0/31CDFC0 |   0 |
|     2 | work2-2 | 172.27.0.7  | 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 | 172.27.0.10 | Replica        | running |  1 |   0/41C0368 |   0 |  0/41C0368 |   0 |
| coord2 | 172.27.0.6  | Quorum Standby | running |  1 |   0/41C0368 |   0 |  0/41C0368 |   0 |
| coord3 | 172.27.0.4  | 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 | 172.27.0.8 | Quorum Standby | running |  1 |   0/31D3198 |   0 |  0/31D3198 |   0 |
| work1-2 | 172.27.0.2 | Leader         | running |  1 |             |     |            |     |
+---------+------------+----------------+---------+----+-------------+-----+------------+-----+

Плановое переключение рабочего узла 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  | 172.27.0.10 | Replica        | running |  1 |   0/41C0368 |   0 |  0/41C0368 |   0 |
|     0 | coord2  | 172.27.0.6  | Quorum Standby | running |  1 |   0/41C0368 |   0 |  0/41C0368 |   0 |
|     0 | coord3  | 172.27.0.4  | Leader         | running |  1 |             |     |            |     |
|     1 | work1-1 | 172.27.0.8  | Leader         | running |  1 |             |     |            |     |
|     1 | work1-2 | 172.27.0.2  | Quorum Standby | running |  1 |   0/31D3198 |   0 |  0/31D3198 |   0 |
|     2 | work2-1 | 172.27.0.5  | Quorum Standby | running |  1 |   0/31CDFC0 |   0 |  0/31CDFC0 |   0 |
|     2 | work2-2 | 172.27.0.7  | 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 | 172.27.0.5 | Quorum Standby | running |  1 |   0/31CDFC0 |   0 |  0/31CDFC0 |   0 |
| work2-2 | 172.27.0.7 | 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 | 172.27.0.5 | Leader  | running |  1 |             |         |            |         |
| work2-2 | 172.27.0.7 | 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  | 172.27.0.10 | Replica        | running |  1 |   0/41C0368 |   0 |  0/41C0368 |   0 |
|     0 | coord2  | 172.27.0.6  | Quorum Standby | running |  1 |   0/41C0368 |   0 |  0/41C0368 |   0 |
|     0 | coord3  | 172.27.0.4  | Leader         | running |  1 |             |     |            |     |
|     1 | work1-1 | 172.27.0.8  | Leader         | running |  1 |             |     |            |     |
|     1 | work1-2 | 172.27.0.2  | Quorum Standby | running |  1 |   0/31D3198 |   0 |  0/31D3198 |   0 |
|     2 | work2-1 | 172.27.0.5  | Leader         | running |  2 |             |     |            |     |
|     2 | work2-2 | 172.27.0.7  | Quorum Standby | running |  2 |   0/31CDFC0 |   0 |  0/31CDFC0 |   0 |
+-------+---------+-------------+----------------+---------+----+-------------+-----+------------+-----+

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

# The worker primary notifies the coordinator that it is going to execute "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))
# From this moment all application traffic on the coordinator to the worker group 2 is paused.

# The old worker primary is assigned as a secondary.
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))

# The future worker primary notifies the coordinator that it acquired the leader lock in DCS and about to run "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))

# The new worker primary just finished promote and notifies coordinator that it is ready to accept read-write traffic.
2024-08-26 07:02:41,485 DEBUG: query(COMMIT, ())
# From this moment the application traffic on the coordinator to the worker group 2 is unblocked.

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

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


Загляните в 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}.

Patroni обнаруживает все объекты Kubernetes с помощью селектора меток , поэтому все Pods с Patroni&Citus, а также Endpoints/ConfigMaps должны иметь метки, соответствующие общему селектору, а Patroni необходимо настроить на их использование через параметры Kubernetes или переменные окружения .

Несколько примеров конфигурации Patroni с использованием переменных среды Pod:

  1. для кластера-координатора
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"
  1. для рабочего кластера из группы 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. При создании новых объектов Kubernetes — ConfigMaps или Endpoints — Patroni автоматически добавляет им метку 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.

Для вас существуют два важных файла:

  1. Dockerfile.citus
  2. citus_k8s.yaml

Обновление Citus и обновление основной версии PostgreSQL

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

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