
做运维的朋友们可能都有过这样的深夜惊魂好容器完成了监控的Server、Proxy的部署、告警的收敛监控的覆盖率也达标了结果承载Zabbix Server和PostgreSQL的机器突然宕机。那一刻不是“少看几张图”的问题而是整个团队瞬间“告警失明”。监控对象越多积累的数据量就越大恢复就会越繁琐这个时候快速的恢复系统的可用是最为重要的任务所以现在和大家分享一下我在设计这套系统时的取舍与思考。第一部分痛点与取舍——我们为什么要这么设计做高可用本质上是一场“拿什么换什么”的交易。我们要解决的问题非常明确监控对象多环境复杂。一旦主机故障最要命的不是数据丢了而是在最需要告警的时候我们失去了全部告警能力。基于这个需求定下了三条硬约束无单点故障无论是zabbix server还是数据库都需要冗余的设计。复制是异步的数据是持续的写入的异步传输虽然可能会损失一些数据可以通过通过架构上的设计来避免但是保证主server的写入性能毕竟监控项数量起来了采集的数据量写入也不小。底线是分钟级恢复我们可以接受丢失几分钟的数据但绝对不能接受系统停摆几个小时。认清了现实我们确立了按优先级排序的三条取舍原则第一优先级绝不出双主 一切。数据分叉双主是不可逆的灾难而停机是可恢复的。所以在任何不确定的情况下我们的答案都是“不动作”。第二优先级可用性 零丢失。我们要的是“分钟级恢复可控丢失”。为了快速拉起服务我们愿意牺牲那一点点还没来得及同步的数据。第三优先级好维护 聪明。我们拒绝引入Keepalived、etcd或Patroni这些复杂的组件。我们只需要一个状态命令和一张决策清单。归位要人跑、强制启动要人敲越简单越不容易出错。所以这套方案解决的是“可用性”问题而不是“数据保全”问题。我们要保管的不是数据反正有主备两份而是“能登录节点”的能力。根据这些原则总体架构图如下架构非常简单就是两台配置一样的虚拟机作为盛昌服务器另外拿一台配置略低的服务器当作仲裁机生产机定期将自己的状态上报给到仲裁再依据仲裁提供的信息决定自己当前的服务启停当无法与生产服务器通信时保持当前服务状态。当前大部分使用场景中监控数量不超过五十万NVPS大概也是在5000以内实际NVPS我个人不是很看重毕竟一条日志和一个整数的写入在NVPS的计算里面体现不出差异但是对于系统资源的消耗是完全不一样的另外采集数据经过zabbix proxy整理以后到server通常都是顺序的写入了对存储的压力并不大反而要对历史数据进行分析的时候会产生大量的离散读这个时候压力是最大的。所以大部分场景中把数据库和zabbix server和DB部署在同一个虚拟机里面是能够应付的带来的好处对外是一个独立的环境运维起来相对简单在本方案中实际有两套完整的监控系统因为zabbix的前端基本就是静态的所以只要保证后端数据库的数据库完整前端就能正常操作。最后因为数据库是完全复制所以也方便做演练。第二部分不同场景的应对——出了事各自怎么办高可用架构不是装完就万事大吉而是要看它在各种极端情况下的表现。我们把各种故障场景的“损失”和“收益”做了一个盘点故障场景收益做对了什么损失付出了什么代价主库真故障60秒内做出决策约2分钟服务在VIP上恢复少量数据丢失主库计划内重启数据零丢失维护窗口内若发生真故障系统不会自动切换备库故障主库继续服务业务不受影响进入“一台服务、零台备份”的脆弱状态可能静默数月仲裁节点故障主库继续服务不中断失去自动切换能力需人工兜底网络分区绝不发生双主该切的时候没切服务不中断但失去高可用旧主库修复回来自动尝试归位一次故障期间未同步的写入永久丢失双主/撞车无收益正在发生数据分叉最严重的灾难复制早已中断后才故障拒绝接管防止丢失更多数据服务停摆直到人工介入两台同时重启自动各归各位无损失第三部分灵活的仲裁设计——怎么防止“脑裂”为什么我们需要第三台仲裁机器想象一下两台机器互相判断时如果网络分区了两边都看不到对方。这时候两边都可能觉得自己才是对的从而都去接管VIP这就是可怕的“双主”。第三台仲裁节点的价值就是“站在两边之外”。它能区分“两台都联系不上它”和“另一台真的死了”。在我们的系统里有两条雷打不动的不变式我是主库 ⇔ 我持有VIP身份不是靠嘴说的是靠绑上VIP这个动作来证明的。 2.成为主库 本机自证 仲裁许可缺一不可。仲裁许可的确切含义是VIP没有别人持有且没有任何证据表明对端正在服务。一次自动接管必须连过五道闸门总开关打开 → 复制是刚刚断的不是早就断了 → 仲裁可达且判定新鲜 → 判定结果为allow → VIP空闲且配置一致。在这里证据是有分级的带外观测 网络观测 自报。我们能确证的只有两件事VIP在哪台、对端在不在复制。该两项条件大于除此之外的一切读数。在遇到网络分区或仲裁宕机时系统会主动放弃自动化宁可“该切不切”也绝不双主。“什么都不做”也是一种极其重要的应对策略。第四部分极端情况下的恢复当故障发生旧主库修复后为什么归位必须是两阶段的归位的意思是让旧主库退回成备库并重新接回复制。自动程序只做两件事停掉本机的PG然后尝试归位一次。为什么只试一次因为每次归位都要停PG、回退数据目录绝不能变成无限重试的死循环。拉起旧主库必须让人来确认。这里有一个强制启动force的设计。当仲裁坏了但主库必须尽快起来时人可以使用force命令。它省掉了繁琐的仪式但不降级判据。它只解锁仲裁许可不解锁VIP检查。一句话绕过仲裁不绕过VIP。我们要时刻警惕最贵的错误——双主。数据分叉不可逆而停机可恢复。所以我们在绑VIP前后各复查一次值守程序也是第二道闸门。每一个不确定的地方答案都是“不动作”。–第五部分重建方式的考量归位是有代价的但也留了退路。增量归位pg_rewind是分钟级的如果失败才会退到全量重建那是小时级的。我们有一条铁律旧数据目录改名但不删。脚本绝不替人删数据这是最后一份“归位前”的现场证据。为了配合这套架构我们定下了三条核心纪律 1.不确定就不动作宁可该切不切人工兜底不可不该切却切了。 2.能确证的只有两件事VIP在哪台、对端在不在复制其余都是自述。 3.恢复速度换少量数据用分钟级恢复可控丢失换来“绝不双主”的底线。此外没有任何东西可以直连节点真实IP的5432端口所有流量必须走VIP。写在最后高可用架构从来不是追求100%不宕机而是在灾难发生时用最小的代价换取业务的延续。我们接受了分钟级的数据丢失接受了偶尔的人工兜底换来的极端条件下快速恢复的能力。运维的本质就是在各种不确定性中寻找那个最稳妥的平衡点。希望这次的分享能给正在搭建高可用架构的你带来一点不一样的启发。个人认知也有限其中若有错误之处也欢迎大家指正。项目地址https://gitee.com/liujun0253/zabbix-ha