--- title: "Citus 支持" weight: 130 icon: fas fa-lemon description: "Patroni 与 Citus 协调节点及工作节点组的集成详情。" module: [PATRONI] categories: [概念] aliases: [/patroni/citus/] upstream_link: "https://patroni.readthedocs.io/en/latest/citus.html" --- Patroni 使部署 [多节点 Citus](https://docs.citusdata.com/en/stable/installation/multi_node.html) 集群变得极为简单。 -------- ## TL;DR 只需遵循以下几条简单规则: 1. 所有节点上必须安装 [Citus](https://github.com/citusdata/citus) 数据库扩展。最低支持的 Citus 版本为 10.0,但为了充分利用工作节点透明主从切换和重启的特性,建议使用至少 Citus 11.2。 2. 集群名称(**`scope`**)在所有 Citus 节点上必须相同! 3. 超级用户凭据在协调节点和所有工作节点上必须相同,且 **`pg_hba.conf`** 应允许所有节点之间的超级用户访问。 4. 工作节点到协调节点的 [**REST API**](/zh/docs/patroni/config/yaml#restapi_settings) 访问必须被允许。例如,凭据应相同,如果配置了客户端证书,工作节点的客户端证书必须被协调节点接受。 5. 在 **`patroni.yaml`** 中添加以下配置节: ```yaml citus: group: X # 0 表示协调节点,工作节点使用 1、2、3 等 database: citus # 所有节点上必须相同 ``` 之后只需启动 Patroni,它会处理其余的一切: 0. Patroni 会将 **`bootstrap.dcs.synchronous_mode`** 设置为 [**法定人数模式**](/zh/docs/patroni/replication_modes#quorum_mode),除非已显式设置为其他值。 1. [`citus`](/zh/docs/patroni/citus#citus) 扩展将自动添加到 **`shared_preload_libraries`**。 2. 如果全局 [**动态配置**](/zh/docs/patroni/config/dynamic#dynamic) 中未显式设置 **`max_prepared_transactions`**,Patroni 将自动将其设置为 **`2*max_connections`**。 3. **`citus.local_hostname`** GUC 的值将从 **`localhost`** 调整为 Patroni 用于连接本地 PostgreSQL 实例的值。有时该值与 **`localhost`** 不同,因为 PostgreSQL 可能没有在该地址上监听。 4. **`citus.database`** 指定的数据库将被自动创建,随后执行 **`CREATE EXTENSION citus`**。 5. 当前超级用户 [**凭据**](/zh/docs/patroni/config/yaml#postgresql_settings) 将被添加到 **`pg_dist_authinfo`** 表中,以允许跨节点通信。如果之后决定修改超级用户的用户名/密码/sslcert/sslkey,别忘了同步更新此表! 6. 协调节点主库将自动发现工作节点主库,并使用 **`citus_add_node()`** 函数将其添加到 **`pg_dist_node`** 表中。 7. Patroni 还会在协调节点或工作节点集群发生故障转移/主从切换时,自动维护 **`pg_dist_node`**。 -------- ## patronictl 协调节点和工作节点集群是物理上独立的 PostgreSQL/Patroni 集群,仅通过 [Citus](https://github.com/citusdata/citus) 数据库扩展在逻辑上组合在一起。因此,在大多数情况下无法将它们作为单一实体进行管理。 当 **`patroni.yaml`** 中包含 [`citus`](/zh/docs/patroni/citus#citus) 配置节时,与通常情况相比,[`patronictl`](/zh/docs/patroni/patronictl#patronictl) 的行为存在两个主要差异: 1. **`list`** 和 **`topology`** 命令默认输出 Citus formation 的所有成员(协调节点和工作节点),新增的 **`Group`** 列表明它们所属的 Citus 组。 2. 所有 [`patronictl`](/zh/docs/patroni/patronictl#patronictl) 命令都引入了新选项 **`--group`**。对于某些命令,组的默认值可以从 **`patroni.yaml`** 中获取。例如,[`patronictl pause`](/zh/docs/patroni/patronictl#patronictl_pause) 默认会为 [`citus`](/zh/docs/patroni/citus#citus) 配置节中设置的 **`group`** 启用维护模式,但对于 [`patronictl switchover`](/zh/docs/patroni/patronictl#patronictl_switchover) 或 [`patronictl remove`](/zh/docs/patroni/patronictl#patronictl_remove) 等命令,则必须显式指定 group。 以下是 Citus 集群的 [`patronictl list`](/zh/docs/patroni/patronictl#patronictl_list) 输出示例: 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 能够在协调节点上 [**暂停**](/zh/docs/patroni/pause#pause) 对某个工作节点组所承载分片的 SQL 流量。主从切换在流量保持在协调节点的同时进行,并在新的工作节点主库准备好接受读写查询后立即恢复。 以下是在工作节点集群上执行 [`patronictl switchover`](/zh/docs/patroni/patronictl#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 | +-------+---------+-------------+----------------+---------+----+-------------+-----+------------+-----+ 以下是协调节点侧的日志: # 工作节点主库通知协调节点,它即将执行 "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 中获取了 leader 锁,即将运行 "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)) # 新的工作节点主库刚完成提升,通知协调节点它已准备好接受读写流量。 2024-08-26 07:02:41,485 DEBUG: query(COMMIT, ()) # 从此刻起,协调节点上发往工作节点组 2 的应用流量被解除阻塞。 -------- ## 从节点(Secondary nodes) 从 Patroni v4.0.0 起,没有 **`noloadbalance`** [**标签**](/zh/docs/patroni/config/yaml#tags_settings) 的 Citus 从节点也会被注册到 **`pg_dist_node`** 中。但若要将从节点用于只读查询,应用程序需要修改 [citus.use_secondary_nodes](https://docs.citusdata.com/en/latest/develop/api_guc.html#citus-use-secondary-nodes-enum) GUC。 -------- ## 查看 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 协调节点需要读取整棵树,因为它们需要发现工作节点。工作节点只读取自身组的子树,某些情况下也可能读取协调节点组的子树。 -------- ## Kubernetes 上的 Citus 由于 Kubernetes 不支持层级结构,我们必须在 Patroni 创建的所有 K8s 对象中包含 citus group: batman-0-leader # 协调节点的 leader ConfigMap batman-0-config # 存储 initialize、config 和 history "键"的 ConfigMap ... batman-1-leader # 工作节点组 1 的 leader ConfigMap batman-1-config ... 命名规则为:**`${scope}-${citus.group}-${type}`**。 Patroni 使用 [标签选择器](https://kubernetes.io/docs/concepts/overview/working-with-objects/labels/#label-selectors) 来发现所有 Kubernetes 对象,因此带有 Patroni&Citus 的所有 Pod 以及 Endpoints/ConfigMaps 必须具有相同的标签,且必须通过 Kubernetes [**Kubernetes 配置**](/zh/docs/patroni/config/yaml#kubernetes_settings) 或环境变量来配置 Patroni 使用这些标签。 以下是使用 Pod 环境变量配置 Patroni 的两个示例: 1. 协调节点集群 ```yaml 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. 第 2 组工作节点集群 ```yaml 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 或 Endpoints)时,会自动为其添加 **`citus-group: ${env.PATRONI_CITUS_GROUP}`** 标签: ```yaml apiVersion: v1 kind: ConfigMap metadata: 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](https://github.com/patroni/patroni/tree/master/kubernetes) 目录中找到支持 Citus 的 Patroni Kubernetes 部署完整示例。 其中两个重要文件: 1. Dockerfile.citus 2. citus_k8s.yaml -------- ## Citus 升级与 PostgreSQL 大版本升级 首先,请阅读 Citus [文档](https://docs.citusdata.com/en/latest/admin_guide/upgrading_citus.html) 中关于升级 Citus 版本的内容。流程中有一处小变化:在执行升级时,必须使用 [`patronictl restart`](/zh/docs/patroni/patronictl#patronictl_restart) 代替 **`systemctl restart`** 来重启 PostgreSQL。 包含 Citus 的 PostgreSQL 大版本升级相对复杂。你需要结合 Citus 文档中关于大版本升级的技术,以及 Patroni 文档中关于 **`PostgreSQL major upgrade`** 的内容。请记住,Citus 集群由多个 Patroni 集群(协调节点和工作节点)组成,它们都需要独立升级。