跳转到主要内容

HA 集群扩展安装

在 Patroni 管理的 HA 集群中安装和管理 PostgreSQL 扩展

在 Patroni 管理的 HA 集群中安装 PostgreSQL 扩展,与单节点实例有本质区别。 本页解释原因,并介绍 pg ha extension 命令子树。

为什么不能用 pg extension install?

单节点流程(pg extension install <instance> <ext>)的工作方式是:

  1. 用 Pigsty 包构建 -ext 派生镜像
  2. 停止并用新镜像重建容器
  3. 编辑 postgresql.conf 设置 shared_preload_libraries
  4. 在容器内执行 CREATE EXTENSION

在 Patroni 集群中,步骤 2-4 全部失效:

  • Patroni 是 PID 1。 重建容器 = 该节点离线。如果是 leader,Patroni 会触发 故障切换 —— 集群在你安装到一半时重新洗牌。
  • Patroni 每个循环都从 DCS 重新生成 postgresql.conf。 任何对文件的直接 编辑都会在几秒内被覆盖。shared_preload_libraries 必须通过 patronictl edit-config(写入 DCS)来设置。
  • CREATE EXTENSION 必须在 leader 上运行,而 leader 可能在远程主机 —— 不在任何本地容器内。

pg ha extension 正确编排了所有这些步骤。

工作原理

安装流程遵循特定顺序,避免上述陷阱:

1. 验证扩展名(IsExtensionKnown)
2. 用 Pigsty 包构建 -ext 镜像
3. patronictl pause <scope> --wait            ← 禁用自动故障切换
4. 逐个重建成员容器(每次一个,等待重新加入)
5. patronictl resume <scope> --wait           ← ⚠ 必须在 edit-config 之前
6. patronictl edit-config(设置 shared_preload_libraries)
   → Patroni 触发滚动重启(replica 先,leader 最后)
7. 等待滚动重启完成
8. 在 leader 上执行 CREATE EXTENSION
9. 将扩展列表保存到 pg.yaml

关键顺序是 resume 必须在 edit-config 之前:暂停状态的 Patroni 集群不会 应用 edit-config 变更。如果在暂停状态下 edit-config,shared_preload_libraries 的更新会被静默丢失。

纯内置扩展快速路径

如果所有请求的扩展都是内置的(contrib 扩展,如 hstore、uuid-ossp,随 PostgreSQL 一起发布),步骤 2-4 会完全跳过 —— 不构建镜像、不重建容器、不 暂停/恢复。流程直接走 edit-config + CREATE EXTENSION。

命令

安装

pg ha extension install <scope> <extension>[,<extension>...] [flags]

构建 -ext 镜像,暂停集群,重建本地成员容器。

  • 单主机集群(所有成员在本地):自动执行 apply(resume + edit-config + CREATE EXTENSION)
  • 跨主机集群:只执行 pause + recreate,集群保持暂停。需要在每台主机上分别运行 install,最后在任意主机上运行 pg ha extension apply 完成安装。

扩展以逗号分隔传入:

# 安装单个扩展
pg ha extension install app pg_stat_statements

# 安装多个扩展到指定数据库
pg ha extension install app pg_cron,pg_stat_statements --database mydb

# 跳过滚动重启确认提示
pg ha extension install app pgvector --auto-restart

参数:

参数 默认值 说明
--database postgres CREATE EXTENSION 的目标数据库
--auto-restart false 跳过 edit-config 触发的滚动重启确认

⚠ --database 影响全部扩展。 指定 --database 后,apply 阶段会对所有已安装扩展(不仅是本次新增的)在目标数据库执行 CREATE EXTENSION IF NOT EXISTS。例如,集群已安装 [pg_cron, pg_stat_statements, pgvector],执行 pg ha extension install app hstore --database mydb 后,这四个扩展都会在 mydb 中创建。扩展的 .so 文件已在镜像中,不会重复安装,只是 SQL 对象(函数、类型等)在目标库中注册。

如果只想在特定数据库中启用已有扩展,无需重新 install,直接用 psql 连接对应库执行 CREATE EXTENSION IF NOT EXISTS 即可。

卸载

pg ha extension remove <scope> <extension>[,<extension>...] [flags]

在 leader 上执行 DROP EXTENSION,然后通过 patronictl edit-config 更新 shared_preload_libraries(触发滚动重启)。不会重建镜像或重建容器 —— -ext 镜像只增不减;磁盘回收很少见且需手动操作。

扩展以逗号分隔传入:

pg ha extension remove app pg_cron
pg ha extension remove app pg_stat_statements,pg_cron --auto-restart

列表

pg ha extension list <scope>

显示集群扩展的三个视图:

  • Config (pg.yaml): 存储在 pg.yaml 中的 extensions 列表
  • DCS (preload): 来自 patronictl show-config 的 shared_preload_libraries 值
  • Leader (installed): leader 上 pg_extension 中的实际扩展
$ pg ha extension list app
Cluster "app" extensions:

  Config (pg.yaml):  [pg_stat_statements pg_cron]
  DCS (preload):     shared_preload_libraries: pg_stat_statements,pg_cron
  Leader (installed): [pg_cron pg_stat_statements]

应用

pg ha extension apply <scope> [flags]

手动触发安装流程的后半段:恢复集群、执行 patronictl edit-config、在 leader 上执行 CREATE EXTENSION。

