Ceph+K8s 中间件实战 | 第 16 天:K8s 部署 PostgreSQL 高可用集群(Ceph RBD + Patroni + 架构设计)

第 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] 场景串联③:大数据/日志密集栈
© 版权声明
THE END
喜欢就支持一下吧
点赞0 分享
评论 抢沙发
头像 - 恒星
欢迎您留下宝贵的见解!
提交
头像 - 恒星

昵称

取消
昵称表情代码图片快捷回复

    暂无评论内容