- Published on
在 K8s 上运维 DB — CNPG、Percona、Vitess、Helm chart 完全指南
- Authors

- Name
- Youngju Kim
- @fjvbn20031
引言
在 Kubernetes 上运维数据库,几年前还是一个颇具争议的话题。 “在为 Stateless 工作负载优化的 K8s 上,为什么偏偏要跑 DB?”这样的疑问非常多。 但到了 2026 年的今天,随着 StatefulSet 与 Operator 生态足够成熟,在 K8s 上运维 DB 已经成为标准选项之一。
本文对比主流的数据库 Operator 与 Helm chart,并整理在实战中什么场景该用什么工具。
1. 为什么要在 K8s 上运维 DB
优点
- 统一的部署流水线:用同一套 GitOps 工作流管理应用与 DB
- 资源效率:节点资源由应用与 DB 共享,通过自动调度把利用率拉满
- 自动恢复:Pod 故障时自动重启,节点故障时自动重新调度
- 环境一致性:在开发/预发/生产用声明式方式部署完全相同的 DB 配置
- 成本下降:相比托管 DB 服务(RDS、Cloud SQL),可以降低许可与基础设施成本
缺点
- 运维复杂度:存储、网络、备份等需要自己直接管理的领域很多
- 性能开销:容器网络、存储层的叠加会带来额外延迟
- 要求专业度:需要对 K8s 与 DB 两侧都有深入理解
- 数据丢失风险:错误的 PV/PVC 配置或升级失误都可能导致数据损失
StatefulSet 基础
StatefulSet 是 K8s 中面向需要保持状态的工作负载的控制器。 与普通 Deployment 不同,它保证以下几点。
- 稳定的网络标识:每个 Pod 拥有并保持固有的主机名(例如 postgres-0、postgres-1)
- 有序的部署/伸缩:Pod 从 0 开始按顺序创建,并按逆序终止
- 持久化存储:通过 volumeClaimTemplates 为每个 Pod 绑定专属的 PVC
apiVersion: apps/v1
kind: StatefulSet
metadata:
name: postgres
spec:
serviceName: postgres
replicas: 3
selector:
matchLabels:
app: postgres
template:
metadata:
labels:
app: postgres
spec:
containers:
- name: postgres
image: postgres:16
ports:
- containerPort: 5432
volumeMounts:
- name: data
mountPath: /var/lib/postgresql/data
volumeClaimTemplates:
- metadata:
name: data
spec:
accessModes: ['ReadWriteOnce']
storageClassName: gp3
resources:
requests:
storage: 50Gi
但仅靠 StatefulSet 很难实现 HA、自动 Failover、备份/恢复、监控等能力。 解决这部分问题的正是 Operator 模式。
2. CloudNativePG (CNPG) — PostgreSQL 专用 Operator
概述
CloudNativePG(CNPG) 由 EDB(EnterpriseDB) 发起开发,目前作为 CNCF Sandbox 项目维护,是 PostgreSQL 专用的 Kubernetes Operator。 截至 2026 年 4 月,最新版本为 1.29,它用 Image Catalog 与 artifacts 生态对 PostgreSQL 扩展管理做了革新性的改进。
核心特性
- 原生 K8s 设计:不依赖 Patroni 这类外部 HA 工具,直接使用 K8s 的领导者选举机制
- 声明式 DB 管理:用 Database CRD 管理 PostgreSQL 数据库的生命周期
- 逻辑复制:用 Publication / Subscription CRD 支持在线迁移与大版本升级
- CNPG-I 插件框架:可以通过外部插件扩展功能
- 支持 PITR:基于 WAL 归档的 Point-In-Time Recovery
- 并行 reconciler:以并行处理提升集群管理效率
安装
用 Helm 安装最为简便。
# 添加 Helm 仓库
helm repo add cnpg https://cloudnative-pg.github.io/charts
helm repo update
# 安装 Operator
helm upgrade --install cnpg \
--namespace cnpg-system \
--create-namespace \
cnpg/cloudnative-pg
也可以直接用 manifest 安装。
kubectl apply --server-side -f \
https://raw.githubusercontent.com/cloudnative-pg/cloudnative-pg/release-1.29/releases/cnpg-1.29.0.yaml
HA 架构
CNPG 采用 Primary 1 台 + Standby N 台的结构。 Primary 负责写入,Standby 通过流复制同步数据。
- Primary 故障时,其中一台 Standby 会自动升主 (Promote)
- Switchover(计划内切换)与 Failover(故障时切换)都支持
- 提供通过同步复制保障数据持久性的选项 (dataDurability)
备份与恢复
CNPG 内置了基于 Barman 的持续备份。
apiVersion: postgresql.cnpg.io/v1
kind: Cluster
metadata:
name: prod-pg
spec:
instances: 3
storage:
size: 100Gi
storageClass: gp3
backup:
barmanObjectStore:
destinationPath: s3://my-backup-bucket/prod-pg/
s3Credentials:
accessKeyId:
name: aws-creds
key: ACCESS_KEY_ID
secretAccessKey:
name: aws-creds
key: SECRET_ACCESS_KEY
wal:
compression: gzip
retentionPolicy: '30d'
可以用 ScheduledBackup 预约定期备份。
apiVersion: postgresql.cnpg.io/v1
kind: ScheduledBackup
metadata:
name: prod-pg-daily
spec:
schedule: '0 2 * * *'
cluster:
name: prod-pg
backupOwnerReference: self
3. Percona Operator — 多 DB 支持
概述
Percona 为 MySQL、MongoDB、PostgreSQL 三种数据库分别提供专用的 Kubernetes Operator。 它以 Apache 2.0 许可完全开源,企业级功能可以免费使用。
各支持 DB 的特点
Percona Operator for MySQL (PXC)
- 基于 Percona XtraDB Cluster 的多主 (Multi-Primary) 架构
- 通过 Galera 同步复制,所有节点都可读写
- 借助 ProxySQL 或 HAProxy 实现自动路由
- 2026 年 GA 版本中新增 Group Replication 选项
Percona Operator for MongoDB (PSMDB)
- 支持 ReplicaSet 与 Sharded Cluster
- 支持基于 PVC 快照的备份(2025 年新增)
- 正式支持 MongoDB 8.0
- 用 IAM Role for Service Account 完成云存储认证
Percona Operator for PostgreSQL (PPG)
- 基于 Patroni 的 HA 构成
- 原生支持 pg_tde(透明数据加密)(2026 年)
- 零停机大版本升级的路线图正在推进中
统一监控:PMM
Percona Monitoring and Management(PMM) 是一款可以在同一个仪表盘中监控 MySQL、MongoDB、PostgreSQL 全部实例的工具。
apiVersion: pxc.percona.com/v1
kind: PerconaXtraDBCluster
metadata:
name: prod-mysql
spec:
crVersion: '1.15.0'
pxc:
size: 3
image: percona/percona-xtradb-cluster:8.0
resources:
requests:
memory: 2Gi
cpu: '1'
volumeSpec:
persistentVolumeClaim:
storageClassName: gp3
resources:
requests:
storage: 100Gi
haproxy:
enabled: true
size: 2
pmm:
enabled: true
serverHost: monitoring-service
backup:
schedule:
- name: daily-backup
schedule: '0 3 * * *'
keep: 7
storageName: s3-backup
storages:
s3-backup:
type: s3
s3:
bucket: my-backup-bucket
credentialsSecret: aws-creds
region: ap-northeast-2
多集群支持
Percona Operator 支持跨区域复制,可以在多个 K8s 集群之间同步数据。 借此可以搭建灾难恢复 (DR) 架构。
4. Vitess — MySQL 水平分片
概述
Vitess 最初由 YouTube 为处理大规模 MySQL 工作负载而开发,目前是 CNCF Graduated 项目。 它在提供与 MySQL 兼容接口的同时,支持透明分片、连接池化与在线重分片。
PlanetScale 是把 Vitess 商业化的代表性 DBaaS 服务。
架构组成要素
| 组成要素 | 作用 |
|---|---|
| VTGate | 查询路由器,应用连接的端点 |
| VTTablet | 包裹每个 MySQL 实例的代理 |
| Topology Service | 保存集群元数据 (etcd 等) |
| VTOrc | Orchestrator,负责自动 Failover |
| VTAdmin | 基于 Web 的管理 UI |
什么时候该选择 Vitess
- 单个 MySQL 实例无法承载的 大规模写入流量
- 需要对数十亿行以上的表做 大容量表分片 的场景
- 在线 schema 变更(Online DDL)频繁的环境
- 在保持 MySQL 兼容性的同时需要 水平扩展 的场景
在 K8s 上安装 Vitess
使用 PlanetScale 提供的 Vitess Operator。
# 安装 Vitess Operator
kubectl apply -f https://github.com/planetscale/vitess-operator/releases/latest/download/operator.yaml
以声明式方式管理 Keyspace(逻辑数据库)与 Shard。
apiVersion: planetscale.com/v2
kind: VitessCluster
metadata:
name: prod-vitess
spec:
images:
vtgate: vitess/lite:v19
vttablet: vitess/lite:v19
vtbackup: vitess/lite:v19
vtctld: vitess/lite:v19
vtorc: vitess/lite:v19
cells:
- name: zone1
gateway:
replicas: 2
resources:
requests:
cpu: '1'
memory: 1Gi
keyspaces:
- name: commerce
turndownPolicy: Immediate
partitionings:
- equal:
parts: 2
shardTemplate:
databaseInitScriptSecret:
name: commerce-schema
key: init.sql
tabletPools:
- cell: zone1
type: replica
replicas: 3
dataVolumeClaimTemplate:
storageClassName: gp3
resources:
requests:
storage: 50Gi
注意事项
Vitess 非常强大,但学习曲线相当陡峭。 对简单的 CRUD 应用来说可能是过度选择,分片策略 (Vschema) 的设计也需要充分论证。
5. 主流 Helm chart 对比
即便不用 Operator,只靠 Helm chart 也能把 DB 部署到 K8s 上。 Bitnami 曾是使用最广泛的 Helm chart 提供方,但 2025 年之后出现了重要变化。
Bitnami 许可变更 (2025)
2025 年 9 月之后,大部分 Bitnami Helm chart 的 OCI 包被移到了 Broadcom 的付费订阅之后。 公开的 docker.io/bitnami 镜像被迁到“Bitnami Legacy”仓库,不再获得更新、修复与安全补丁。
作为替代方案,Chainguard 提供了 40 多个从 Bitnami 分叉而来的安全加固 Helm chart, 社区主导的替代品也在持续增加。
主流 Helm chart 对比表
| chart | DB | 默认构成 | HA 支持 | 内置备份 | 备注 |
|---|---|---|---|---|---|
| bitnami/postgresql | PostgreSQL | Primary + Read Replica | 基于 Repmgr | X | 注意 legacy 问题 |
| bitnami/postgresql-ha | PostgreSQL | Primary + Standby | 对接 Pgpool-II | X | HA 专用 chart |
| bitnami/mysql | MySQL | Primary + Secondary | 半同步复制 | X | 有 InnoDB Cluster 选项 |
| bitnami/redis | Redis | Master + Replica | 基于 Sentinel | X | Cluster 模式另行配置 |
| bitnami/mongodb | MongoDB | ReplicaSet | 内置 | X | Sharded 为独立 chart |
| bitnami/mariadb | MariaDB | Primary + Secondary | 可选 Galera | X | 兼容 MySQL |
适合使用 Helm chart 的场景
- 开发/测试环境:想快速把 DB 跑起来的时候
- 简单构成:HA 并非必选项的小规模服务
- 学习用途:熟悉在 K8s 上运维 DB 的基础时
- 自定义配置很多的情况:需要用 values.yaml 做精细调优时
示例:PostgreSQL HA Helm chart
helm install prod-pg bitnami/postgresql-ha \
--set postgresql.replicaCount=3 \
--set postgresql.resources.requests.memory=2Gi \
--set postgresql.resources.requests.cpu=1 \
--set persistence.size=100Gi \
--set persistence.storageClass=gp3 \
--set pgpool.replicaCount=2 \
--set metrics.enabled=true
6. CNPG vs Percona vs Vitess vs Helm chart 综合对比
功能对比表
| 项目 | CNPG | Percona | Vitess | Helm chart |
|---|---|---|---|---|
| 支持的 DB | PostgreSQL | MySQL、MongoDB、PG | MySQL(分片) | 多样 |
| 许可 | Apache 2.0 | Apache 2.0 | Apache 2.0 | 因 chart 而异 |
| CNCF 状态 | Sandbox | - | Graduated | - |
| HA 自动 Failover | O | O | O | 因 chart 而异 |
| 自动备份 | O (Barman) | O(多存储后端) | O | X(需另行配置) |
| PITR | O | O | 部分支持 | X |
| 水平分片 | X | 仅 MongoDB | O(核心能力) | X |
| 监控集成 | Prometheus | PMM + Prometheus | VTAdmin | 各 chart 自带指标 |
| 连接池化 | 内置 PgBouncer | ProxySQL/HAProxy | 内置 VTGate | 需另行配置 |
| 运维难度 | 中 | 中 | 高 | 低 |
| 生产适配度 | 高 | 高 | 高(大规模) | 中等 |
| 基于 CRD 管理 | O | O | O | X |
选型指南
- 在 K8s 上运维 PostgreSQL:CNPG 是首选。K8s 原生设计加上活跃的社区
- 运维 MySQL/MongoDB:Percona Operator。统一监控 (PMM) 是它的强项
- 大规模 MySQL 分片:Vitess。可处理数十亿行以上的大容量数据
- 开发/测试环境:Helm chart。部署快、构成简单
- 多 DB 环境的统一管理:Percona。把 MySQL + MongoDB + PostgreSQL 纳入同一套运维模型
7. 运维注意点
存储 (PV/PVC)
存储是 K8s DB 运维中最重要的要素。
# 推荐的 StorageClass 示例 (AWS EBS gp3)
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
name: gp3-db
provisioner: ebs.csi.aws.com
parameters:
type: gp3
iops: '5000'
throughput: '250'
encrypted: 'true'
volumeBindingMode: WaitForFirstConsumer
allowVolumeExpansion: true
reclaimPolicy: Retain
核心原则:
- volumeBindingMode: WaitForFirstConsumer — 在 Pod 被调度到的 AZ 中创建卷
- reclaimPolicy: Retain — 即使删除 PVC 也保留数据
- allowVolumeExpansion: true — 允许在线扩容卷
- WAL 与 Data 卷分离 — 把顺序写入 (WAL) 与随机访问 (Data) 分开以提升性能
性能调优
# 为 Pod 配置 CPU pinning 与 NUMA 感知的示例
spec:
containers:
- name: postgres
resources:
requests:
cpu: '4'
memory: 8Gi
limits:
cpu: '4'
memory: 8Gi
# 确保拿到 Guaranteed QoS 级别
# 设置为 requests == limits
补充建议:
- Guaranteed QoS:把 requests 与 limits 设为相同值,防止 CPU 限流
- 拓扑感知调度:用 nodeAffinity 把 DB Pod 放到高性能节点上
- 反亲和性:让多个 DB Pod 分散到彼此不同的节点上
spec:
affinity:
podAntiAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
- labelSelector:
matchExpressions:
- key: app
operator: In
values:
- postgres
topologyKey: kubernetes.io/hostname
资源限制
- DB Pod 必须设置 resources.requests 与 limits
- 内存超过 limits 会触发 OOMKill — DB 进程会被强制终止
- PostgreSQL 的 shared_buffers 设为容器内存的 25% 上下
- MySQL 的 innodb_buffer_pool_size 设为容器内存的 50~70%
监控
# 在 CNPG 中启用 PodMonitor
apiVersion: postgresql.cnpg.io/v1
kind: Cluster
metadata:
name: prod-pg
spec:
instances: 3
monitoring:
enablePodMonitor: true
customQueriesConfigMap:
- name: pg-custom-queries
key: queries
storage:
size: 100Gi
必备监控指标:
- 复制延迟(Replication Lag):Standby 相比 Primary 落后了多少
- 连接数:相对最大连接数的使用率
- 事务吞吐量(TPS):每秒事务数
- 磁盘使用率:在 PV 容量耗尽前配置告警
- WAL 归档状态:备份系统是否正常运转
备份策略
把 3-2-1 备份法则应用到 K8s 环境。
- 3 份副本:Primary 数据 + WAL 归档 + 物理备份
- 2 种不同介质:本地 PV + Object Storage (S3/GCS)
- 1 份异地副本:复制到其他区域的存储桶
# CNPG 恢复集群示例
apiVersion: postgresql.cnpg.io/v1
kind: Cluster
metadata:
name: recovery-cluster
spec:
instances: 2
storage:
size: 100Gi
bootstrap:
recovery:
source: prod-pg
recoveryTarget:
targetTime: '2026-04-10T08:00:00Z'
externalClusters:
- name: prod-pg
barmanObjectStore:
destinationPath: s3://my-backup-bucket/prod-pg/
s3Credentials:
accessKeyId:
name: aws-creds
key: ACCESS_KEY_ID
secretAccessKey:
name: aws-creds
key: SECRET_ACCESS_KEY
8. 实战示例:用 CNPG 搭建 PostgreSQL HA 集群
下面是可以直接用在生产环境的 CNPG 集群完整 YAML。
Step 1: 创建 Namespace 与 Secret
kubectl create namespace database
kubectl create secret generic pg-superuser \
--namespace database \
--from-literal=username=postgres \
--from-literal=password=CHANGE_ME_TO_STRONG_PASSWORD
kubectl create secret generic aws-creds \
--namespace database \
--from-literal=ACCESS_KEY_ID=your-access-key \
--from-literal=SECRET_ACCESS_KEY=your-secret-key
Step 2: 定义 PostgreSQL 集群
apiVersion: postgresql.cnpg.io/v1
kind: Cluster
metadata:
name: prod-pg
namespace: database
spec:
description: 'Production PostgreSQL HA Cluster'
imageName: ghcr.io/cloudnative-pg/postgresql:16.4
instances: 3
startDelay: 30
stopDelay: 30
primaryUpdateStrategy: unsupervised
postgresql:
parameters:
shared_buffers: '2GB'
effective_cache_size: '6GB'
work_mem: '64MB'
maintenance_work_mem: '512MB'
max_connections: '200'
max_wal_size: '2GB'
min_wal_size: '1GB'
wal_buffers: '64MB'
random_page_cost: '1.1'
effective_io_concurrency: '200'
log_statement: 'ddl'
log_min_duration_statement: '1000'
pg_hba:
- host all all 10.0.0.0/8 scram-sha-256
bootstrap:
initdb:
database: appdb
owner: appuser
secret:
name: pg-superuser
storage:
size: 100Gi
storageClass: gp3-db
walStorage:
size: 30Gi
storageClass: gp3-db
resources:
requests:
memory: 8Gi
cpu: '4'
limits:
memory: 8Gi
cpu: '4'
affinity:
enablePodAntiAffinity: true
topologyKey: kubernetes.io/hostname
monitoring:
enablePodMonitor: true
backup:
barmanObjectStore:
destinationPath: s3://my-backup-bucket/prod-pg/
s3Credentials:
accessKeyId:
name: aws-creds
key: ACCESS_KEY_ID
secretAccessKey:
name: aws-creds
key: SECRET_ACCESS_KEY
wal:
compression: gzip
maxParallel: 4
data:
compression: gzip
jobs: 4
retentionPolicy: '30d'
nodeMaintenanceWindow:
inProgress: false
reusePVC: true
Step 3: 定期备份计划
apiVersion: postgresql.cnpg.io/v1
kind: ScheduledBackup
metadata:
name: prod-pg-daily
namespace: database
spec:
schedule: '0 2 * * *'
backupOwnerReference: self
cluster:
name: prod-pg
target: prefer-standby
Step 4: 配置 Pooler (PgBouncer)
apiVersion: postgresql.cnpg.io/v1
kind: Pooler
metadata:
name: prod-pg-pooler-rw
namespace: database
spec:
cluster:
name: prod-pg
instances: 2
type: rw
pgbouncer:
poolMode: transaction
parameters:
max_client_conn: '1000'
default_pool_size: '50'
Step 5: 部署与确认
kubectl apply -f cluster.yaml
kubectl apply -f scheduled-backup.yaml
kubectl apply -f pooler.yaml
# 确认集群状态
kubectl get cluster -n database
# 确认 Pod 状态
kubectl get pods -n database
# 集群详细信息
kubectl describe cluster prod-pg -n database
# 测试连接 Primary
kubectl exec -it prod-pg-1 -n database -- psql -U postgres -d appdb
9. 反模式 — 在 K8s 上运维 DB 时要避开的错误
1) 用 Deployment 部署 DB
Deployment 是给无状态 (Stateless) 工作负载用的。 对 DB 使用 Deployment,会出现 Pod 重启时数据丢失,或多个 Pod 同时访问同一数据目录的问题。 请务必使用 StatefulSet 或 Operator CRD。
2) 不用 PVC 而用 emptyDir
emptyDir 会随着 Pod 被删除而一同消失。 即便是测试环境,也请养成给 DB 数据使用 PVC 的习惯。
3) 没有备份就上线运行
即便 Operator 提供了 HA,也 必须单独配置备份。 HA 是针对基础设施故障的保护,而备份是针对逻辑错误(比如写错的 DELETE 语句)的保护。
4) 不设置资源限制
如果不给 DB Pod 设置 limits,它可能会侵占其他 Pod 的资源, 或在 OOM 时于无法预测的时点被终止。 请把 requests 与 limits 设为相同值,确保拿到 Guaranteed QoS。
5) 使用 reclaimPolicy: Delete
如果使用默认 reclaimPolicy 为 Delete 的 StorageClass, 删除 PVC 时 PV(也就是真实数据)会被一并删除。 DB 用的 StorageClass 请务必设置为 Retain。
6) 把所有 DB Pod 放在单个 AZ
不配置 Pod Anti-Affinity 就部署,所有 DB Pod 可能被排到同一个节点或 AZ 上。 一旦该节点/AZ 故障,整个 DB 都会宕掉。
# 正确的 Anti-Affinity 配置
affinity:
podAntiAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
- labelSelector:
matchExpressions:
- key: cnpg.io/cluster
operator: In
values:
- prod-pg
topologyKey: topology.kubernetes.io/zone
7) 没有监控/告警就上线运行
可能会出现复制延迟持续增大或磁盘被写满却无人察觉的情况。 至少要配置下面这些告警。
- 磁盘使用率超过 80%
- 复制延迟达到 10 秒以上
- Pod 重启次数上升
- 检测到备份失败
8) DB 升级时未验证滚动更新
PostgreSQL、MySQL 等的大版本升级,请务必先在独立集群上测试后再执行。 即便 Operator 支持自动升级,应用兼容性测试仍然必须由人来做。
9) 用明文管理 Secret
请不要把 DB 密码硬编码进 YAML。 请使用 External Secrets Operator 或 Sealed Secrets 来安全地管理 Secret。
# External Secrets 示例
apiVersion: external-secrets.io/v1beta1
kind: ExternalSecret
metadata:
name: pg-credentials
spec:
refreshInterval: 1h
secretStoreRef:
name: aws-secretsmanager
kind: SecretStore
target:
name: pg-superuser
data:
- secretKey: username
remoteRef:
key: prod/database/credentials
property: username
- secretKey: password
remoteRef:
key: prod/database/credentials
property: password
结语
在 K8s 上运维 DB 已经不再是实验性的选择。 CNPG、Percona、Vitess 这些成熟的 Operator 把复杂的运维工作自动化, 并与 K8s 的声明式管理模型自然融合。
核心要点:
- 若要在 K8s 上运维 PostgreSQL,CloudNativePG 是最佳选择
- 若要统一管理 MySQL/MongoDB/PostgreSQL,就用 Percona Operator
- 若需要大规模 MySQL 分片,则选 Vitess
- 在开发/测试环境中,Helm chart 依然最为便利
- 无论选择哪种工具,存储、备份、监控 都必须落实到位
如果要引入 Operator,请先在开发环境充分测试, 并模拟故障场景(删除 Pod、节点宕机、AZ 故障)之后再应用到生产环境。