第 16/28 天
引言
在 K8s + Ceph 的生产环境中,PostgreSQL 是企业级业务中最常用的关系型数据库之一。与 MySQL 不同,PostgreSQL 以其强大的事务支持、丰富的数据类型、优秀的扩展能力和标准 SQL 兼容性在企业核心业务中占据重要地位。在 K8s 中部署 PostgreSQL,核心挑战在于高可用(HA)和自动故障转移。本篇将使用 Patroni 方案实现 PostgreSQL 高可用集群,结合 Ceph RBD 块存储持久化,构建一个生产级可运行的 PostgreSQL 集群。
设计架构
架构拓扑
Patroni 方案的核心架构如下:
┌─────────────────────────────────────────────────────────┐
│ K8s 集群 │
│ │
│ ┌──────────┐ ┌──────────┐ ┌──────────┐ │
│ │ Patroni │ │ Patroni │ │ Patroni │ │
│ │ (Leader) │ │ (Follower)│ │ (Follower)│ │
│ └────┬─────┘ └────┬─────┘ └────┬─────┘ │
│ │ │ │ │
│ ┌────▼─────┐ ┌────▼─────┐ ┌────▼─────┐ │
│ │PostgreSQL│ │PostgreSQL│ │PostgreSQL│ │
│ │ (主节点) │ │ (从节点1)│ │ (从节点2)│ │
│ └────┬─────┘ └────┬─────┘ └────┬─────┘ │
│ │ │ │ │
│ ┌────▼─────┐ ┌────▼─────┐ ┌────▼─────┐ │
│ │ Ceph RBD │ │ Ceph RBD │ │ Ceph RBD │ │
│ │ PVC │ │ PVC │ │ PVC │ │
│ └──────────┘ └──────────┘ └──────────┘ │
│ │
│ ┌──────────────────────────────────────────────┐ │
│ │ ZooKeeper / etcd (选主协调) │ │
│ └──────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────┘
核心组件说明
| 组件 | 数量 | 职责 |
|---|---|---|
| Patroni | 3 | PostgreSQL HA 管理器,负责主从切换、健康检查、选主协调 |
| PostgreSQL | 3 | 数据库实例(1 主 + 2 从),使用流复制(Streaming Replication) |
| ZooKeeper/etcd | 3 | 分布式协调服务,为 Patroni 提供 leader election 和 DCS |
| Ceph RBD PVC | 3 | 块存储持久化,每节点独立 PVC,避免数据争用 |
高可用机制
- 自动故障转移:Patroni 通过 ZooKeeper 的分布式锁检测主节点状态,主节点异常时自动将从节点提升为新主节点,切换时间通常在 30 秒内。
- 流复制:主节点通过异步流复制将 WAL 日志同步到从节点,从节点可配置
synchronous_commit实现同步复制保障数据安全。 - 读写分离:从节点可配置为只读,应用程序可通过 PostgreSQL 的连接池中间件(如 PgBouncer + Patroni API)实现读写分离。
- 存储持久化:每个 PostgreSQL 节点使用独立的 Ceph RBD PVC,RBD 镜像独立于节点生命周期,即使 Pod 被重新调度数据也不会丢失。
部署实战
步骤 1:添加 Helm 仓库
我们使用 Bitnami 官方的 postgresql-ha Chart,它内置了 Patroni 支持:
# 添加 Bitnami Helm 仓库
helm repo add bitnami https://charts.bitnami.com/bitnami
helm repo update
# 搜索 postgresql-ha chart 版本信息
helm search repo bitnami/postgresql-ha –versions | head -10
# 如果使用官方 PostgreSQL Chart(带 Patroni 支持)
helm repo add postgresql https://opensource.postgresql.org/chart
helm search repo postgresql/postgresql –versions | head -10
步骤 2:配置 Ceph StorageClass
确保 Ceph RBD StorageClass 已就绪(参见 Day 2 的配置):
# 验证 Ceph StorageClass 是否存在
kubectl get storageclass ceph-rbd
# 预期输出示例:
# NAME PROVISIONER RECLAIMPOLICY VOLUMEBINDINGMODE ALLOWVOLUMEEXPANSION AGE
# ceph-rbd k8s.io/rbd Delete Immediate true 30d
步骤 3:编写 values.yaml
以下是完整的 values.yaml 配置文件,重点关注 Ceph 存储、高可用和资源限制:
# values-postgresql-ha.yaml
# === 全局配置 ===
global:
imageRegistry: registry.example.com # 私有镜像仓库(可选)
# === PostgreSQL 配置 ===
postgresql:
auth:
postgresPassword: "YourStrongP@ssw0rd123"
username: "app_user"
password: "AppStr0ngP@ssw0rd456"
database: "app_db"
primary:
persistence:
enabled: true
storageClass: "ceph-rbd" # Ceph RBD StorageClass
size: 50Gi # 主节点数据盘大小
resources:
requests:
cpu: "1"
memory: 2Gi
limits:
cpu: "2"
memory: 4Gi
# === Patroni 配置 ===
patroni:
enabled: true
postgresql:
pg_hba:
– host all 0.0.0.0/0 scram-sha-256
parameters:
max_connections: 200
shared_buffers: "1GB"
effective_cache_size: "3GB"
work_mem: "16MB"
maintenance_work_mem: "512MB"
wal_buffers: "64MB"
random_page_cost: "1.1" # SSD/SSD 优化
effective_io_concurrency: "200"
# === ZooKeeper(DCS 协调) ===
zookeeper:
enabled: true
replicaCount: 3
persistence:
enabled: true
storageClass: "ceph-rbd"
size: 5Gi
resources:
requests:
cpu: "0.5"
memory: 512Mi
limits:
cpu: "1"
memory: 1Gi
# === 副本配置 ===
replicaCount: 3 # Patroni + PostgreSQL 总节点数(1 主 + 2 从)
# === 网络策略 ===
networkPolicy:
enabled: true
allowAnyIngress: false
additionalNetworkPolicies: []
# === 监控 ===
metrics:
enabled: true
serviceMonitor:
enabled: true
labels:
release: prometheus # 匹配 Prometheus ServiceMonitor
步骤 4:部署 PostgreSQL HA 集群
# 部署 PostgreSQL HA 集群(含 Patroni)
helm install postgresql-ha bitnami/postgresql-ha
–namespace postgresql
–create-namespace
–values values-postgresql-ha.yaml
–timeout 600s
# 查看部署进度
kubectl get pods -n postgresql -w
预期输出(等待所有 Pod Ready):
NAME READY STATUS RESTARTS AGE
postgresql-ha-postgresql-0 2/2 Running 0 2m
postgresql-ha-postgresql-1 2/2 Running 0 2m
postgresql-ha-postgresql-2 2/2 Running 0 2m
postgresql-ha-zookeeper-0 1/1 Running 0 3m
postgresql-ha-zookeeper-1 1/1 Running 0 3m
postgresql-ha-zookeeper-2 1/1 Running 0 3m
步骤 5:验证集群状态
# 查看 PVC 绑定状态(Ceph RBD 持久化验证)
kubectl get pvc -n postgresql
# 预期输出:
# NAME STATUS VOLUME CAPACITY STORAGECLASS
# data-postgresql-ha-postgresql-0 Bound pvc-xxx-yyy-zzz 50Gi ceph-rbd
# data-postgresql-ha-postgresql-1 Bound pvc-xxx-yyy-zzz 50Gi ceph-rbd
# data-postgresql-ha-postgresql-2 Bound pvc-xxx-yyy-zzz 50Gi ceph-rbd
# data-postgresql-ha-zookeeper-0 Bound pvc-xxx-yyy-zzz 5Gi ceph-rbd
# data-postgresql-ha-zookeeper-1 Bound pvc-xxx-yyy-zzz 5Gi ceph-rbd
# data-postgresql-ha-zookeeper-2 Bound pvc-xxx-yyy-zzz 5Gi ceph-rbd
# 查看 Service 列表
kubectl get svc -n postgresql
# 查看 PostgreSQL 集群状态(进入 Patroni 容器执行)
kubectl exec -it postgresql-ha-postgresql-0 -n postgresql -c postgresql —
patronictl -c /patroni/postgresql/conf/patroni.yml list
预期输出(集群状态):
+—————+———-+———+————-+—————-+
| Cluster Name | Role | Status | Timeline | Lag (MB) |
+—————+———-+———+————-+—————-+
| postgresql-ha | Leader | Running | 1 | 0 |
| postgresql-ha | Follower | Running | 1 | 0 |
| postgresql-ha | Follower | Running | 1 | 0 |
+—————+———-+———+————-+—————-+
登录验证
连接主节点
# 端口转发到本地(通过 Service)
kubectl port-forward -n postgresql svc/postgresql-ha-postgresql 5432:5432
# 使用 psql 连接(主节点)
psql -h 127.0.0.1 -U postgres -d app_db -p 5432
# 进入 PostgreSQL 后验证集群信息
SELECT pg_is_in_recovery() AS is_primary,
pg_postmaster_start_time() AS started_at,
current_setting('port') AS port;
验证复制状态
# 在主节点上查看复制状态
psql -h 127.0.0.1 -U postgres -d app_db -c "SELECT client_addr, state, sync_state, write_lag, flush_lag, replay_lag FROM pg_stat_replication;"
# 在从节点上查看复制进度
psql -h 127.0.0.1 -U postgres -d app_db -c "SELECT pg_last_wal_receive_lsn() AS receive_lsn, pg_last_wal_replay_lsn() AS replay_lsn;"
# 从 patronictl 查看同步模式
kubectl exec -it postgresql-ha-postgresql-0 -n postgresql -c postgresql —
patronictl -c /patroni/postgresql/conf/patroni.yml show-config | grep synchronous
验证 ZooKeeper 状态
# 查看 ZooKeeper 中 Patroni 的选主信息
kubectl exec -it postgresql-ha-zookeeper-0 -n postgresql —
zookeeper-shell -server localhost:2181 ls /postgresql-ha
# 查看当前 leader
kubectl exec -it postgresql-ha-zookeeper-0 -n postgresql —
zookeeper-shell -server localhost:2181 get /postgresql-ha/leader
日常巡检
健康检查
# 一键健康检查脚本
#!/bin/bash
NS=postgresql
echo "=== PostgreSQL HA 集群巡检 ==="
echo ""
# 1. Pod 状态
echo "— Pod 状态 —"
kubectl get pods -n $NS -o wide | grep -E "postgresql|zookeeper"
# 2. PVC 状态
echo ""
echo "— PVC 状态 —"
kubectl get pvc -n $NS | grep -E "postgresql|zookeeper"
# 3. 集群主从状态
echo ""
echo "— 集群主从状态 —"
kubectl exec -it postgresql-ha-postgresql-0 -n $NS -c postgresql —
patronictl -c /patroni/postgresql/conf/patroni.yml list
# 4. 复制延迟检查
echo ""
echo "— 复制延迟检查 —"
kubectl exec -it postgresql-ha-postgresql-0 -n $NS -c postgresql —
psql -U postgres -d app_db -c "
SELECT
client_addr AS '从节点IP',
state AS '复制状态',
pg_size_pretty(pg_wal_lsn_diff(pg_current_wal_lsn(), replay_lsn)) AS '延迟',
sync_state AS '同步模式'
FROM pg_stat_replication;
"
# 5. Ceph 存储用量
echo ""
echo "— Ceph 存储用量 —"
for i in 0 1 2; do
POD="postgresql-ha-postgresql-$i"
SIZE=$(kubectl exec $POD -n $NS -c postgresql — df -h /var/lib/postgresql/data | tail -1 | awk '{print $3}')
echo "$POD 数据目录占用: $SIZE"
done
日志查看
# 查看 PostgreSQL 日志
kubectl logs postgresql-ha-postgresql-0 -n postgresql -c postgresql -f
# 查看 Patroni 日志(关注 failover 和 leader 变化)
kubectl logs postgresql-ha-postgresql-0 -n postgresql -c patroni -f
# 查看 ZooKeeper 日志
kubectl logs postgresql-ha-zookeeper-0 -n postgresql -f
# 查看最近的 failover 事件
kubectl logs postgresql-ha-postgresql-0 -n postgresql -c patroni | grep -i "failover|leader|promotion" | tail -20
性能监控
# 查看连接数使用情况
psql -h 127.0.0.1 -U postgres -d app_db -c "
SELECT
numbackends AS '当前连接数',
max_connections AS '最大连接数',
ROUND(numbackends * 100.0 / max_connections, 1) AS '连接数使用率(%)'
FROM pg_postmaster_bgworkers;
"
# 查看慢查询(超过 1 秒)
psql -h 127.0.0.1 -U postgres -d app_db -c "
SELECT pid, now() – pg_stat_activity.query_start AS duration,
state, left(query, 100) AS query
FROM pg_stat_activity
WHERE state = 'active'
AND (now() – pg_stat_activity.query_start) > interval '1 second'
ORDER BY duration DESC;
"
# 查看表级统计
psql -h 127.0.0.1 -U postgres -d app_db -c "
SELECT schemaname, relname,
seq_scan AS '顺序扫描',
seq_scan_tuples AS '顺序扫描行数',
idx_scan AS '索引扫描',
n_tup_ins AS '插入行数',
n_tup_upd AS '更新行数',
n_tup_del AS '删除行数'
FROM pg_stat_user_tables
ORDER BY n_tup_upd + n_tup_ins DESC
LIMIT 10;
"
容量巡检
# 查看 Ceph RBD 镜像容量
for i in 0 1 2; do
POD="postgresql-ha-postgresql-$i"
echo "=== $POD ==="
kubectl exec $POD -n postgresql -c postgresql —
df -h /var/lib/postgresql/data
kubectl exec $POD -n postgresql -c postgresql —
du -sh /var/lib/postgresql/data/
done
# 查看 PostgreSQL 数据库大小
psql -h 127.0.0.1 -U postgres -d app_db -c "
SELECT datname, pg_size_pretty(pg_database_size(datname)) AS size
FROM pg_database
WHERE datname NOT IN ('template0', 'template1')
ORDER BY pg_database_size(datname) DESC;
"
常见问题
Q1: Patroni 选主失败,集群处于 split-brain 状态怎么办?
# 检查 ZooKeeper 是否正常
kubectl exec -it postgresql-ha-zookeeper-0 -n postgresql —
zookeeper-shell -server localhost:2181 ruok
# 返回 "imok" 表示正常。如果 ZooKeeper 异常,先修复 ZooKeeper 再恢复 Patroni
# 手动指定 leader(谨慎操作)
kubectl exec -it postgresql-ha-postgresql-0 -n postgresql -c patroni —
patronictl -c /patroni/postgresql/conf/patroni.yml failover
# 如果需要强制指定节点为主
kubectl exec -it postgresql-ha-postgresql-0 -n postgresql -c patroni —
patronictl -c /patroni/postgresql/conf/patroni.yml failover –leader postgresql-ha-postgresql-0
Q2: 从节点复制延迟过大如何处理?
# 查看具体延迟
psql -h 127.0.0.1 -U postgres -d app_db -c "
SELECT client_addr,
pg_size_pretty(pg_wal_lsn_diff(pg_current_wal_lsn(), replay_lsn)) AS '复制延迟',
write_lag, flush_lag, replay_lag
FROM pg_stat_replication;
"
# 常见原因:
# 1. Ceph 磁盘 I/O 瓶颈 → 检查 Ceph OSD 状态
# 2. 从节点 CPU/内存不足 → 检查资源限制
# 3. 网络延迟 → 检查 CNI 网络状态
# 4. WAL 生成过快 → 调整应用写入频率或增加从节点资源
# 临时缓解:增加 shared_buffers 和 wal_buffers
# 在 values.yaml 中调整后重新部署
Q3: 如何执行数据库备份?
# 使用 pg_dump 备份(逻辑备份)
kubectl exec -it postgresql-ha-postgresql-0 -n postgresql -c postgresql —
pg_dump -U postgres -d app_db -Fc > backup_$(date +%Y%m%d).dump
# 使用 pgBackRest(物理备份,推荐)
# 在 values.yaml 中启用 pgBackRest:
# pgbackrest:
# enabled: true
# configuration:
# stanza: app_db
# repo1:
# type: s3 # 或 local/sftp
# …
# 全量备份
pgbackrest –stanza=app_db backup –type=full
# 查看备份历史
pgbackrest –stanza=app_db info
总结
本篇详细介绍了在 K8s + Ceph 环境中部署 PostgreSQL 高可用集群的完整实践。通过 Patroni + ZooKeeper 方案实现了自动故障转移和主从管理,Ceph RBD 提供了可靠的持久化存储。核心要点:
- Patroni 负责主从切换和健康检查,切换时间通常在 30 秒内
- Ceph RBD 独立 PVC 保障数据持久化,支持在线扩容
- values.yaml 中
storageClass: ceph-rbd是关键配置,确保数据落在 Ceph 上 - pg_stat_replication 表是巡检复制状态的核心视图
- 生产环境建议启用 pgBackRest 进行物理备份,并配置 Prometheus + pg_exporter 监控
下期预告:第 17 天 — K8s 部署 MongoDB 三主三从集群(Ceph RBD + 架构设计 + 运维命令),详解 MongoDB 分片集群的架构设计与运维实战。
系列目录
- [Day 1] Ceph 分布式存储回顾与 K8s 存储体系概述 — 3307.html
- [Day 2] K8s 接入 Ceph RBD 块存储 — 3311.html
- [Day 3] K8s 接入 CephFS 共享文件存储 — 3317.html
- [Day 4] K8s 部署 MySQL 高可用集群 — 3320.html
- [Day 5] K8s 部署 Redis Cluster 集群 — 3324.html
- [Day 6] K8s 部署 Redis Sentinel 哨兵模式 — 3325.html
- [Day 7] K8s 部署 MinIO 对象存储集群 — 3329.html
- [Day 8] K8s 部署 MongoDB 副本集集群 — 3333.html
- [Day 9] K8s 部署 Nacos 注册配置中心 — 3337.html
- [Day 10] K8s 部署 Zookeeper 集群 — 3341.html
- [Day 11] K8s 部署 Elasticsearch 集群 — 3345.html
- [Day 12] K8s 部署 Kafka 集群 — 3349.html
- [Day 13] K8s 部署 GitLab 代码托管平台 — 3355.html
- [Day 14] K8s 部署 ClickHouse 列式数据库集群 — 3359.html
- [Day 15] K8s 中间件统一监控与运维巡检总结 — 3363.html
- 🟢 Day 16 今日 K8s 部署 PostgreSQL 高可用集群
- [Day 17] K8s 部署 MongoDB 三主三从集群
- [Day 18] K8s 部署 RabbitMQ 集群
- [Day 19] K8s 部署 Apache Pulsar 集群
- [Day 20] K8s 部署 OpenSearch 集群
- [Day 21] K8s 部署 ELK Stack 日志平台
- [Day 22] K8s 部署 Loki + Grafana 日志监控
- [Day 23] K8s 部署 Jenkins CI/CD 平台
- [Day 24] K8s 部署 Harbor 镜像仓库
- [Day 25] K8s 部署 Consul 服务网格
- [Day 26] 场景串联①:基础生产栈
- [Day 27] 场景串联②:微服务全栈
- [Day 28] 场景串联③:大数据/日志密集栈
















暂无评论内容