PostgreSQL 高可用通常意味着主节点选举、流复制、自动故障转移、健康检查和大量配置。CYBERTEC PG Operator(CPO)把这些能力封装进 Kubernetes 自定义资源:声明两个实例,Operator 负责生成集群,Patroni 则在数据库 Pod 内处理角色管理与故障转移。
下面以 Kubernetes 1.30、CPO 0.9.2 和 PostgreSQL 18.4 为例,在一台安装了 Docker、Minikube、Helm 和 kubectl 的开发机上复现整个过程。
启动单节点实验环境
创建一个分配 2 个 CPU、4 GiB 内存的 Minikube 集群:
minikube start -p cpo-deploy \
--driver=docker \
--cpus=2 \
--memory=4096
minikube profile cpo-deploy
接着安装 CPO:
helm repo add cpo https://cybertec-postgresql.github.io/CYBERTEC-operator-tutorials
helm repo update cpo
kubectl create namespace cpo
helm install cpo cpo/postgres-operator \
--namespace cpo \
--version 0.9.2 \
--set configKubernetes.enable_pod_antiaffinity=false
kubectl -n cpo rollout status deployment/postgres-operator
这里最需要注意的是 enable_pod_antiaffinity=false。Operator 默认会把数据库副本分散到不同 Kubernetes 节点,这正是生产环境需要的行为;但 Minikube 只有一个节点,如果保留强制反亲和性,第二个数据库 Pod 会长期停留在 Pending。
因此,这个开关只适合单节点实验。在真实的多节点集群中,应保留 Pod 反亲和性,并确认不同副本确实落在不同节点、可用区或故障域中。
用自定义资源声明数据库
下面的清单可以直接执行。它创建两个 PostgreSQL 实例,请求每个实例使用 250m CPU 和 1 GiB 内存,并分配 1 GiB 存储:
kubectl -n cpo apply -f - <<'EOF'
apiVersion: cpo.opensource.cybertec.at/v1
kind: postgresql
metadata:
name: pg-cluster
spec:
dockerImage: containers.cybertec.at/cybertec-pg-container/postgres:rocky9-18.4-1
numberOfInstances: 2
postgresql:
version: "18"
resources:
requests:
cpu: 250m
memory: 1Gi
limits:
cpu: "1"
memory: 1Gi
teamId: acid
volume:
size: 1Gi
EOF
numberOfInstances: 2 是这个示例中最关键的声明:集群将包含一个 Leader 和一个流复制 Replica。等待两个 Pod 就绪:
kubectl -n cpo wait \
--for=condition=Ready pod \
-l cluster.cpo.opensource.cybertec.at/name=pg-cluster \
--timeout=360s
kubectl -n cpo get pods -o wide
1 GiB 卷和固定的资源参数只适合本地验证。用于生产前,需要根据数据量、WAL 增长、查询工作集以及备份保留周期重新计算容量,并使用支持持久化和故障域调度的 StorageClass。
从 Patroni 和 SQL 两层验证复制
Pod 进入 Ready 并不等于数据库复制一定正常。先进入其中一个 Pod,通过 Patroni 查看集群成员:
kubectl -n cpo exec pg-cluster-0 -- patronictl list
正常情况下,列表中应出现一个 Leader 和一个处于 streaming 状态的 Replica。还要观察时间线、Receive LSN、Replay LSN 和复制延迟;示例运行中的副本延迟为零,但生产环境不能假设它始终为零。
再从 SQL 层验证。先向 Leader 写入数据:
kubectl -n cpo exec pg-cluster-0 -- \
psql -U postgres -v ON_ERROR_STOP=1 -c \
"CREATE TABLE t(id int); INSERT INTO t VALUES (1), (2), (3);"
然后从 Replica 查询:
kubectl -n cpo exec pg-cluster-1 -- \
psql -U postgres -c "SELECT count(*) FROM t;"
预期结果为 3。最后确认备用节点拒绝写入:
kubectl -n cpo exec pg-cluster-1 -- \
psql -U postgres -c "INSERT INTO t VALUES (4);"
命令应返回只读事务错误。这三步分别检查了成员角色、数据复制和只读保护,比单纯查看 Pod 状态更有意义。
需要留意,示例假定 pg-cluster-0 是 Leader、pg-cluster-1 是 Replica。这在刚创建的演示环境中成立,但故障转移之后,Pod 序号不再代表固定角色。自动化脚本应查询 Patroni 状态或使用 Operator 创建的角色服务,不应把某个 Pod 名永久写死为主库地址。
Operator 与运行时高可用的职责边界
这套架构并不是让 Operator 持续代理所有数据库流量。Kubernetes StatefulSet 负责维持 Pod 数量,缺失的 Pod 即使在 Operator 暂时离线时也能由控制器重新创建;Patroni 运行在数据库 Pod 内,负责 Leader 选举和副本提升。Operator 的主要职责是创建集群,并把扩容、配置调整和升级等声明变更转换为 Kubernetes 资源操作。
这种职责拆分减少了 Operator 本身成为单点故障的风险,但两实例也不等于完整的生产容灾方案。它仍然需要可靠的多数派协调机制、跨节点部署、备份与恢复演练、连接切换、监控告警,以及对恢复点目标和恢复时间目标的明确设计。
上线前不要省略这些检查
本地 Minikube 演示证明了 CPO 可以快速建立 Leader、流复制 Replica 和只读备用节点,但它只能验证进程级故障场景,无法证明节点级或可用区级高可用。
采用前应至少完成以下工作:
- 在多节点集群中重新启用 Pod 反亲和性,并检查副本的实际调度位置。
- 配置持久卷、快照或对象存储备份,并执行一次完整恢复演练。
- 验证 Leader Pod、工作节点和 Operator 分别中断时的行为,而不只检查正常状态。
- 通过稳定的 Kubernetes Service 或连接池访问数据库,避免直接连接 Pod IP。
- 监控复制延迟、WAL 积压、磁盘容量、Patroni 状态和频繁角色切换。
- 在升级 PostgreSQL、容器镜像或 Operator 前,先在预生产环境验证兼容性与回滚路径。
十分钟可以搭起高可用的骨架,但生产可用性来自故障域、存储、备份、连接管理和演练共同构成的系统,而不是 YAML 行数本身。