1.收集ceph s3的地址
rook-ceph 方式
export AWS_HOST=$(kubectl -n default get cm ceph-bucket -o jsonpath='{.data.BUCKET_HOST}')
export PORT=$(kubectl -n default get cm ceph-bucket -o jsonpath='{.data.BUCKET_PORT}')
export BUCKET_NAME=$(kubectl -n default get cm ceph-bucket -o jsonpath='{.data.BUCKET_NAME}')
export AWS_ACCESS_KEY_ID=$(kubectl -n rook-ceph get secret rook-ceph-object-user-s3-store-s3-admin -o jsonpath='{.data.AccessKey}' | base64 --decode)
export AWS_SECRET_ACCESS_KEY=$(kubectl -n rook-ceph get secret rook-ceph-object-user-s3-store-s3-admin -o jsonpath='{.data.SecretKey}' | base64 --decode)
ceph方式
radosgw-admin bucket list --rgw-realm s3-store
radosgw-admin bucket stats --bucket=ceph-bkt-aad0791f-76df-43d3-9313-cca489d46ead --rgw-realm s3-store
rados -p s3-store.rgw.meta ls --namespace root
rados -p s3-store.rgw.meta --namespace root getxattr .bucket.meta.ceph-bkt-aad0791f-76df-43d3-9313-cca489d46ead:2cf54eee-cb43-4937-a385-87fb7a5fc689.473844.1 user.rgw.iam-policy
radosgw-admin user info --uid=s3-admin --rgw-realm s3-store
kubectl -n rook-ceph get httproutes.gateway.networking.k8s.io
kubectl -n rook-ceph get service
2.安装cloudnative-pg
创建ceph-s3-creds secret
kubectl create namespace postgresql
kubectl create secret generic ceph-s3-creds -n postgresql --from-literal=ACCESS_KEY_ID=5XQ8OBGZWG8MNO6M52Y2 --from-literal=ACCESS_SECRET_KEY=3negGAxSrskJ0OediH3osHLEAhs36AAoE8sD9nRt
kubectl apply --server-side -f https://github.com/cloudnative-pg/cloudnative-pg/releases/download/v1.29.0/cnpg-1.29.0.yaml
如果是生产系统,需要修改cnpg-1.29.0.yaml加入反亲和性,并将replicas改为3
在 spec.template.spec 下加入
affinity:
podAntiAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
- labelSelector:
matchExpressions:
- key: app.kubernetes.io/name
operator: In
values:
- cloudnative-pg
topologyKey: kubernetes.io/hostname
查看哪个pod是主库(leader)
kubectl get lease -n cnpg-system
注意:默认监控所有namesapce,如果需要监控特定namespace,请加以下参数,但是未验证
args:
- --leader-elect
- --watch-namespace=pg-prod
- --watch-namespace=pg-staging
- --watch-namespace=postgresql
和prometheus对接
# kubectl apply -f - <<EOF
apiVersion: v1
kind: Service
metadata:
name: cnpg-controller-manager-metrics
namespace: cnpg-system
labels:
app.kubernetes.io/name: cloudnative-pg
spec:
selector:
app.kubernetes.io/name: cloudnative-pg
ports:
- port: 8080
name: metrics
targetPort: 8080
EOF
service/cnpg-controller-manager-metrics created
# kubectl apply -f - <<EOF
apiVersion: monitoring.coreos.com/v1
kind: ServiceMonitor
metadata:
name: cnpg-controller-manager
namespace: cnpg-system
spec:
selector:
matchLabels:
app.kubernetes.io/name: cloudnative-pg
endpoints:
- port: metrics
interval: 15s
EOF
servicemonitor.monitoring.coreos.com/cnpg-controller-manager created
# kubectl get svc -n cnpg-system
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
cnpg-controller-manager-metrics ClusterIP 172.23.6.167
<none> 8080/TCP 9s
cnpg-webhook-service ClusterIP 172.23.7.171
<none> 443/TCP 14m
3.安装postgresql
cat pg-prod-final.yaml
# cat pg-prod-final.yaml
apiVersion: postgresql.cnpg.io/v1
kind: Cluster
metadata:
name: pg-prod
namespace: postgresql
spec:
# 关闭自动创建(废弃警告消失)
monitoring:
enablePodMonitor: false
instances: 3
imageName: ghcr.io/cloudnative-pg/postgresql:17.2
bootstrap:
initdb:
database: prod_db # 自定义初始数据库名称
owner: admin_user # 自定义初始数据库所属用户/用户名
# secret:
# name: pg-prod-custom-creds # 自定义生成的 Secret 名称(不填则默认为 pg-prod-app)
storage:
size: 30Gi
storageClass: openebs-hostpath-mysql-data2
affinity:
nodeAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
nodeSelectorTerms:
- matchExpressions:
- key: openebs-storage
operator: In
values:
- enabled
podAntiAffinityType: required
tolerations:
- key: ceph-taint
operator: Equal
value: "osd"
effect: NoSchedule
failoverDelay: 30
postgresql:
synchronous:
method: any # quorum模式,等价旧版min/maxSyncReplicas=1
number: 1
dataDurability: required # 副本不足时阻塞写入,强数据安全
failoverQuorum: true # v1.28+ 切换时校验多数派,防脑裂丢数据
parameters:
# --- 数据安全 ---
max_slot_wal_keep_size: "1GB"
synchronous_commit: "on" # 开启同步提交,保证零数据丢失
wal_level: "logical" # 开启逻辑日志,支持 CDC
archive_timeout: "300s" # 每 5 分钟强制切换一次 WAL,保证 RPO
# --- 内存分配 (按 8GB 节点内存优化,如内存更大请按比例调大) ---
shared_buffers: "2GB" # 核心缓存
work_mem: "16MB" # 排序操作内存
maintenance_work_mem: "512MB" # 管理维护操作内存
max_connections: "200" # 最大连接数
# --- 存储与查询优化 ---
random_page_cost: "1.1" # 适配 Ceph/SSD 存储的随机 IO 开销
effective_cache_size: "6GB" # 操作系统可用作缓存的总量
checkpoint_completion_target: "0.9" # 平滑检查点写入压力
max_wal_size: "2GB" # 增大 WAL 空间减少检查点频率
min_wal_size: "512MB"
# [备份策略 - 对接您的 Ceph S3]
# backup:
# retentionPolicy: "30d" # 备份在 S3 中保留 30 天
# barmanObjectStore:
# serverName: "pg-prod"
# destinationPath: s3://ceph-bkt-aad0791f-76df-43d3-9313-cca489d46ead/
# endpointURL: http://rook-ceph-rgw-s3-store.rook-ceph.svc:80
# # 引用上面创建的 Secret
# s3Credentials:
# accessKeyId:
# name: ceph-s3-creds
# key: ACCESS_KEY_ID
# secretAccessKey:
# name: ceph-s3-creds
# key: ACCESS_SECRET_KEY
# wal:
# compression: gzip # 日志压缩后再上传,节省带宽和空间
kubectl create -f pg-test-final.yaml
和prometheus对接
kubectl create -f cnpg-pg-full-alerts.yaml
# kubectl apply -f - <<EOF
apiVersion: monitoring.coreos.com/v1
kind: PodMonitor
metadata:
name: pg-test-podmonitor
namespace: postgresql
spec:
selector:
matchLabels:
cnpg.io/cluster: pg-test
podMetricsEndpoints:
- port: metrics
interval: 15s
EOF
podmonitor.monitoring.coreos.com/pg-test-podmonitor created
查看postgresql默认用户和密码,注意postgres没有密码
默认数据库名 (dbname):app
默认用户名 (user / username):app
默认 Secret 名称:<cluster-name>-app(本次测试就是 pg-prod-app)
# kubectl get secret -n postgresql pg-test-app -o jsonpath='{.data.password}' | base64 -d
s5gA8FtG57tQxkl1bmAONk8uc0GcCgsIrmROZ4XhOdrdDYbeMVhKXr4EqNT9ZSqI
# kubectl get secret -n postgresql pg-test-app -o jsonpath='{.data.username}' | base64 -d
app
登录postgresql
kubectl exec -it -n postgresql pg-test-1 -- psql "host=pg-test-rw user=app password=s5gA8FtG57tQxkl1bmAONk8uc0GcCgsIrmROZ4XhOdrdDYbeMVhKXr4EqNT9ZSqI dbname=app"
以超级管理员直连默认库
kubectl exec -it -n postgresql pg-test-1 -- \
psql -U postgres -d postgres
kubectl exec -it -n postgresql pg-prod-1 -- psql -U postgres
4.查看数据同步关键参数
查看哪个pod是主库
# kubectl get endpointslice -n postgresql -l kubernetes.io/service-name=pg-test-rw
NAME ADDRESSTYPE PORTS ENDPOINTS AGE
pg-test-rw-v78lq IPv4 5432 10.110.46.198 66m
切换主库测试
kubectl delete pod pg-test-1 -n postgresql
kubectl exec -it pg-test-1 -n postgresql -- psql -c "show synchronous_commit;"
Defaulted container "postgres" out of: postgres, bootstrap-controller (init)
synchronous_commit
--------------------
on
(1 row)
# kubectl exec -it pg-test-1 -n postgresql -- psql -c "show synchronous_standby_names;"
Defaulted container "postgres" out of: postgres, bootstrap-controller (init)
synchronous_standby_names
---------------------------------
ANY 1 ("pg-test-2","pg-test-3")
(1 row)
查看同步状态,确保sync_priority为1,sync_state为quorum或者sync,但不能是async,该指令只能主库执行
kubectl exec -it -n postgresql pg-test-2 -- psql -U postgres -c "select * from pg_stat_replication;"
pid | usesysid | usename | application_name | client_addr | client_hostname | client_port | backend_start | backend_xmin | state | sent_lsn | write_lsn | flush_lsn | replay_lsn | write_lag | flush_lag | replay_lag | sync_priority | sync_state | reply_time
------+----------+-------------------+------------------+---------------+-----------------+-------------+-------------------------------+--------------+-----------+-----------+-----------+-----------+------------+-----------+-----------+------------+---------------+------------+-------------------------------
3829 | 16386 | streaming_replica | pg-test-3 | 10.110.45.43 | | 49236 | 2026-04-08 08:30:14.91041+00 | | streaming | 0/A000110 | 0/A000110 | 0/A000110 | 0/A000110 | | | | 1 | quorum | 2026-04-08 08:37:02.949663+00
3957 | 16386 | streaming_replica | pg-test-1 | 10.110.49.106 | | 45990 | 2026-04-08 08:30:26.492283+00 | | streaming | 0/A000110 | 0/A000110 | 0/A000110 | 0/A000110 | | | | 1 | quorum | 2026-04-08 08:37:02.950647+00
(2 rows)
kubectl exec -it -n postgresql pg-prod-1 -- psql -U postgres -d prod_db -c "show synchronous_standby_names;"
Defaulted container "postgres" out of: postgres, bootstrap-controller (init)
synchronous_standby_names
---------------------------------------------
ANY 1 ("pg-prod-2","pg-prod-3","pg-prod-1")
(1 row)
dataDurability failoverQuorum两个参数验证方式 ① dataDurability: required(数据库层行为) 逻辑:在线可用同步副本 <1 时,所有写入阻塞,不提交事务防丢数据。 简易验证(业务低峰执行): 临时缩容删除 pg-prod-2、pg-prod-3,仅留主库 pg-prod-1; 主库执行 insert into xxx values(…); SQL 卡住无返回 = required 生效;立刻提交 = 未生效。
② failoverQuorum: true(CNPG Operator 控制器逻辑,库内无参数) 只能看 operator 日志校验,无 SQL 可查: 制造主库失联(停 pg-prod-1 pod / 隔离网络); 查看 cnpg-operator 日志,会打印多数派仲裁校验日志; 集群共 3 实例,必须至少 2 台存活才允许切换新主; 若只剩 1 台备存活,operator 拒绝切换,防止脑裂,代表该参数生效。
现在的状态已经是真正的生产级高可用了。
- 为什么现在的配置不会丢数据? 从你的 pg_stat_replication 输出看,状态发生了质变: synchronous_standby_names: 显示为 ANY 1 (“pg-test-2″,”pg-test-3”)。这意味着主库只有在收到 pg-test-2 或 pg-test-3 中至少一个副本的写入确认后,才会向你的应用程序返回“提交成功”。 sync_state 变为 quorum: 这代表 PostgreSQL 进入了法定人数同步模式。只要有一个从库在线并确认了日志,数据就是安全的。 sync_priority 为 1: 说明这两个从库都被正式纳入了同步选举名单。 结论:即使主库此刻突然断电,由于至少有一个从库(2号或3号)已经拿到了最新的日志,Operator 切换后数据是 100% 完整的。
- 为什么显示 quorum 而不是 sync? 这是 CloudNativePG 为了提高可用性的聪明做法: sync (Priority 模式):通常指定某个从库为同步,另一个为异步。如果同步从库挂了,主库会卡死。 quorum (Any 模式):你配置的是 minSyncReplicas: 1,所以只要副本集中有任意一个(Any 1)响应即可。这种模式在保证数据不丢的同时,容错性更高。
目前的同步延迟 (Lag) 全是空的,说明你的 Ceph 存储和网络性能非常好,完全跟得上主库的写入速度。
5.查看集群信息
查看集群名字
kubectl get cluster -n postgresql
NAME AGE INSTANCES READY STATUS PRIMARY
pg-test 128m 3 3 Cluster in healthy state pg-test-1
查看集群节点信息
kubectl get cluster pg-test -n postgresql -o jsonpath='{.status}' | jq
查看postgresql配置文件和参数
kubectl get cluster pg-prod -n postgresql -o yaml
登陆主库
MAIN_PG=$(kubectl -n postgresql get cluster pg-prod -o jsonpath='{.status.currentPrimary}')
kubectl exec -it -n postgresql $MAIN_PG -- env PGPASSWORD="zStta53wvjUUEQYPOAfR4oBYCYvYd94dbjujsV5D3cgHI6po6lXl9RuBTknMjOih" psql -U admin_user -h 127.0.0.1 -d postgres
6.安装kubectl-cnpg插件
wget https://github.com/cloudnative-pg/cloudnative-pg/releases/download/v1.29.0/kubectl-cnpg_1.29.0_linux_x86_64.tar.gz
tar zxvf kubectl-cnpg_1.29.0_linux_x86_64.tar.gz
chmod +x kubectl-cnpg
mv kubectl-cnpg /usr/local/bin/
通过kubectl-cnpg插件查看集群状态
立即查看同步状态
kubectl cnpg status pg-test -n postgresql
Cluster Summary
Name postgresql/pg-test
System ID: 7626286829348511765
PostgreSQL Image: ghcr.io/cloudnative-pg/postgresql:17.2
Primary instance: pg-test-2
Primary promotion time: 2026-04-08 08:30:12 +0000 UTC (20m51s)
Status: Cluster in healthy state
Instances: 3
Ready instances: 3
Size: 175M
Current Write LSN: 0/B000000 (Timeline: 2 - WAL File: 00000002000000000000000B)
Continuous Backup status
First Point of Recoverability: Not Available
Working WAL archiving: OK
WALs waiting to be archived: 0
Last Archived WAL: 00000002000000000000000A @ 2026-04-08T08:40:14.209077Z
Last Failed WAL: 00000002.history @ 2026-04-08T08:30:12.134846Z
Streaming Replication status
Replication Slots Enabled
Name Sent LSN Write LSN Flush LSN Replay LSN Write Lag Flush Lag Replay Lag State Sync State Sync Priority Replication Slot
---- -------- --------- --------- ---------- --------- --------- ---------- ----- ---------- ------------- ----------------
pg-test-1 0/B000000 0/B000000 0/B000000 0/B000000 00:00:00 00:00:00 00:00:00 streaming quorum 1 active
pg-test-3 0/B000000 0/B000000 0/B000000 0/B000000 00:00:00 00:00:00 00:00:00 streaming quorum 1 active
Instances status
Name Current LSN Replication role Status QoS Manager Version Node
---- ----------- ---------------- ------ --- --------------- ----
pg-test-2 0/B000000 Primary OK BestEffort 1.29.0 node174
pg-test-1 0/B000000 Standby (sync) OK BestEffort 1.29.0 node172
pg-test-3 0/B000000 Standby (sync) OK BestEffort 1.29.0 node173
重点看输出中的这两项: Continuous Archiving: 应该显示 OK(代表 WAL 日志正成功传往 Ceph S3)。 Ready instances: 3:代表你有 3 个活跃副本。 Sync State: quorum:代表它们处于法定人数同步状态。 Standby (sync):代表两个从库都是同步节点。
这份状态报告非常完美,说明你的集群已经处于最高级别的生产就绪状态。 核心状态解读(为什么它是“零丢失”的) Sync State: quorum: 这证实了你之前的配置已完全生效。主库 pg-test-2 只有在 pg-test-1 或 pg-test-3 至少其中一个确认收到日志后,才会提交事务。 Standby (sync): 注意底部的实例状态,pg-test-1 和 pg-test-3 都被标记为 sync。这意味着它们都有资格在主库挂掉时被选为新主,且数据是 100% 同步的。 Working WAL archiving: OK: 你的 Ceph S3 备份通道是通的。日志正在源源不断地传往 S3(Last Archived WAL 就在几分钟前)。
7.备份
kubectl apply -f pg-backup-schedule.yaml
或者
# kubectl apply -f - <<EOF
apiVersion: postgresql.cnpg.io/v1
kind: ScheduledBackup
metadata:
name: pg-test-daily-backup
namespace: postgresql
spec:
# 1. 立即执行一次
immediate: true
# 2. 定时策略:每天凌晨 2:00 执行全量备份 (Cron 格式: 秒 分 时 日 月 周)
schedule: "0 0 2 * * *"
# 3. 指定目标集群
cluster:
name: pg-test
# 4. 备份保留策略(1.29 版本建议在这里也写一份,双重保险)
# 配合 Cluster 定义中的 retentionPolicy: "30d" 使用
backupOwnerReference: self
EOF
查看备份状态和进度
kubectl get backup -n postgresql
或者用aws-cli查看s3 bucket
aws s3 ls s3://ceph-bkt-aad0791f-76df-43d3-9313-cca489d46ead --endpoint-url https://s3.infraserviceonline.com --recursive --human-readable
查看 Operator 的日志来确认清理动作: kubectl logs -n cnpg-system -l app.kubernetes.io/name=cloudnative-pg | grep -i “retention”
8.Grafana
dashboard ID:20417
9.避免数据库和crds被误删除
最安全、最标准、CNPG 专用的锁死命令!只锁:postgresql.cnpg.io/v1 Cluster(你的 pg-test)
- 只锁死你这个 pg-test(单独命令)
kubectl patch cluster pg-test -n postgresql --type=merge -p '{"metadata":{"finalizers":["do-not-delete"]}}' - 锁死 CNPG 的 CRD(防止删 CRD 触发删库)
kubectl patch crd clusters.postgresql.cnpg.io --type=merge -p '{"metadata":{"finalizers":["do-not-delete"]}}' `` 3.验证kubectl get cluster pg-test -n postgresql -o yaml | grep finalizers -A 5
10.各个组件职责分工 组件名称 对应本次测试的实体 核心职责 存储底座 Rook-Ceph (Block) 提供具备多副本、跨节点冗余的块存储。即使节点挂了,数据依然在。 数据库管家 CNPG Operator 负责监控 PG 进程、自动选主(Failover)、滚动升级和配置下发。 数据库实例 PostgreSQL Pods 运行真实的 PG 17 进程。1主2从,通过 Quorum 同步 确保数据不丢。 备份大脑 Barman (内置模式) 负责将 WAL 日志和全量数据压缩并上传到你的 Ceph S3。 定时器 ScheduledBackup 像 Linux 的 Crontab 一样,在每天凌晨自动踢一下 Operator 去做备份。 流量调度 RW / RO Service RW 始终指向主库,RO 把查询压力分流给两个从库,实现读写分离。 监控哨兵 Prometheus + PodMonitor 深入 Pod 内部 9187 端口抓取 cnpg_collector_up 等指标。
🔒 原创版权声明
本文标题:cloudnative-pg1.29/1.30安装部署
原文链接:https://www.infraserviceonline.com/cloudnative-pg1-29-1-30%e5%ae%89%e8%a3%85%e9%83%a8%e7%bd%b2/
版权来源:Infraservicenow
⚠️ 重要声明:本站所有文章均为原创技术文档,未经作者书面正式授权,严禁任何形式转载、复制、摘抄、二次发布。
如需转载、引用、整理发布,请务必提前联系作者获得授权,违者将依法追究侵权责任。
© 2026 Infraservicenow All Rights Reserved. 保留所有权利。