将 PostgreSQL 部署到 Kubernetes 看似只是把容器编排进去,实际生产中会立刻遇到挂载卷同步、Pod 漂移后数据一致性、副本切换自动化、WAL 归档生命周期管理等一系列问题。传统手动部署在 K8s 上不仅维护成本高,每次滚动更新都可能触发无意的换主。
CloudNativePG 是目前最完整的 PostgreSQL Kubernetes Operator 方案,它用自定义资源(CRD)将数据库集群当作一等公民进行管理,提供声明式备份、时间点恢复(PITR)、滚动升级等高级能力。本专题将从痛点出发,逐一拆解 Operator 的核心工作模式。
核心认知:Kubernetes 上的数据库 ≠ 无状态容器集群。数据持久性、一致性切换、备份恢复是需要 Operator 显式抽象的复杂逻辑,不能靠普通 Deployment + PVC 自行拼凑。
一、云原生数据库运维痛点
1.1 为什么普通 StatefulSet 不够
# 反例:用 StatefulSet 手动维护 PG
apiVersion: apps/v1
kind: StatefulSet
metadata:
name: my-postgres # 这行有坑
单纯 StatefulSet + PVC 能启动 PostgreSQL,但生产上必须解决:
| 痛点 | 说明 |
|---|---|
| 副本角色管理 | StatefulSet 不区分主从,需要手动维护 pg_is_in_recovery() |
| 故障切换 | Pod 挂了谁触发 failover?自动还是手动?数据丢失窗口多大? |
| 滚动升级 | 新版本容器推动作如何做到不终止活跃连接? |
| 备份与恢复 | WAL 归档到 object storage 的过程谁管理?PITR 如何做? |
| Secret/证书 | pg_hba.conf 和 replication 密码如何安全编排? |
| 监控集成 | 如何在 Pod 生命周期之外保持指标连续性? |
普通 StatefulSet 这些问题全部要自己写脚本解决,Operator 的存在正是为了把这些运维逻辑标准化。
二、CloudNativePG 架构
2.1 架构组件
┌──────────────────┐
│ CloudNativePG │
│ Manager Pod │ ← 监听 Cluster CRD 变更,协调所有子资源
└────────┬─────────┘
│
┌────┴────┐
↓ ↓
┌────────┐ ┌────────┐
│Cluster │ │Backup │ CRD 声明式资源
│ CRD │ │ CRD │
└───┬────┘ └───┬────┘
│ |
↓ ↓
┌────────┐ ┌────────────┐
│Primary │ │ Object │ (S3 / GCS / Azure Blob)
│ Pod │ │ Storage │
└──┬─────┘ └────────────┘
│
┌──┴──┐
↓ ↓
Replica Pods (同步/异步)
CloudNativePG 的运行时主要包含:
- Manager:一个 Deployment,负责监听 CRD 并协调所有子资源
- Cluster CRD:声明式定义数据库集群(主从、资源、存储类、备份配置)
- Instance Pod:实际运行 PostgreSQL 的 Pod,每个 Pod 运行一个
postgres实例 - Backup / ScheduledBackup CRD:定义一次性或周期性备份任务
- Pooler CRD(可选):管理 PgBouncer 连接池实例
2.2 与传统 Patroni 方案对比
| 特性 | Patroni(手动部署) | CloudNativePG |
|---|---|---|
| 部署方式 | 手动配置 etcd/consul + Patroni | Operator 自动部署 |
| API 交互 | REST API + 环境变量 | 声明式 CRD |
| 备份管理 | 需集成 wal-g/pgBackRest 脚本 | 内置,CRD 声明 |
| PITR | 手动配置 WAL 归档 + 时间戳恢复 | 内置,一个 CRD 声明 |
| 滚动升级 | 手动依次重启 | Operator 自动串行滚动 |
| 监控 | 自行集成 postgres_exporter | 可选 sidecar 自动注入 |
三、Cluster CRD 定义
3.1 基础集群
apiVersion: postgresql.cnpg.io/v1
kind: Cluster
metadata:
name: my-pg-cluster
namespace: database
spec:
imageName: ghcr.io/cloudnative-pg/postgresql:16.2
instances: 3 # 1 Primary + 2 Replicas
postgresql:
pg_hba:
- hostssl all all 0.0.0.0/0 scram-sha-256
bootstrap:
initdb:
database: myapp
owner: myapp_user
secret:
name: myapp-db-secret
storage:
size: 100Gi
storageClass: ssd-block
monitoring:
enabled: true
customQueriesConfigMap:
name: cnpg-custom-queries
| 关键字段 | 说明 |
|---|---|
instances | 总实例数(1 个 Primary,其余 Replica) |
bootstrap | 初始化方式:initdb 新建库、或 recovery 从备份恢复 |
storage | PVC 大小与 StorageClass |
superuserSecret | superuser 密码 Kubernetes Secret |
replicationSlots.highAvailability.enabled | 启用复制槽确保 WAL 不丢失 |
3.2 从备份初始化(PITR 预备)
apiVersion: postgresql.cnpg.io/v1
kind: Cluster
metadata:
name: my-pg-cluster
namespace: database
spec:
imageName: ghcr.io/cloudnative-pg/postgresql:16.2
instances: 3
bootstrap:
recovery:
source: my-pg-cluster # 从同名集群的备份恢复
database: myapp
owner: myapp_user
storage:
size: 100Gi
storageClass: ssd-block
backup:
enabled: true
retentionPolicy: "30d"
schedule: "0 2 * * *" # 每天凌晨 2 点
barmanObjectStore:
destinationPath: "s3://my-bucket/backups"
s3Credentials:
inheritFromIAMRole: true
四、自动备份与时间点恢复
4.1 备份原理
CloudNativePG 底层使用 Barman 进行备份管理:
- Base Backup:周期性的全量物理备份(
pg_basebackup) - WAL Archiving:持续将 WAL 段归档到对象存储
- PITR:基于 base backup + WAL timeline 恢复到任意时间点
apiVersion: postgresql.cnpg.io/v1
kind: ScheduledBackup
metadata:
name: daily-backup
namespace: database
spec:
immediate: true
schedule: "0 2 * * *"
cluster:
name: my-pg-cluster
backupOwnerReference: self
4.2 执行一次性备份
kubectl apply -f - <<'EOF'
apiVersion: postgresql.cnpg.io/v1
kind: Backup
metadata:
name: manual-backup-$(date +%s)
namespace: database
spec:
cluster:
name: my-pg-cluster
EOF
# 查看备份状态
kubectl get backup -n database
kubectl describe backup manual-backup-xxx -n database
4.3 时间点恢复(PITR)
# 恢复到一个特定时间点的完整流程:
# 1. 确认目标时间点有完整 WAL 覆盖
# 2. 创建新集群,指定 recovery target
apiVersion: postgresql.cnpg.io/v1
kind: Cluster
metadata:
name: pg-cluster-restored
namespace: database
spec:
imageName: ghcr.io/cloudnative-pg/postgresql:16.2
instances: 1 # 恢复时通常先单节点验证
bootstrap:
recovery:
source: my-pg-cluster
database: myapp
owner: myapp_user
recoveryTarget:
targetTime: "2026-09-28T14:30:00+08:00"
# 目标时间点必须有 base backup + 前后 WAL 完整
storage:
size: 100Gi
storageClass: ssd-block
关键:PITR 指定的时间点必须在
oldest_full_backup_time与latest_wal_time之间。可用kubectl cnpg backup --immediate-check验证连续性。
五、Replica 管理与故障自动切换
5.1 复制拓扑
# 查看集群拓扑与角色
kubectl cnpg status my-pg-cluster -n database
输出示例:
Cluster Summary
Name: my-pg-cluster
Namespace: database
PostgreSQL Image: ghcr.io/cloudnative-pg/postgresql:16.2
Instances: 3
Primary: my-pg-cluster-1
Status: OK
5.2 同步与异步复制
spec:
postgresql:
synchronous:
method: first
number: 1 # 至少 1 个同步副本
# 等效于 synchronous_standby_names = 'FIRST 1 (*)'
| 方法 | 含义 |
|---|---|
method: first | FIRST n (standby_names) 列前 n 个 |
method: any | ANY n (standby_names) 任意 n 个 |
5.3 手动发起 Failover
# 手动触发 failover(将 Primary 切换到指定实例)
kubectl cnpg promote my-pg-cluster my-pg-cluster-2 -n database
# 查看复制延迟
kubectl cnpg status my-pg-cluster -n database --verbose
# 关注 streaming replication lag
5.4 故障自动恢复
CloudNativePG 内置健康检测与自动故障切换:
- 检测到 Primary Pod 不健康
- 选举同步副本中最健康的实例
- 提升为 Primary
- 更新 Service Endpoint 指向新 Primary
- 其余 Replica 自动重新连接到新 Primary
六、Secret 与访问控制编排
6.1 Secret 自动生成
apiVersion: v1
kind: Secret
metadata:
name: myapp-db-secret
namespace: database
type: Opaque
stringData:
username: myapp_user
password: "$(openssl rand -base64 32)"
CloudNativePG 创建的 cluster 会自动生成以下 Secret:
| Secret 名称 | 内容 |
|---|---|
{cluster}-app | 应用账号(owner)用户名密码 |
{cluster}-superuser | postgres superuser 密码 |
{cluster}-ca | 集群内部 TLS CA |
{cluster}-replication | replication 密码 |
6.2 pg_hba.conf 与网络策略
spec:
postgresql:
pg_hba:
# 仅允许同 namespace 内访问
- hostssl all all 10.0.0.0/8 scram-sha-256
# K8s 内部 service CIDR
- hostssl all all 172.16.0.0/12 scram-sha-256
6.3 网络隔离
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: pg-allow-internal
namespace: database
spec:
podSelector:
matchLabels:
cnpg.io/cluster: my-pg-cluster
policyTypes:
- Ingress
ingress:
- from:
- namespaceSelector:
matchLabels:
name: backend
ports:
- protocol: TCP
port: 5432
七、滚动升级策略
7.1 Minor PostgreSQL 版本升级
# 修改 imageName 到新 minor 版本
spec:
imageName: ghcr.io/cloudnative-pg/postgresql:16.3
kubectl apply -f cluster.yaml
# Operator 会自动:
# 1. 依次对 replica 执行滚动重启
# 2. 最后对 primary 执行 switchover + 重启
# 3. 保证整个过程中集群可用
7.2 大版本升级
# CloudNativePG 支持通过 pg_upgrade 进行大版本升级
# 需要创建新的 Cluster,并挂载旧版本的数据 PVC
kubectl cnpg backup my-pg-cluster -n database --immediate
# 然后用新版本镜像创建 new-cluster,指定 recovery.source
常见问题(FAQ)
可以用 CloudNativePG 替代自建高可用方案吗?
完全可以。CloudNativePG 已在多家生产环境中验证,其故障切换、备份管理、滚动升级能力覆盖大多数场景。如果你的 K8s 集群自身高可用有缺陷(如单 Master、不可靠的网络分区处理),Operator 也无法弥补。
备份存在哪里最安全?
建议同时满足三条原则:
- 跨区冗余:对象存储桶开启跨区域复制
- 不可变策略:S3 Object Lock / GCS Retention Policy,防止备份被误删
- 定期恢复演练:每月至少一次用 PITR 恢复到测试环境
为什么 Primary 重启时会触发连接中断?
PostgreSQL 进程的连接伴随进程本身存在。任何 Primary 重启(即使是 switchover)都会终止当前活跃连接。建议应用层配置连接池重试策略(如 HikariCP connectionTestQuery + connectionTimeout),或在前端加 PgBouncer 作为 TCP 层缓冲。
相关阅读
- PostgreSQL 高可用方案 — Patroni 与物理复制的底层机制
- PostgreSQL 连接池与 PgBouncer 生产配置 — K8s 连接池的 Pooler CRD 方式
- PostgreSQL 备份与恢复 — pg_basebackup 与 WAL 归档原理
- PostgreSQL Docker 部署与初始化 — 容器化部署的基础知识
- PostgreSQL 监控与诊断体系 — Prometheus 与 postgres_exporter 监控
- PostgreSQL 专题导航
延伸阅读
- PostgreSQL 安全加固与权限管理 — K8s Secret 编排与 TLS 配置
- PostgreSQL 事件触发器与审计日志实现 — 云原生环境中的 DDL 审计
完整示例(一键复制)
# ========== CloudNativePG Cluster 完整模板 ==========
apiVersion: postgresql.cnpg.io/v1
kind: Cluster
metadata:
name: production-pg
namespace: database
spec:
imageName: ghcr.io/cloudnative-pg/postgresql:16.2
instances: 3
postgresql:
pg_hba:
- hostssl all all 0.0.0.0/0 scram-sha-256
synchronous:
method: first
number: 1
bootstrap:
initdb:
database: myapp
owner: myapp_user
secret:
name: myapp-db-secret
storage:
size: 500Gi
storageClass: ssd-block
monitoring:
enabled: true
customQueriesConfigMap:
name: cnpg-custom-queries
backup:
enabled: true
retentionPolicy: "30d"
schedule: "0 2 * * *"
barmanObjectStore:
destinationPath: "s3://my-backup-bucket/pg-backups"
s3Credentials:
inheritFromIAMRole: true
resources:
requests:
memory: "4Gi"
cpu: "2"
limits:
memory: "8Gi"
cpu: "4"
affinity:
enablePodAntiAffinity: true
topologyKey: kubernetes.io/hostname
---
# ========== 每天凌晨 2 点的定时备份 ==========
apiVersion: postgresql.cnpg.io/v1
kind: ScheduledBackup
metadata:
name: daily-backup
namespace: database
spec:
immediate: true
schedule: "0 2 * * *"
cluster:
name: production-pg
backupOwnerReference: self
---
# ========== PITR 恢复用的 Cluster 定义 ==========
apiVersion: postgresql.cnpg.io/v1
kind: Cluster
metadata:
name: production-pg-restored
namespace: database
spec:
imageName: ghcr.io/cloudnative-pg/postgresql:16.2
instances: 1
bootstrap:
recovery:
source: production-pg
database: myapp
owner: myapp_user
recoveryTarget:
targetTime: "2026-09-28T14:30:00+08:00"
storage:
size: 500Gi
storageClass: ssd-block
# ========== 常用 kubectl cnpg 命令 ==========
# 查看集群状态
kubectl cnpg status production-pg -n database
# 查看集群拓扑
kubectl cnpg status production-pg -n database --verbose
# 手动备份
kubectl cnpg backup production-pg -n database --immediate
# 手动提升 replica 为 primary
kubectl cnpg promote production-pg production-pg-2 -n database
# 查看集群 pod 日志
kubectl logs -n database -l cnpg.io/cluster=production-pg
# 进入 primary 数据库
kubectl cnpg psql production-pg -n database
# 查看所有备份
kubectl get backup -n database
继续阅读
探索更多技术文章
浏览归档,发现更多关于系统设计、工具链和工程实践的内容。