PostgreSQL高可用实战:repmgr witness仲裁与switchover切换指南

发布时间:2026/10/19 5:52:41
PostgreSQL高可用实战:repmgr witness仲裁与switchover切换指南
生产环境里跑PostgreSQL高可用的朋友十有八九最后都会走到repmgr这条路上。一主一备两个节点repmgrd从旁值守主库挂掉自动提升备库。听起来很顺但真正实操过的人都知道两节点架构里藏着一个很尴尬的问题当两个节点互相联系不上时谁来拍板到底哪个节点才是活着的没人投票就容易脑裂。这篇文章想聊的就是两件事一是怎么用witness节点补齐这个仲裁环节二是在witness节点就位之后怎样执行一次完整、可控的switchover让备用节点正式顶班。整个过程我会按实际生产环境里“先规划、再注册、后切换”的顺序写附带配置示例、验证命令和踩坑记录适合正在用repmgr维护PostgreSQL集群、或者正准备从两节点升级到带仲裁节点架构的DBA和运维同学参考。1. 两节点高可用最大的盲区没人投票1.1 脑裂是怎么发生的两节点主从复制跑久了很多人会把关注点放在复制延迟和备份上却忽略了一个基础问题自动failover的决策依据是什么。repmgrd在检测到primary不可达时会尝试提升本地的standby。如果此时primary其实没有死只是网络抖动导致两个节点互相看不见那就会出现“原主库还在写新主库也开始写”的局面两边的数据开始分叉这就是脑裂。脑裂的可怕之处在于它不一定立刻暴露。等网络恢复两个节点重新建立联系时数据已经不一致了。这时候无论以哪边为准都会丢数据。传统做法是配置recovery.conf时代的trigger_file谁能触发生成文件谁就提升但这只是“抢”不解决判断问题。两节点架构里standby无法确认primary是真的死了还是只是自己跟primary之间的链路断了。1.2 witness到底解决了什么问题witness节点是repmgr从较早期版本就引入的一个角色官方文档的定义是“作为独立仲裁者加入集群的元数据节点”。它不存储业务数据也不参与流复制只做一件事实时记录它所看到的primary状态。当两个数据节点之间失联时repmgrd会去询问witness节点看它那边能否正常连接到primary。如果witness也连不上primary才判定primary彻底不可用允许standby提升。你可以把它通俗理解成“裁判”。两个队员起争执时谁也不能说服谁裁判出来说一句“我刚才看到那边已经下场了”问题就有了结论。witness的价值在于引入了一个第三视角避免只有双方互撕时的无解僵局。1.3 witness在自动failover中的实际参与方式在repmgr的自动failover流程里witness并不是简单地“被问一下”它有更具体的参与机制。默认配置下repmgrd会在primary、standby和witness三个节点上都运行。primary失联后standby上的repmgrd进入candidate状态同时witness节点上的repmgrd会尝试连接primary并记录连接结果。standby会根据witness的上报情况决定是否发起提升请求。这意味着witness节点本身也要保持高可用。如果witness宕机自动failover仍然可以发生但可靠性会下降因为少了一个判断依据。我这里建议把witness放在与主备两个节点不同的机房、网段或物理机上尽量别跟primary放在同一个故障域里否则primary机房断电时witness跟着一起挂仲裁就失去了意义。2. 准备工作添加witness之前必须交代清楚的环境2.1 基础架构与版本组合我先交代一下操作背景。以一套常见的部署为例两个数据节点跑PostgreSQL 14repmgr版本为5.3.x操作系统是Linux发行版。主节点叫pg-primary备节点叫pg-standby后面新增的witness节点叫witness-host。IP地址我这里用文档测试网段的占位地址实际部署时替换成真实内网地址即可。三个节点之间的端口要求不复杂data节点之间要互通5432端口witness节点要能访问primary和standby的5432端口同时所有节点要能访问repmgr元数据数据库所在的连接地址。如果中间有防火墙提前把规则开好别等到注册时才发现连不上。2.2 repmgr.conf里跟witness相关的参数witness节点上的repmgr.conf和普通数据节点略有差异核心参数如下node_id3 node_namewitness-host conninfohost192.0.2.10 port5432 userrepmgr dbnamerepmgr connect_timeout2 data_directory/var/lib/postgresql/14/witness log_file/var/log/repmgr/repmgr.log log_levelINFO witness_sync_interval_secs5有几个细节容易踩坑。第一node_id不能和现有节点冲突我在例子里用了3。第二conninfo里的host要指向primary的地址repmgr注册witness时会通过这个连接串找primary写入元数据。第三data_directory在witness节点上不需要真正存在数据目录repmgr不会往里面写数据但这个参数必须配置不配置会直接报错。我习惯指向一个空目录同时也让postgres用户对这个路径有可读权限减少不必要的权限报错。2.3 三节点之间的网络和认证要求认证配置是添加witness前最容易被忽略的环节。witness节点上需要安装repmgr软件包创建一个repmgr系统用户并建立一个同名的数据库用户。这个数据库用户要能从witness节点远程登录到primary上的repmgr数据库同时也要能登录到standby上的repmgr数据库。pg_hba.conf里需要为repmgr用户配置来自三个节点网段的host连接规则。我给一个参考片段host replication repmgr 192.0.2.0/24 md5 host repmgr repmgr 192.0.2.0/24 md5 host all repmgr 192.0.2.0/24 md5修改完不要忘记reload配置。另外所有节点的repmgr超级用户密码要保持一致因为repmgr在节点间同步元数据时使用的是同一个连接串。密码不一致时witness注册可能成功但后续repmgrd在witness上连接时会频繁报认证失败。3. 实战添加witness注册命令与验证3.1 witness节点安装与初始化witness节点上需要安装PostgreSQL服务端软件但实际上不需要初始化data目录。repmgr在做节点检查和连接探测时会调用pg_isready、psql等客户端工具所以二进制文件必须存在。安装软件包后创建repmgr系统用户再创建repmgr数据库用户然后直接跳到配置repmgr.conf这一步。需要确认的是witness节点上的repmgr版本必须和数据节点保持一致。比如数据节点用的是5.3.1witness节点就不要装5.2或5.4。我这里曾经遇到过版本不一致导致witness register时报参数不匹配的错误后来统一版本就正常了。3.2 witness注册命令配置好repmgr.conf后在witness节点上执行注册命令sudo -u postgres repmgr -f /etc/repmgr/repmgr.conf witness register执行成功后会输出类似下面的信息NOTICE: registering witness node with primary node pg-primary (ID: 1) DETAIL: attempting to connect to primary node INFO: witness node witness-host (ID: 3) registered如果注册前已经运行过repmgr的元数据初始化流程witness节点会被写入repmgr.nodes表角色显示为witness。注册过程本质上不向本地写任何数据只是把witness节点自身的信息登记到primary上的repmgr元数据库里。这一点很多朋友第一次操作时会困惑“我的witness节点上怎么没有生成任何文件”这是正常的它本来就是个纯逻辑节点。3.3 验证cluster show到底应该看到什么注册完成后在任意一个节点上查看集群整体状态sudo -u postgres repmgr -f /etc/repmgr/repmgr.conf cluster show输出大致是这样的Role | Name | Upstream | Connection String ---------------------------------------------------------- * Primary | pg-primary | - | host192.0.2.10 ... Standby | pg-standby | pg-primary | host192.0.2.11 ... Witness | witness-host | pg-primary | host192.0.2.12 ...看到Role列出现Witness就说明注册成功。注意观察Upstream列witness的upstream一般指向primary。如果指向为空或者指向错误节点多半是注册时conninfo指向错了需要注销后重新注册。还需要验证witness节点上的repmgrd能正常启动。启动后检查日志sudo -u postgres repmgrd -f /etc/repmgr/repmgr.conf --monitoring-history --daemonize tail -f /var/log/repmgr/repmgr.log日志里会周期性出现witness节点与primary节点之间的状态同步记录。如果出现连接超时或认证失败优先检查pg_hba.conf和防火墙规则再检查conninfo中的密码是否与repmgr用户实际密码一致。4. 手动switchover让备用节点正式顶班4.1 switchover前的检查清单witness节点就位后接下来就是重头戏手动switchover。所谓switchover就是有计划地把primary角色切换到standby原主库降级成备库。它和failover最大的区别在于“有准备、可控制、丢数据风险趋近于零”。执行switchover前我给自己定了一个固定检查清单照抄即可第一确认当前primary和standby都健康。跑一遍repmgr cluster show确保两个数据节点都处于正常状态没有任何一个节点处于“UNKNOWN”或“FAILED”。第二确认standby的WAL追平。可以通过repmgr node status查看standby的replay_lag最好为0或者极小值。我这里要求replay_lag小于一个WAL段的大小再切换。第三确认repmgr.conf中的promote_command和follow_command配置正确这两个命令是切换过程中自动执行的。第四确认应用连接字符串具备可切换的条件比如使用VIP或连接池而不是直接写死IP。第五找低峰期执行。虽然repmgr的switchover能做到基本不停服但切换瞬间还是有少量连接闪断。一个容易被忽略的点是执行switchover时不仅主库会短暂变为只读standby在提升前也需要追齐所有WAL。如果standby长时间没有接收WALswitchover会一直等待。等待时间过长时业务端的写入会先被阻塞。所以提前检查延迟不是可选项是必选项。4.2 执行standby switchover并观察全过程在standby节点上执行以下命令sudo -u postgres repmgr -f /etc/repmgr/repmgr.conf standby switchover执行过程中会输出一系列日志。正常流程大致是先检测当前节点身份确认它是standby然后检查primary节点是否可达接着把primary设置为只读模式等待standby追平WAL在standby上执行提升原primary切换到standby角色并follow新的primary最后更新repmgr元数据。这里我提醒一点repmgr 5.x的switchover命令执行时如果有其他会话连接在primary上repmgr会等待它们结束。如果等不到切换会卡住。实际生产环境里长事务是家常便饭我通常会在切换前手动对业务侧确认没有长时间运行的写入事务或者直接设定一个切换超时窗口超过就终止操作避免切换过程僵持。切换完成后repmgr会输出类似下面的信息NOTICE: attempting to pause the primary node NOTICE: promoting standby pg-standby (ID: 2) NOTICE: following new primary pg-standby (ID: 2) NOTICE: STANDBY SWITCHOVER has completed successfully看到completed successfully再松口气。4.3 切换后的角色确认与流量切换切换成功后再次执行repmgr cluster show此时的显示应该是Role | Name | Upstream | Connection String ---------------------------------------------------------- * Primary | pg-standby | - | host192.0.2.11 ... Standby | pg-primary | pg-standby | host192.0.2.10 ... Witness | witness-host | pg-primary | host192.0.2.12 ...注意两个变化一是Role和Upstream列已经互换二是原来primary对应的witness的Upstream列可能仍然指向旧primary。repmgr在切换后会自动更新witness的upstream指向吗绝大多数版本里witness的Upstream列刷新会有延迟或者在某些情况下显示的还是原primary。这不一定代表异常但需要确认witness到新primary的网络连通性。我通常是手动ping一下再查看repmgr日志里witness与新primary的同步记录是否正常。应用流量切换上如果用了keepalived或者repmgr的event脚本绑定VIP切换脚本会在promote事件触发时把VIP漂移到新primary。如果没用脚本就需要手动改DNS或者连接池地址。这一步不在repmgr核心职责范围内但却是整个切换能不能被业务感知的关键。很多生产事故不是repmgr切换失败而是VIP没漂、应用连的还是旧主库。5. 切换现场复盘我踩过的坑与改善习惯5.1 常见问题的排查表把这两块操作合在一起实战过几轮之后我整理了一个常见问题排查表遇到问题可以先对着查一遍现象可能原因处理方式witness register报连接失败conninfo指向错误或防火墙未放行核对primary地址、5432端口连通性注册成功但cluster show没有Witness行repmgr元数据缓存未刷新在主节点执行repmgr cluster show --verbose强制刷新repmgrd在witness节点启动失败repmgr版本与数据节点不一致统一所有节点repmgr版本switchover卡在“attempting to pause”primary上有长事务或持锁会话找到并处理长事务或选择业务低峰重试切换后standby不自动follow新primarypromote_command或follow_command配置错误检查repmgr.conf中两个command字段及其执行权限切换后witness长时间不更新witness节点repmgrd未启动启动repmgrd查看日志定位连接问题排查时有个通用技巧所有repmgr操作的第一手线索都在日志文件里。不要只看终端输出命令行打印的往往只是摘要日志里才有完整链路。遇到解决不了的问题把log_level临时调到DEBUG复现一次故障基本都能找到原因。5.2 一些真实经验时间窗口、日志与连接池切换窗口的选择比想象中更重要。一次完整的switchover即便一切顺利也可能需要几十秒。如果是主备之间WAL积压较长时间切换周期会被拉长。我现在的习惯是做任何计划内切换前先连续跑一段时间的repmgr cluster show观察延迟曲线确认延迟稳定后再定窗口。不要只看切换那一刻的延迟。另一个容易被忽略的地方是连接池。如果应用直接连数据库端口切换瞬间所有连接都会断开应用要能自动重连。如果用了连接池要注意连接池配置的数据库地址是否与VIP绑定切换期间连接池本身的探活参数要做检查。我见过不止一次repmgr切换很成功但连接池没有正确释放旧连接导致应用持续报错。还有一点关于witness的心得不要只在文档里看到witness就急着加加上之后一定要做一次真正的failover演练。witness在正常状态下非常安静它真正发挥作用是在事故发生时。如果从来没验证过“主库彻底失联witness参与仲裁standby自动接管”这个完整链条那witness就可能只是个摆设。5.3 如果switchover和witness联动失效怎么办有一种比较少见但确实存在的场景switchover执行完毕后witness日志里不断出现“unable to connect to upstream node的报错。这种情况通常不是repmgr本身的问题而是witness节点上的repmr.conf中conninfo还指向旧primary地址而旧primary已经变成standbyrepmgr元数据库中的节点信息没有完全同步过来。解决办法很简单登录witness节点编辑repmgr.conf把conninfo中的主机地址改成新primary的地址然后重启repmgrd。不需要重新注册witness。改完之后继续观察两轮日志周期确认witness与新primary之间的状态同步恢复。另外如果因为异常情况导致witness在repmgr元数据库里的状态异常可以先注销再重新注册。注销命令是repmgr witness unregister重新走一遍注册流程即可。注意这个操作不会影响数据节点上的任何复制关系可以放心执行。6. 把witness和switchover放到日常运维节奏里witness节点的添加和switchover操作并非常规周更任务但一旦涉及PostgreSQL高可用架构调整这两件事几乎总是前后脚出现。我对生产团队的日常建议是witness注册完成后立刻安排一次完整switchover演练并保留演练日志。每季度或每次数据库参数大调整后再做一次同样演练。演练时重点观察三件事——切换耗时、连接闪断情况、witness在切换前后的元数据刷新状态。演练结束后别急着清理现场建议让新primary保持运行一到两天确认业务侧完全稳定后再考虑是否把角色切回原位。repmgr支持连续多次switchover架构上完全允许但生产环境不是实验室稳定压倒一切。最后提一句我个人体会到的东西repmgr这套工具链的文档已经写得很全但它最大的学习成本不在命令本身而在于理解每个角色在故障场景下的真实行为。witness不是锦上添花switchover也不是一次点按钮的表演。把它们放在真实的故障模拟里反复验证几遍再回到生产环境做变更时你的底气会完全不一样。希望这篇实战记录能给正在搭建PostgreSQL高可用体系的你一些参考少走我走过的弯路。