ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

Keepalived高可用集群搭建与permanent error config报错排查实战

Keepalived高可用集群搭建与permanent error config报错排查实战 做运维这些年我见过太多因为入口服务挂掉导致整条链路雪崩的事故。高可用集群不是新概念但一直到今天Keepalived依然是我搭建入口高可用时最常用的基础组件之一它基于VRRP协议用很低的成本实现VIP在主备节点间的自动漂移配合健康检查脚本能让Nginx、LVS、HAProxy这类入口服务在故障时自动切换业务几乎无感。这篇文章适合刚接触高可用集群的运维和开发也适合那些正被“keepalived exited with permanent error config”这个报错折磨得头疼的人。我会把方案选型、配置原理、完整实操、报错排查都放在一起讲尤其是那个看起来吓人的“permanent error config”我会拆开揉碎讲清楚它到底在说什么。1. 高可用集群的设计思路与Keepalived的定位1.1 高可用首先要回答的问题故障发生时谁能接管高可用不是让机器永不故障而是让故障的影响尽可能小。围绕“服务入口”的高可用核心要回答三个问题谁在提供服务谁能在它故障时接管以及接管后流量如何无缝切换。Keepalived的定位就是用VRRP协议在这组节点中选举出一个主节点把虚拟IP绑定到它身上当它失联后其它节点自动把虚拟IP抢过来。虚拟IP对客户端来说始终存在这就是“逻辑上的单入口物理上的多节点”。我见过很多团队一开始只做Nginx进程守护用supervisor或systemd把Nginx拉起觉得这样就算高可用了。实际这种方案只解决了进程退出问题解决不了整机宕机、网络分区、内核故障这些更严重的场景。机器挂了进程守护也跟着没了VIP还在一个已经失联的机器上流量自然全断。Keepalived的价值恰恰在这里它不依赖单个节点的存活而是靠节点之间的互相“通信”来决策。1.2 为什么选择Keepalived而不是自己写脚本市面上高可用的方案确实不少有硬件负载均衡器有Consul配合HAProxy动态摘流也有直接在操作系统层面写脚本绑定VIP的。硬件方案效果好但成本高普通项目很难批量化落地。Consul那套更适合动态服务发现对入口这种固定角色来说有点重。自写VIP绑定脚本是最危险的一种做法我见过不止一个团队自己写了“检测到主节点挂了就切VIP”的脚本结果出现误判、双主脑裂、切换后路由不通这些问题最后还是要换回Keepalived。Keepalived胜在成熟、轻量和部署快一个配置文件加一个检查脚本就能跑起来。它本身支持VRRP协议、LVS调度、实时健康检查和主备通知机制对大多数中等规模的入口高可用场景来说属于性价比最高的选择之一。这个选择逻辑放在几年前如此现在依然如此。Keepalived的配置文件看着复杂但一旦理解了几个核心块后面的操作基本上就是抄自己的旧配置做小改动。1.3 VRRP协议是怎么工作的VRRP全称是Virtual Router Redundancy Protocol虚拟路由冗余协议。理解它其实很简单可以类比成小区有两个门卫一个主一个备对外只公布一个门牌号。主门卫每天定时在岗亭里发出“我还在”的信号备门卫听到信号就一直待在值班室。有一天主门卫突然不喊了备门卫等了一会儿没动静就立刻跑到岗亭里接管对外接待工作。外面的访客感知不到刚才发生了什么还是继续按老门牌号进出。在Keepalived的实现里所有参与节点都叫VRRP Router它们通过周期性发送VRRP报文竞选Master。Master负责绑定虚拟IP并对外提供转发服务Backup节点只是持续监听Master的报文一旦在超时时间内没收到就自动从Backup状态转为Master状态重新绑定虚拟IP并对外通告。这里有两个关键点必须提醒。第一VRRP默认使用组播地址224.0.0.18很多云平台默认屏蔽组播这时候就要改用单播模式在配置里用unicast_src_ip和unicast_peer显式指定对端地址。第二VRRP协议要求报文经过的链路必须可靠如果中间有防火墙拦截协议号112节点之间就收不到彼此通告结果就是两边同时认为自己是Master脑裂。这两条我在后面实操部分都会再提到。2. Keepalived的核心配置模块与参数详解2.1 配置文件结构四个块之间的关系Keepalived的配置文件默认在/etc/keepalived/keepalived.conf整个文件由几个核心块组成global_defs、vrrp_script、vrrp_instance以及LVS场景下的virtual_server。global_defs是全局参数区可以简单理解为环境变量区域通常用来设置router_id、脚本执行用户等。router_id虽然是唯一标识但在双节点里只要两边不一样就行不必纠结格式。脚本执行用户keepalived_script建议明确指定用root跑所有脚本会有一定风险但要是不指定有些脚本可能拿不到需要的系统状态。vrrp_script块定义健康检查脚本这个块是核心中的核心它决定了Keepalived要不要把某个节点“降级”。vrrp_instance块定义参与选举的实例一个Keepalived进程可以跑多个vrrp_instance用于多个VIP的不同主备组合。virtual_server块则是LVS负载均衡场景下用来配置后端真实服务器用的如果只是做Nginx或HAProxy的高可用转发不需要配这个块。很多初学者把这些块一股脑全写上结果只是做个最简单的Nginx VIP漂移却把real_server也配了启动后Keepalived一直尝试检测后端服务日志里刷一堆错误。我的建议是先搞清楚自己到底属于哪种场景再决定配置文件里需要哪些块。2.2 vrrp_instance关键参数state和priority的关系vrrp_instance里的参数是出错的高发区。先看state和priority的关系这是很多人一开始就搞错的点。state只是指定初始状态真正决定谁是Master的是priority。两个节点即使都写成MASTERpriority高的那个也会胜出另一个会自动降级为Backup。如果priority相同VRRP认为IP地址更大的节点优先。所以实际配置时有两种等价写法一种是一台写MASTER一台写BACKUP另一种是两台都写MASTER只靠priority分大小。我习惯都用MASTER只用priority区分这样即使有人把配置文件里的state字面量改乱了也不会破坏选举逻辑本身。advert_int是VRRP通告间隔默认1秒一般不需要动。如果网络抖动比较厉害可以适当增大到2秒但要明白增大间隔意味着主节点真挂了之后备节点要多等一会儿才能发现切换时间会变长。authentication块是必须的两边的认证方式和密码必须完全一致否则收到的报文会被丢弃在日志里表现为不停收到无效报文。virtual_ipaddress子块里我强烈建议加上dev和label比如“192.168.1.100/24 dev ens33 label ens33:0”这样VIP绑定到哪块网卡、漂移后显示成什么名字都一目了然。还有两个参数在云环境特别重要unicast_src_ip和unicast_peer。组播模式下不需要这两个参数但我前面说过云平台普遍抑制组播所以云服务器上的Keepalived十有八九得用单播。配置方法很简单主备节点都写上自己的源IP和对端的IP列表。2.3 健康检查脚本vrrp_script的正确写法高可用集群如果没有健康检查那就等于盲切换主节点哪怕服务已经瘫了只要进程还活着Keepalived就会继续对外通告“我很好”VIP也不会漂移过去。vrrp_script就是用来解决这个问题的周期执行一个外部脚本根据脚本返回值决定当前节点是否还能承担Master角色。这里有个关键参数weight。weight为负值时脚本执行失败会从当前优先级里扣掉对应数值比如priority是100weight是-20失败后实际参与选举的优先级就变成80备节点很容易胜出。weight为正值时则反过来脚本成功会额外加分。还有一种写法是添加nopreempt参数表示即使本节点恢复正常也不抢占Master适合不愿意频繁切换的场景。脚本本身要遵守一个原则只做检测不做恢复。我见过有人把脚本写成检测到Nginx挂了就自己拉起Nginx这其实也可以但要注意别在脚本里做太多事情否则Keepalived调一次脚本要花好几秒interval设成2秒就完全跟不上节奏了。检测逻辑建议按“进程、端口、HTTP探测”三个层级来做越靠后越接近真实用户视角。脚本要赋予可执行权限Keepalived默认以root执行如果脚本文件权限不对会被直接判定为失败触发降级。2.4 virtual_serverLVS场景下的调度配置如果你的高可用集群不只是做一个VIP转发而是作为LVS负载均衡器统一接收入口流量再分发到后面一堆真实服务器上那就必须用virtual_server块。这个块定义一个VIP加端口然后通过real_server指定后端真实服务器列表每个real_server还可以配置自己的健康检查方式比如TCP检查或HTTP_GET检查。Keepalived的一大优势就是把VRRP高可用和LVS调度整合在同一个进程里主节点负责调度备节点待命主节点挂了之后备节点顶上并继承整套LVS规则。这个模式非常适合四层负载转发场景尤其是对性能要求高的业务。但要提醒的是如果你只是做Nginx或HAProxy的高可用virtual_server块不是必须的。加上去反而会引入后端健康检查的逻辑一旦后端检测失败会连带影响VIP的可用性判断给自己增加不少排查负担。配置之前先想清楚出身我到底是要做“单入口高可用”还是要做“负载均衡入口高可用”两者选型差别很大。3. 双节点Nginx高可用集群的完整搭建实操3.1 环境准备与安装我下面用一套最简单的双节点Nginx配置来演示完整流程。两台机器分别是node1和node2都装CentOS 7或8都行网卡名假设都是ens33IP分别是192.168.1.10和192.168.1.11规划虚拟IP为192.168.1.100。先安装Keepalived。CentOS系用yum install -y keepalivedUbuntu系用apt install -y keepalived。安装完先确认版本执行keepalived -v2.x版本在配置语法上有一些增强但下面的配置在1.x和2.x上都兼容。Nginx同样先装好并确保两台机器上都能通过本机回环地址正常访问默认页面。为了后面验证切换效果我建议修改Nginx默认的index.html把node1和node2的主机名写进去这样切换后通过VIP访问时一眼就能看出当前流量落在哪台机器上。防火墙这里必须提前处理。VRRP协议走的是IP协议号112如果开启firewalld要放行VRRP协议或者直接放行组播地址224.0.0.18。云服务器还要关注安全组是否放行对应协议这一步不做后面两个节点永远处于“互相失联”状态脑裂概率极高。3.2 主备节点的Keepalived配置node1上的完整配置如下稍微解释几个关键点global_defs { router_id node1 script_user root enable_script_quota } vrrp_script chk_nginx { script /etc/keepalived/check_nginx.sh interval 2 weight -20 fall 2 rise 1 } vrrp_instance VI_1 { state MASTER interface ens33 virtual_router_id 51 priority 100 advert_int 1 authentication { auth_type PASS auth_pass 1111 } unicast_src_ip 192.168.1.10 unicast_peer { 192.168.1.11 } virtual_ipaddress { 192.168.1.100/24 dev ens33 label ens33:0 } track_script { chk_nginx } notify_master /etc/keepalived/notify.sh master notify_backup /etc/keepalived/notify.sh backup notify_fault /etc/keepalived/notify.sh fault }node2上的配置只需要改动几处router_id改成node2priority改成90state可以继续写MASTER也可以写BACKUPunicast_src_ip改成192.168.1.11unicast_peer改成192.168.1.10virtual_router_id要与node1保持一致authentication块里的auth_pass也要保持一致。之所以建议都写MASTER只用priority区分是为了防止某些人复制配置时把state漏改导致两个节点一个是MASTER一个是BACKUP但priority高低顺序却反了结果主备角色和预期相反。check_nginx.sh脚本我采用“进程检查加HTTP探测”双保险避免出现进程还在但Nginx已经僵死的情况。脚本内容#!/bin/bash if ! pgrep -x nginx /dev/null; then exit 1 fi if ! curl -fsS -o /dev/null --connect-timeout 2 http://127.0.0.1/; then exit 1 fi exit 0脚本放到/etc/keepalived/check_nginx.sh后要赋权执行chmod x /etc/keepalived/check_nginx.sh。然后可以先执行keepalived -t -f /etc/keepalived/keepalived.conf验证语法看到syntactically correct再启动服务。两个节点都启动后用systemctl status keepalived确认进程状态。3.3 VIP漂移与故障切换验证配置启动后先在node1上执行ip addr show ens33正常情况下能看到VIP 192.168.1.100已经绑定到ens33:0这个别名接口上。再在任意一台客户端机器上执行ping -c 3 192.168.1.100能通就说明VIP对外可达。接着用浏览器或curl访问VIP的80端口页面会显示node1。现在做故障切换模拟。第一步在node1上停掉Nginx服务执行systemctl stop nginx。Keepalived的健康检查脚本会在下一次执行时发现Nginx异常脚本返回1权重被扣掉20node1的优先级从100降到80。node2的优先级是90它很快就发现自己比node1更适合当Master于是接管VIP。在node1上看VIP已经消失在node2上执行ip addr show ens33看VIP已经漂移过来。整个切换时间通常在几秒内。第二步重新访问VIP应该会看到node2的页面。再把node1的Nginx启动起来如果配置了nopreemptVIP不会自动抢回去如果没配置node1会在脚本恢复成功后重新夺回Master。这个“切过去”和“切回来”的行为不同业务有不同的偏好需要根据实际情况选择。我还强烈建议配置notify脚本也就是配置文件里的notify_master、notify_backup、notify_fault这三项。它们会在节点状态变化时被调用可以在里面写日志、发告警、调用外部接口让每一次切换都有据可查。没有这个脚本出了故障你只能靠命令行去翻状态排查效率低很多。4. “keepalived exited with permanent error config”报错深度排查4.1 这个报错到底在说什么很多人在启动Keepalived时碰到了“Keepalived exited with permanent error config.”第一反应是觉得配置写错了但错误信息本身非常模糊根本没告诉你错在哪一行。这个英文直译过来就是“Keepalived因配置中的永久性错误而退出”。要理解这句话先要理解Keepalived的启动逻辑。Keepalived启动后首先会解析配置文件如果解析阶段遇到致命错误它不会进入运行状态而是直接打印错误并退出。此时日志里的“permanent error config”是最后那句话真正的原因其实在它前几行里比如“Unknown keyword”“Expecting ...”“please fix this configuration and restart keepalived”这类更具体的提示。所以遇到这个报错时一定不要只盯着最后一句。我见过不少人在网上搜索这句话把各种参数都改了一遍结果真正的问题只是配置文件里一个花括号没配对。时间全花在无意义的猜测上。正确做法是先看全量日志确定真正的错误行再动手改配置。4.2 高频踩坑清单这些配置错误会触发permanent error我汇总了这些年遇到的高频触发点做成一张对照表直接可以当排查手册用错误场景具体表现修复方式括号不匹配vrrp_instance或virtual_ipaddress子块少一个花括号全文检查成对括号用支持括号高亮的编辑器打开参数名写错比如virtual_router_id写成virtual_router对照官方配置模板逐项核对interface写错配置里写eth0但机器实际网卡是ens33执行ip addr确认后再填authentication块配置不一致两端auth_pass或auth_type不同修改为完全一致vrrp_script的script路径不存在脚本路径写错或漏写文件确认脚本存在并赋予可执行权限virtual_ipaddress语法错误写了CIDR但缺dev和label按“IP/掩码 dev 网卡 label 别名”格式修改weight超范围比如weight配置超过-254到254的范围修正weight数值文件编码问题编辑器保存时带BOM或者Tab与空格混用用cat -A检查隐藏字符统一为无BOM的UTF-8其中文件编码问题最隐蔽。有些Windows下编辑过的配置文件传到Linux会带一个看不见的BOM头Keepalived解析时会把BOM当成非法字符直接报“Unknown keyword”。另外有些人喜欢用Tab缩进如果配置文件里有混用解析器在某些版本上也会出问题。排查时执行sed -n 1p /etc/keepalived/keepalived.conf | od -c如果第一行开头多出357 273 277那就是BOM用sed -i 1s/^\xEF\xBB\xBF//去掉。4.3 高效的排查流程十分钟定位问题遇到这个报错我的固定排查流程是第一步systemctl status keepalived确认进程确实没起来记录下当前时间点。第二步journalctl -u keepalived --no-pager | tail -50看具体报错日志重点找“permanent error config”前面的几行。第三步执行keepalived -t -f /etc/keepalived/keepalived.conf做语法检查这一步会在终端直接输出问题行位置比在日志里翻更直观。如果输出“syntactically correct”说明语法本身没有致命问题接下来就要考虑是不是配置里引用的脚本、路径、权限出了状况。第四步如果语法检查通过但启动仍然报错就把配置文件里非必要的注释全删掉尤其是中文注释。有部分Keepalived版本对某些特殊字符不够友好注释里带中文或特殊符号解析器状态被扰乱后容易给出莫名其妙的错误。删完注释再重复一次语法检查。第五步确认配置里引用的所有脚本路径都存在并已赋权比如check_nginx.sh。我以前就遇到过配置语法完全正确但脚本路径拷过来时文件名前后多了个空格启动后一直在报错。按这个流程绝大多数permanent error都能在十分钟内定位到具体行。如果跑完这五步还是找不到原因建议把一个最小化配置写进单独文件逐个测试比如只保留一个vrrp_instance不挂脚本不配unicast确认能启动后再一步步加回其他块最后锁定问题出在哪一段。5. 生产环境经验与避坑指南5.1 脑裂问题比VIP丢失更可怕高可用集群里脑裂是最危险的故障模式。节点之间的通信断了但两边都还活着于是两边都认为自己是MasterVIP同时出现在两台机器上流量被拆成两半客户端会随机连到不同后端服务体验直接裂开。预防脑裂我总结过几条实用原则。第一通告超时时间设置要合理advert_int不建议超过2秒否则主节点实际挂了之后备节点要等很久才敢接管。第二能用单播就不用组播组播报文的稳定性很多云平台没法保障。第三在notify脚本里做双重校验当节点变成Master时先探测对端IP还通不通如果对端还能ping通但双方状态都是Master说明很可能发生了脑裂此时可以选择拒绝绑定VIP或发出高优先级告警。第四监控系统盯着VIP的数量正常情况下VIP只存在于一台节点上一旦发现两台节点同时都有VIP立刻触发告警。我在生产环境里给VIP做了一套独立的探活任务从第三方监控点定时ping VIP连续失败多次就报警这样即使Keepalived内部的日志没有暴露问题外部监控也能给到一个兜底的感知。5.2 配置变更要遵循的规范生产环境改Keepalived配置最忌讳“改完不检查就重启”。我给自己定了几个死规矩。每次只改一个变量。不要一次改动多个参数否则出了问题根本不知道是哪个变更引起的。改完必须先执行keepalived -t做语法校验确认没有语法错误。然后确认主备两台配置的差异只存在于允许不一致的字段比如priority、router_id、各节点的unicast_src_ip和unicast_peer其他参数必须完全一致。像virtual_router_id、auth_pass、advert_int这些字段但凡有一点点差异都可能导致选举异常或报文被丢弃。最后再平滑重启Keepalived并观察两端日志确认没有出现异常切换。如果一次要改多个实例不要直接在线上改先在测试环境完整模拟一遍。我见过一个真实案例运维同学一次改了三组vrrp_instance的priority顺序结果线上切换直接把还在服务的节点降级了业务中断了将近一分钟。这种代价完全可以通过规范变更操作来避免。5.3 几个实用脚本片段最后分享几个我在生产环境里反复使用的脚本框架可以直接抄。第一个是健康检查脚本框架我通常做三层检测。#!/bin/bash # 第一层进程检查 if ! pgrep -x nginx /dev/null; then exit 1 fi # 第二层本机端口检查 if ! ss -tln | grep -q :80 ; then exit 1 fi # 第三层真实HTTP探测 if ! curl -fsS -o /dev/null --connect-timeout 2 http://127.0.0.1/healthz; then exit 1 fi exit 0第二层是notify脚本框架它接收一个状态参数在状态变化时写日志并触发告警命令。注意脚本执行时Keepalived可能还没完全稳定最好先sleep几秒再动作否则容易产生误报。#!/bin/bash STATE$1 sleep 5 echo $(date %F %T) keepalived state changed to $STATE /var/log/keepalived-state.log if [ $STATE master ]; then # 这里可以调用告警接口或执行业务侧切换动作 /usr/local/bin/notify_alert.sh Keepalived became MASTER on $(hostname) fi exit 0这些脚本框架我在不同集群里反复用了一年多遇到的问题基本都集中在脚本权限、路径、探测超时这几个点上。只要把基础检查做扎实Keepalived本身是很稳的平时少折腾出故障时切得快就是它最好的状态。
返回列表