内容提要
本文介绍在Kubernetes上使用Crunchy PostgreSQL Operator和MinIO搭建多区域高可用架构,通过NGINX反向代理为MinIO提供HTTPS端点以满足pgBackRest的TLS要求,并演示跨区域复制、模拟区域故障、备用库提升为主库、处理时间线冲突与S3归档污染,最终完成安全回切。
延伸解读
TLS 终止与流式传输优化
pgBackRest 要求 S3 端点必须使用 HTTPS,但 MinIO 原生配置 TLS 较为繁琐。文章采用 NGINX 反向代理在 443 端口终止 SSL/TLS,并将明文 HTTP 转发至 MinIO 的 9000 端口。由于数据库备份和 WAL 流可能非常大,必须关闭 NGINX 的请求和响应缓冲,避免磁盘缓冲导致流中断。这一设计让集群内任何命名空间都能通过 minio-secure.minio.svc.cluster.local:443 安全访问 MinIO。
跨区域复制与故障转移机制
备用集群通过 spec.standby.enabled: true 配置为持续归档恢复模式,从 S3 存储库读取 WAL 并重放。当模拟区域故障时,先将主集群关闭,再将备用集群的该参数改为 false,Patroni 会退出恢复模式并触发时间线切换(从 Timeline 1 到 Timeline 2),使备用集群成为新的主库。这一过程依赖 S3 作为 WAL 仓库,确保数据同步。
回切中的时间线冲突与 S3 污染
回切时,旧主库可能已写入 Timeline 3 的元数据到 S3,而新主库处于 Timeline 2。备用库会尝试恢复至最高时间线,导致无法从 Timeline 2 的主库复制。同时,Operator 会保留不健康的旧实例 Pod,造成死锁。解决方案是清除 Operator 状态、删除 PVC,并清空 S3 存储桶,然后让新主库重新初始化存储桶并写入干净的 Timeline 2 备份,再以备用模式重新部署旧区域。
回切操作的关键步骤
回切需要按顺序执行:首先停止旧主库并清除其命名空间和存储;然后清空 MinIO 存储桶,并让新主库重新初始化存储桶布局,生成新的 Timeline 2 备份;最后将旧区域配置为备用模式(spec.standby.enabled: true,spec.shutdown: false)并重新部署。待 Pod 运行正常后,通过 patronictl list 确认其作为 Timeline 2 的备用库加入集群,并验证数据已从新主库复制回来。
Q&A
为什么在MinIO前面需要部署NGINX反向代理?
因为pgBackRest严格要求通过HTTPS的安全S3端点进行通信,而直接在本地MinIO上配置原生TLS过于复杂和繁琐。部署NGINX反向代理可以终止SSL/TLS(使用自签名证书),并将纯HTTP流量转发到MinIO的9000端口,从而满足pgBackRest的TLS要求。
如何配置备用集群以持续从主集群复制?
备用集群的配置与主集群基本相同,但需要设置spec.standby.enabled: true。这告诉Patroni以持续归档恢复模式启动,而不是初始化一个新的可写实例。备用集群需要从主集群获取基础备份来启动。
在模拟区域故障后,如何将备用集群提升为主集群?
首先,指示Operator关闭东区的主数据库。然后,将西区备用集群的manifest中的spec.standby.enabled设置为false。Patroni会退出恢复模式并启动时间线切换(从Timeline 1切换到Timeline 2),使西区数据库成为新的主库。
故障回切时遇到的两个主要障碍是什么?
一是Operator会保留旧的实例Pod(如StatefulSets)同时运行,因为时间线不匹配导致它们不健康,Operator拒绝删除它们。二是S3归档污染:旧东区主库可能写入了Timeline 3的元数据,而新西区主库在Timeline 2上运行,备用库无法从Timeline 2的主库复制,因为归档中存在Timeline 3。
如何清理S3归档污染以完成故障回切?
需要重置S3后端,清空MinIO桶。然后,指示西区主库初始化桶布局并写入一个干净的Timeline 2备份。这样,东区可以重新部署为备用库,从干净的Timeline 2备份启动。
完成故障回切后,如何验证东区备用库已成功加入集群?
当东区备用库Pod状态为4/4 Running后,运行patronictl list命令,确认它已作为健康的备用库领导者加入集群,并且处于Timeline 2。然后检查东区中的表,确认在西区故障期间写入的数据已复制回来。