ARTICLE DETAIL

资讯详情

深耕网站视觉设计与运营推广的一线实战洞察。

keepalived+NFS高可用架构实战:主备切换与故障演练全解析

keepalived+NFS高可用架构实战:主备切换与故障演练全解析 1. 别让NFS成为业务里那个最沉默的定时炸弹先说一个我踩过的真实场景。公司内部有个知识库系统后端服务全部无状态化前端挂了没问题重启拉起就行。所有附件、图片、导入导出的临时文件统一丢在一台NFS服务器上。这台机器配置不错系统盘做了RAID看起来稳如老狗。结果某天凌晨磁盘控制器报警紧接着整台服务器直接失去响应知识库所有附件下载全部超时前端页面报错刷屏。这时候你才会意识到一个问题应用可以随便重启但共享存储一旦挂掉所有依赖它的节点会同时出问题这不是扛一扛能过去的。更扎心的是当时并没有做任何高可用方案数据全靠那台NFS服务器的单块磁盘活着虽然系统盘有RAID但NFS数据目录所在的存储卷并没有冗余最后只能从备份恢复丢失了差不多半天的数据。这就是keepalivedNFS高可用要解决的问题。Keepalived负责提供一个虚拟IPVIP在NFS主节点故障时自动把VIP漂移到备用节点同时对NFS服务做健康检查保证漂移的不是一个空壳IP。两台服务器维护同一份数据业务端永远只访问VIP感知不到后端哪台机器在提供服务。这套方案成本低、结构清晰、在很多中小型生产环境里足够可靠。这篇文章主要面向正在做方案选型或者刚接手类似架构的运维同学。我会把架构设计、NFS和keepalived的完整配置、切换测试过程、以及常见的配置报错和坑全部过一遍。看完你应该能独立搭出一套可用的NFS高可用环境并且知道在真正上线前需要做哪些验证。2. 架构设计不是两台机器跑一样的东西就行2.1 主备模式的核心同一时刻只有一个节点在提供服务很多人第一次接触高可用第一反应是搞两台NFS服务器客户端随机连。这是完全错误的思路。NFS协议本身不提供多主并发写入同一目录的能力如果两台服务器同时导出同一个目录给所有客户端客户端A写入的数据只落在节点1客户端B读取时可能落在节点2上数据根本不一致还会出现各种诡异的文件句柄错误和缓存冲突。所以采用主备Master-Backup模式。正常状态下主节点拥有VIP对外提供NFS服务备用节点同样安装了NFS服务、导出了同样的目录但不对外提供服务。主节点故障后VIP漂移到备节点继续对外服务客户端无须任何改动。这里有个关键细节常被忽略备用节点的NFS服务必须处于可用状态。也就是说备节点上NFS要开机自启、exports配置要正确、存储目录要挂载好只是没有VIP客户端访问不到而已。否则切换发生时VIP过去了但服务起不来高可用就变成了高空跳伞。2.2 IP规划与组件交互以典型的双节点生产环境为例角色主机名物理IPVIPNFS主节点nfs01192.168.10.11192.168.10.20NFS备节点nfs02192.168.10.12192.168.10.20业务客户端Web服务器、应用服务器通过192.168.10.20访问共享目录。keepalived在主备节点上都配置这个VIP正常时VIP绑定在主节点网卡上当主节点网络中断、进程崩溃或NFS服务异常时备节点通过VRRP协议接收到状态变化然后在自己的网卡上绑定VIP并发送ARP广播通知同一网段内的设备更新MAC地址表。组件交互上分三层理解比较清晰VRRP层keepalived基于VRRP协议主节点周期性发送心跳报文备节点如果连续几个周期没收到心跳就认为主节点宕机开始抢占VIP。健康检查层keepalived通过脚本定期检查NFS服务状态比如systemctl status nfs-server、showmount -e、或者实际写文件测试。脚本返回0表示正常非0则触发降级。VIP漂移层所有对外的服务入口都走VIPVIP本身只是一个普通IP谁持有它谁就是当前主节点。2.3 脑裂问题为什么会发生以及怎么约束脑裂是主备架构里最让人头皮发麻的问题。所谓脑裂就是主节点并没有真正宕机只是它和备用节点之间的心跳网络断了备节点误以为主节点挂了于是把VIP抢过来结果同一时刻两台服务器都持有VIP客户端请求随机分发到两台机器上数据不一致立刻出现。VRRP协议本身有一定机制避免这个问题它要求备节点在收到比自己优先级低的Master通告时主动让位。但真实场景里还有很多情况会导致脑裂心跳网线松动、交换机端口异常、系统负载过高导致keepalived进程无法及时发送报文等。实践中能做的约束有几点心跳报文走独立网段或专用网卡不要和业务流量混在一起降低拥塞导致心跳丢包的概率。设置合理的arp_interval和arp_unicast_interval保证VIP漂移后ARP通告能及时发出。在关键业务里可以加一层仲裁机制比如备节点发现主节点失联后先尝试通过ssh连接主节点确认状态再决定是否抢占。这个可以用自定义脚本实现但会显著增加复杂度不推荐刚开始接触高可用的朋友一上来就这么搞。把脑裂的约束条件想清楚之后下一步才是动手处理NFS本身的部署细节。3. NFS部署不是装个包那么简单挂载参数直接决定切换体验3.1 不同发行版的安装差异NFS服务的安装在不同系统上有一些细微差别。以最常见的Ubuntu和Rocky Linux或CentOS 9系为例。在Ubuntu 24.04上NFS服务端安装包是nfs-kernel-server客户端工具在nfs-common里sudo apt update sudo apt install -y nfs-kernel-server nfs-common在Rocky Linux 9上sudo dnf install -y nfs-utils注意在Rocky Linux上nfs-utils同时包含服务端和客户端工具不用再单独装客户端包。装完后启用服务sudo systemctl enable --now nfs-serverUbuntu 24.04上还需要额外注意nfs-kernel-server安装后会自动创建/etc/exports默认不会导出任何目录。而Rocky Linux 9的nfs-server服务默认是关闭的必须手动enable --now否则配置了exports也不会生效。3.2 exports配置里的门道假设我们规划的数据目录是/data/nfs-share主备两台机器都先把该目录准备好sudo mkdir -p /data/nfs-share然后编辑/etc/exports/data/nfs-share 192.168.10.0/24(rw,sync,no_root_squash,no_subtree_check,fsid1)这里的参数每一项都值得解释rw允许客户端读写这是NFS共享的基础诉求。sync同步写NFS服务端必须把数据落到磁盘后才返回成功。选它意味着牺牲一点点性能换取强一致性。在高可用架构里这个参数尤其重要因为它直接关系到故障切换后数据是否完整。no_root_squash允许客户端root用户保留root权限操作共享目录。如果你有类似应用容器用root跑的需求这个参数很实用但要注意安全边界共享目录本身不应被无关主机访问。no_subtree_check关闭子树检查减少文件访问时的状态跟踪提升性能。对于导出整个目录树的情况是主流选择。fsid1给这个导出设置一个全局唯一的fsid多节点NFS共享、或者在某些客户端上避免文件句柄不一致问题都会用到。主备两个节点最好配置完全相同的fsid。配置完成后重新加载并验证sudo exportfs -r sudo exportfs -v sudo showmount -e localhost输出里能看到导出的目录和允许访问的网段就说明服务端正常了。在主备节点上千万不要只配置主节点、忽略备节点。备用节点要提前把同样的目录、同样的exports、同样的权限准备好。否则切换时流量过去了客户端直接挂载失败。我自己在测试时发现一个细节/etc/exports里的网段如果写得过于宽泛比如0.0.0.0/0某些云环境的安全组会导致NFS端口被扫描暴露。建议精确到业务的CIDR段不要图省事。3.3 客户端挂载参数与自动恢复客户端挂载NFS时不能裸用默认参数至少要加这几个mount -t nfs -o rw,hard,timeo30,retrans2,bg 192.168.10.20:/data/nfs-share /mnt/nfshard程序对NFS发起的IO请求会持续重试直到成功不会立刻报错返回。这是高可用场景下推荐的模式配合下面的加分项能让业务平滑恢复。timeo30请求超时时间单位是1/10秒这里是3秒。如果时间设太短主备切换那几十秒内应用会大量报超时错误设太长应用卡死感知变慢。30左右是实践里比较平衡的值。retrans2超时后重传次数。bg如果挂载失败在后台持续重试而不是在前台阻塞启动流程。这可以避免客户端开机时NFS服务还没就绪导致整条挂载链路卡死。需要特别提醒的是主备切换时VIP从主节点转移到备节点NFS服务端的文件锁和客户端打开的fd并不会自动恢复。很多应用在切换瞬间还是会报错需要重试逻辑兜底这一点后面我会单独讲。4. keepalived配置拆解、常见配置报错与修复完整过程4.1 安装和基础配置两台节点上都安装keepalived。Ubuntu上sudo apt install -y keepalivedRocky Linux 9上sudo dnf install -y keepalivedkeepalived的主配置文件是/etc/keepalived/keepalived.conf。主节点配置示例global_defs { router_id LVS_NFS_01 vrrp_skip_check_adv_addr vrrp_strict } vrrp_instance VI_NFS { state MASTER interface eth0 virtual_router_id 66 priority 150 advert_int 1 authentication { auth_type PASS auth_pass 1234 } virtual_ipaddress { 192.168.10.20/24 dev eth0 } track_script { chk_nfs } }备用节点把state改为BACKUPpriority设为100其余保持一致。virtual_router_id在同一个VRRP域内必须相同这个ID相当于组播域标识两台机器不一致的话互相不认识根本不会组成高可用组。这里要着重强调vrrp_strict这个参数。它的作用是严格执行VRRP协议规则包括对组播地址、源IP的校验。之前见过很多教程里默认带上vrrp_strict但没有配套设置网络导致keepalived起来以后VIP根本不生效、甚至通信异常。所以我的习惯是如果不能确认你的网络环境对组播报文完全没有限制先不启用vrrp_strict等整体跑通后再评估要不要打开。4.2 NFS健康检查脚本的编写逻辑keepalived本身只负责VIP漂移它不会自动判断NFS服务是否正常。我们必须通过track_script挂一个健康检查脚本让keepalived能感知NFS状态。推荐写一个实际挂载写入的探测脚本比单纯systemctl is-active可靠得多。因为systemctl只能说明NFS服务进程还活着不能说明它能正常提供读写服务有时候文件系统满了、目录被误删、网络路由异常服务进程依然是active。脚本路径比如/etc/keepalived/chk_nfs.sh#!/bin/bash VIP192.168.10.20 TEST_DIR/data/nfs-share/.keepalived_test if ! systemctl is-active --quiet nfs-server; then exit 1 fi if ! mkdir -p $TEST_DIR 2/dev/null; then exit 1 fi if echo test $TEST_DIR/probe_$$ 2/dev/null; then rm -f $TEST_DIR/probe_$$ exit 0 else exit 1 fi脚本检查两件事NFS服务是否在运行、当前节点是否真的能写入共享目录。然后把track_script写进配置文件track_script { chk_nfs }如果探测失败keepalived会逐步降低当前节点的优先级触发VIP漂移。脚本执行权限务必设置好sudo chmod x /etc/keepalived/chk_nfs.sh这里还有个容易踩的坑脚本里如果写了VIP地址去挂载在备节点上脚本应该永远失败因为备节点没有VIP挂载不上去。但我们在脚本里写的是检查本机/data/nfs-share目录的写权限而不是去挂载VIP路径这样主备节点都可以正确执行只是各自维护自己那份数据目录。这个设计在两节点各自有独立存储的场景下是合理的但如果你的备节点需要通过某种同步机制去访问主节点的数据情况就复杂得多需要在脚本里区分角色。4.3 keepalived exited with permanent error config 报错全排查配置完keepalived后启动服务最常见的报错信息就是Keepalived exited with permanent error config. See /var/log/syslog or /var/log/messages for details.这个报错本身只说了一句话真正的错误原因要看keepalived日志。我在Ubuntu 24.04上遇到时日志里给出的关键信息是Configuration problem: auth_pass length must be equal to 8很多人都栽在这里。auth_pass不是随便填的密码长度必须正好是8位。我一开始填的密码是自定义的9位字符串keepalived直接就认为配置非法根本不启动。另一个常见原因是virtual_ipaddress配置了不正确的CIDR格式。比如写成virtual_ipaddress { 192.168.10.20/32 dev eth0 }某些版本会把/32解析出问题。建议写成virtual_ipaddress { 192.168.10.20/24 dev eth0 }保持和网卡实际子网掩码一致。还有个大坑是配置文件的语法兼容性。网上很多教程是基于keepalived 1.x早期版本的写法而在Ubuntu 24.04上默认的keepalived是2.x两者对某些参数的处理有差异。比如global_defs里如果写了vrrp_garp_master_delay 1 vrrp_garp_master_repeat 2这些老参数在2.x里有的已经被废弃或改了位置直接启动就报permanent error。遇到这个报错不用慌按顺序排查先看真实日志tail -n 50 /var/log/syslog | grep KeepalivedUbuntu上日志位置是这个Rocky Linux上通常在/var/log/messages。用keepalived --config方式直接前台启动观察完整输出。重点检查auth_pass是否8位、virtual_router_id是否在0~255范围内、virtual_ipaddress的CIDR是否正确、是否有硬编码的老参数。配置改完后先keepalived -t -f /etc/keepalived/keepalived.conf做语法检查这一步能帮你提前发现90%的配置问题。4.4 启动keepalived后务必确认的几件事配置修好、服务顺利启动后光看到进程活着还不行。我用下面这套命令确认状态是否正常sudo systemctl status keepalived sudo ip addr show eth0 | grep 192.168.10.20 sudo journalctl -u keepalived -f正常情况下VIP应该出现在主节点的网卡上。然后从业务客户端尝试showmount -e 192.168.10.20能看到导出的目录说明VIP和NFS链路通了。在Rocky Linux 9上还要注意防火墙。很多环境装了keepalived和NFSVIP死活ping不通最后发现是firewalld挡了VRRP协议和NFS相关端口。sudo firewall-cmd --permanent --add-protocolvrrp sudo firewall-cmd --permanent --add-servicenfs sudo firewall-cmd --permanent --add-service{rpc-bind,mountd} sudo firewall-cmd --reloadUbuntu上如果启用了ufw类似操作sudo ufw allow proto 112 comment VRRP sudo ufw allow from 192.168.10.0/24 to any port nfs5. 故障演练实测VIP漂移、回切与数据一致性验证5.1 模拟NFS服务故障配置完成后不要急着让它空跑一定要做故障演练。我第一次做的时候备节点切过来虽然VIP漂移成功了但客户端写文件一直报stale file handle折腾了很久才明白是客户端缓存了旧的服务端文件句柄。第一步先做最温和的故障模拟停掉主节点的NFS服务。sudo systemctl stop nfs-server等一会儿大概是keepalived检测间隔通告间隔我配置的advert_int 1脚本每2秒执行一次然后看备节点ip addr show eth0 | grep 192.168.10.20如果看到VIP出现在备节点上说明自动切换成功。再从客户端验证挂载是否还正常mount | grep nfs df -h ls /mnt/nfs echo test /mnt/nfs/test_file如果客户端走的是VIP挂载这些命令应该顺利执行。这里的核心是客户端并没有重新挂载NFS自动恢复了数据路径前提是使用了hard挂载参数并等待足够的时间。5.2 模拟整机宕机服务停止测试只能验证NFS故障整机宕机更贴近真实严重事故。可以直接把主节点关机或者执行echo c /proc/sysrq-trigger强制崩溃让备节点彻底联系不上主节点。这种场景下切换会慢几秒因为keepalived要等主节点的心跳完全超时默认配置下大概是3到4秒。备节点抢占VIP后同样需要验证数据写入。由于整机是直接宕掉的主节点上如果有未落盘的写入备节点的数据可能比主节点旧一点这取决于主备数据同步机制。如果你的两节点使用各自独立的本地磁盘这种模式下必须有数据同步手段否则宕机就意味着部分最新数据丢失。这也是这个方案里大家最容易忽略的点。Keepalived保证了服务的连续性但并没有解决数据同步问题。我见过的生产落地方式大致有两种主备节点共用一套分布式存储或云盘只把NFS服务高可用化数据本身是共享的。主备节点各自本地磁盘用rsync、inotify、LSI或DRBD做实时同步。后者复杂度明显更高也不是keepalived本身能解决的。如果刚开始做建议优先考虑主备节点挂载同一个后端存储或者至少用实验室环境验证清楚数据同步机制再上线。5.3 回切到底该不该自动执行主节点恢复后keepalived的默认行为是因为主节点优先级更高150 100它会在心跳恢复后主动夺回VIP这就是回切。回切在生产上不一定是你想要的。举个例子主节点曾经宕机恢复后虽然keepalived起来了但NFS数据目录里可能有部分文件被备用节点的进程写入两边的数据状态并不完全一致。这时候自动回切可能让旧主节点继续服务反而覆盖掉备节点在接管期间产生的新数据。业界常见的做法是设置nopreempt让主节点恢复后不自动抢回VIP等运维人员确认两边数据一致后手动把VIP切回去。配置方式是在vrrp_instance里加一行nopreempt注意nopreempt通常配合state BACKUP使用让恢复后的原主节点处于等待状态避免抢占。这个参数需要仔细测试不同版本的keepalived行为略有差异。我在测试环境里的结论是对外提供服务的连续性比谁当主节点重要得多回切动作要慢、要可控最好由人来决策。6. 高可用架构里客户端侧和运维侧的隐形坑6.1 服务端高可用了客户端代码也要跟着改很多人做完keepalivedNFS以后就以为万事大吉结果切换的一瞬间后端应用还是报一堆NFS相关的错误。这里的根源在于NFS客户端有自己的状态缓存包括文件句柄、锁、inode信息等。主备切换后这些缓存可能失效。所以在服务高可用场景下后端代码对NFS的使用必须遵守几个基本原则对NFS路径的读写操作要写重试逻辑。一次失败不立即返回业务错误而是等待1到3秒重试几次这个时间窗口足够客户端恢复文件句柄。不要把文件锁长时间持有不放。NFS锁在故障切换时经常恢复不了长时间持有锁几乎必然导致所有写入都卡死。临时文件的命名要带唯一标识比如PID加时间戳避免多个实例写入同一路径时互相覆盖。上传文件的业务最好采用先写临时文件再rename到正式路径的模式即使中途失败也不会产生半截文件客户端重试时不会读到损坏的数据。我之前处理过一个知识库系统的故障切换本身只用了4秒但应用服务器因为持有NFS文件锁在切换后持续报错十几分钟最后重启应用才恢复。后来把锁的持有时间缩短、加入重试逻辑才彻底解决。6.2 文件锁和NFS stale handle的排查经验切换后客户端常见的报错是nfs: server 192.168.10.20 not responding, still trying这个不用慌多半是切换过程中VIP短暂失联客户端还在重试。保持hard参数耐心等待恢复即可。但如果出现Stale file handle问题就比较麻烦了。这个错误通常意味着客户端之前持有了旧的NFS文件句柄而这些句柄在新服务端上已经失效。解决办法是重新挂载NFS共享或者让进程重新打开路径没有别的捷径。在代码里表现为打开文件、读文件失败时不要无限重试同一个fd要主动关闭重新打开。6.3 日志监控与巡检清单这套架构上线以后日常运维不能只看keepalived进程还活着建议至少监控这几个维度NFS导出目录的磁盘使用率超过90%要告警因为sync模式下磁盘满了会直接影响写入。keepalived的VIP是否始终只存在一台机器上如果两台机器同时有VIP说明脑裂了需要立即介入。NFS服务的端到端探测最好从业务客户端发起真实写读测试而不是只看主机的端口存活。主备节点的数据一致性定期对比比如对固定目录做文件列表和校验和的抽样比对。监控手段上keepalived本身会输出状态日志可以用grep把关键事件抓出来。比如grep -E Transition to MASTER|Transition to BACKUP|Entering BACKUP /var/log/syslog这些日志能帮你复盘每一次切换的原因。6.4 从一个小集群到更大规模的一点思考这套keepalivedNFS高可用方案最适合的场景是双节点、中等规模、对数据一致性要求不是极高、预算有限的中小业务。比如内部知识库、CI/CD产物共享目录、日志聚合临时存储都很合适。如果业务量进一步增长或者对数据可靠性的要求上升到不能丢字节的层面就需要考虑分布式存储Ceph、GlusterFS或者专业NAS双活方案了。但即使将来要迁移这套keepalivedNFS方案里关于VIP、健康检查、客户端重试的很多经验依然通用理解它不会白费功夫。我在实际落地后的最大体会是高可用不是某一个软件的事而是一整套链路的设计和验证。keepalived负责让VIP听指挥NFS负责把数据可靠地交出去客户端负责在抖动时保持冷静运维负责在每次切换后复盘。环环相扣少了哪一个事故还是会在你最放松的时候找上门。
返回列表