用 14 行 YAML 在 Minikube 上搭建 PostgreSQL 高可用集群

2026-08-18 22 预计阅读时间: 1 分钟
来源: postgr.es AI 摘要 Original link

Disclaimer: This article is an AI-assisted summary. Read it together with the original source when precision matters. The summary may omit context, version differences, or edge cases and is not official documentation.

预计阅读时间:8 分钟

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 行数本身。


相关推荐