用于跨主机集群 —— 在每台主机上运行 install(各自构建镜像并重建本地 成员)后,在任意主机上运行一次 apply 完成 DCS 更新和扩展创建。

pg ha extension apply app
pg ha extension apply app --database mydb --auto-restart

⚠ --database 对所有已安装扩展生效。 apply 会对集群中全部扩展在指定数据库执行 CREATE EXTENSION IF NOT EXISTS,不仅限于本次新增的扩展。

跨主机工作流

在跨主机集群中,每台主机只管理自己的成员。扩展安装流程分为两个阶段, 全局只暂停/恢复一次,避免不必要的 leader 漂移:

第一阶段 —— 每台主机依次运行 install:

每台主机执行:

  • 在本地构建 -ext 镜像(合并 DCS 已有的扩展列表,确保包含所有包)
  • 暂停集群(幂等 —— 第二台主机不会因"已暂停"而报错)
  • 用新镜像重建自己的本地成员(同主机内 replica 先、leader 后)
  • 保存配置 —— 集群保持暂停状态,不 resume

推荐顺序:从 replica 所在主机开始。 如果 leader 所在主机先执行 install, leader 重建会触发故障切换(leader 漂移到其他主机)。从 replica 主机开始, leader 始终不动,直到最后才处理 leader 所在主机,最大限度减少 leader 漂移 带来的数据同步开销。

# 主机 A(只有 replica,先执行)—— 集群变为暂停状态
pg ha extension install app pg_stat_statements,pg_cron

# 主机 B(有 leader,后执行)—— 集群保持暂停
pg ha extension install app pg_stat_statements,pg_cron

第二阶段 —— 一次,在任意主机运行 apply:

  • 恢复集群(幂等 —— tolerate “not paused”)
  • 通过 patronictl edit-config 更新 shared_preload_libraries
  • 等待滚动重启
  • 在 leader 上执行 CREATE EXTENSION
pg ha extension apply app --auto-restart

注意: install 后集群处于暂停状态。如果忘记执行 apply,集群会一直暂停 (自动故障切换被禁用)。此时运行 pg ha extension apply 或手动 patronictl resume <scope> 即可恢复。

对于单主机集群(所有成员都在本地),install 自动执行 apply 步骤 —— 不需要单独的命令。

关于 leader 漂移: 目前无法完全避免 leader 漂移。当 leader 所在主机的 容器被重建时,Patroni 进程停止,DCS 中的 leader key 在 TTL 过期后失效, 即使集群已暂停(pause),其他 replica 仍会检测到 key 失效并触发新一轮选举。 replicas-first 重建顺序能确保 leader 所在主机是最后一个执行 install 的, 但无法阻止 leader 漂移本身。这是 Patroni + 容器重建的固有限制。

extensions 配置字段

扩展在 pg.yaml 的 Patroni 集群配置中以集群级别跟踪:

addons:
  patroni:
    app:
      name: app
      extensions:
        - pg_stat_statements
        - pg_cron
      passwords:
        superuser: ...
      members:
        node1: { ... }
        node2: { ... }

这个列表是 pg ha extension list 报告的内容,也是 apply 安装的依据。 install 和 remove 都会自动更新它。

shared_preload_libraries 排序

某些扩展必须出现在 shared_preload_libraries 的第 0 位(如果不在第一位, PostgreSQL 会 FATAL)。pgcli 在扩展目录中以 PreloadFirst 属性跟踪此约束 —— 当前设置此标记的扩展:

  • citus —— 分布式 PostgreSQL,必须最先加载
  • timescaledb —— 时序引擎,同样的约束

部分扩展还需要额外的 DCS 参数:

  • pg_cron 需要 cron.database_name

pg ha extension 自动处理所有情况:

  • 带 PreloadFirst 的扩展始终放在 CSV 开头,不管输入顺序如何
  • 当存在 pg_cron 时,cron.database_name 设为 --database 的值 (默认 postgres)
  • 当 pg_cron 被移除时,cron.database_name 被清除

示例

安装 pg_stat_statements 和 pg_cron

pg ha extension install app pg_stat_statements,pg_cron --database mydb --auto-restart

构建 -ext 镜像,重建所有成员(带 pause/resume),在 DCS 中设置 shared_preload_libraries=pg_stat_statements,pg_cron 和 cron.database_name=mydb,然后在 mydb 中创建两个扩展。

安装 Citus(必须排在 preload 第一位)

pg ha extension install app pg_stat_statements,citus

即使 citus 列在第二位,preload CSV 也会生成为 citus,pg_stat_statements —— 带 PreloadFirst 的扩展始终放在位置 0。

卸载扩展

pg ha extension remove app pg_cron

从 leader 删除 pg_cron,从 shared_preload_libraries 中移除,并清除 cron.database_name。触发滚动重启。

查看已安装的扩展

pg ha extension list app

限制

  • 镜像重建是增量的。 卸载扩展不会重建 -ext 镜像或缩小它。要回收磁盘, 需用 podman image prune 手动清理旧镜像。
  • 没有按成员的扩展列表。 扩展是集群级别的 —— 所有成员共享相同的 -ext 镜像和相同的 shared_preload_libraries。
  • CREATE EXTENSION 只针对一个数据库。 PostgreSQL 扩展是按数据库的。要在 多个数据库中安装,需用 --database 分别指定每个数据库重新运行。
  • 跨主机需要手动协调。 每台主机必须先运行 install,然后才能运行一次 apply。pgcli 不会 SSH 到远程主机。

参考