1. 不选商业方案为什么偏偏凑齐KVM GFS1.1 先还原一个我亲身经历的场景不少团队走到虚拟化高可用这步第一反应是上商业超融合或者买一套集中式存储再配两台物理机做集群。预算充足当然没问题但如果你在中小型机房、分支机构或者做实验环境大概率卡在预算审核和审批周期上。我那次遇到的情况是机房里十来台业务虚拟机跑在单台KVM宿主机上半夜一块磁盘报废整个业务线直接停摆等配件加恢复系统花了将近两天。事后复盘结论很简单——虚拟化层做得再好宿主机单点就是致命短板。然后我开始研究开源方案的可行性。KVM本身是Linux内核自带的虚拟化模块性能损耗小命令行生态成熟存储方面GFS这里说的是GlusterFS不是谷歌那个GFS论文中的分布式文件系统它在用户态跑部署简单不需要单独的内核模块横向扩展能力也不错。把这两者组合起来再配一套集群资源管理器就能构建一个完整的虚拟机高可用环境。1.2 高可用方案里最容易被忽略的核心共享存储做虚拟机高可用的关键是当一台宿主机挂了另一台宿主机要能立即接管虚拟机继续运行。但接管的前提是什么是虚拟机磁盘文件在第二台宿主机上也能访问到。如果磁盘文件只存在于故障节点的本地硬盘里第二台宿主机连文件都看不到根本无从谈接管。所以方案设计的核心就变成三件事第一件事把虚拟机磁盘文件放到共享存储上两个节点都能访问第二件事用集群软件监控各节点的存活状态心跳断了就触发切换第三件事接管时要保证存储侧的数据一致性不能在两个节点同时写同一份磁盘镜像否则文件系统会损坏。KVM负责虚拟化计算GlusterFS负责共享存储Pacemaker Corosync负责节点监控和资源调度三者搭起来就是一条非常清晰的逻辑链。很多教程喜欢直接甩命令我觉得理解这条逻辑链比命令本身重要——后边的每一步配置都是在落实这三件事。1.3 GFS和KVM的契合点在哪有人会问选GFS而不是其他分布式存储核心理由是什么就我实验和后来生产环境的体会来说部署轻。GlusterFS服务端用yum或者apt装个包起一个glusterd服务然后命令行创建卷全程不需要格式化和挂载底层文件系统数据都以文件形式存放在各节点的普通目录中排错思路非常直观。原生支持KVM镜像。KVM的磁盘镜像既可以走网络块设备协议也可以直接放在挂载了GlusterFS文件系统的目录里。实验中我选择把卷挂载到节点上的固定目录然后把虚拟机的XML定义文件也放进去两节点就能读到完全一致的虚拟机配置。卷类型灵活。两节点环境可以用replica卷做数据冗余如果担心脑裂时数据不一致还能加arbiter机制做仲裁。实验阶段配置成本很低跑通后再往生产迁移心里就有底。当然GFS也有短板比如对高并发小文件的随机读写性能不如专业SAN阵列但这部分我会在第6节展开讲避免你踩到PT测试的坑。有了上面这些认知铺垫下面开始搭建。2. 实验环境准备三张业务网络怎么接、存储盘怎么分2.1 节点角色与硬件分配我建议用三台物理机来搭这套实验环境两两之间有区分度又不至于太复杂。如果条件有限两台也能跑通但少一个场景验证位。下面是我实验环境的清单节点角色硬件配置硬盘规划node1KVM计算节点 GFS存储节点4核CPU / 16GB内存 / 2块1TB SATA系统盘用一块另一块做brick目录node2KVM计算节点 GFS存储节点同node1同node1node3仲裁或备用监控节点2核CPU / 4GB内存 / 1块磁盘系统盘即可前两个节点承担虚拟机的计算与存储第三个节点在实验初期可以先不做GFS的brick而是用来观察集群状态或者模拟第三方仲裁。我自己实验时node3还兼任了客户端专门用来挂载卷验证读写。这样一台机器干三个审计的活资源利用率更高。2.2 网络规划比选硬件更关键高可用集群最怕的就是心跳网络抖动所以网络规划时必须把流量分开。我分了三个平面管理网用于SSH登录、系统运维。我用了192.168.10.0/24网段。业务网承载虚拟机对外提供服务的网络也是KVM默认虚拟网桥接的流量。我用了192.168.20.0/24网段。存储网跑GFS的复制流量和客户端的读写流量。我单独用了192.168.99.0/24网段并且这段网络用物理隔离交换机不跟业务网混在一起。有条件的存储网建议上万兆或者至少双千兆绑定。为什么因为虚拟机在做快照、克隆、在线迁移的时候存储流量会瞬间飙高一旦存储网拥塞整个集群的健康检查都会受影响。实验环境至少也要单独拉一根千兆网线不要图省事把存储流量放在管理网里跑。上述三个网段在三台节点上都要配好交换机端口做VLAN划分。配置完以后在每个节点上ping另外两个节点的三个IP确保全通。2.3 系统初始化里容易忽略的几个细节操作系统我这里选Rocky Linux 8系列和CentOS兼容KVM和GFS的软件包都能直接装。装系统时特别注意两点用最小化安装不要装带GUI的版本图形界面会多占用内存不说还会多出一堆用不到的组件。/etc/hosts一定要写主机名和IP的对应关系。Pacemaker和GlusterFS对主机名解析非常敏感解析不到会有各种诡异问题后边排错会非常痛苦。我直接在三个节点执行cat /etc/hosts EOF 192.168.10.11 node1 192.168.10.12 node2 192.168.10.13 node3 EOF接下来关闭防火墙和SELinux。生产环境不建议这么干但实验环境这么操作能大幅减少干扰变量systemctl disable --now firewalld setenforce 0 sed -i s/^SELINUXenforcing/SELINUXdisabled/ /etc/selinux/config时钟同步也别忘了NTP不同步的话后边Pacemaker的quorum计算和日志时间线都不可靠。装完chrony配置好时间源三个节点时间误差控制在1秒以内。3. 核心组件部署从存储卷到虚拟化再到集群栈3.1 GlusterFS存储集群的搭建与验证三个节点的系统都准备好后正式开始装GFS。每台节点执行yum install -y centos-release-gluster yum install -y glusterfs-server systemctl enable --now glusterd然后选任意一个节点把另外两台加入信任池gluster peer probe node2 gluster peer probe node3 gluster pool list执行gluster pool list能列出三个节点说明信任池建好了。接下来创建brick目录。brick就是GFS存储数据的基本单元我建议把第二块数据盘格式化后单独挂载不要放在系统根分区里否则数据增长会把系统盘占满mkfs.xfs /dev/sdb1 mkdir -p /data/brick1 mount /dev/sdb1 /data/brick1 echo /dev/sdb1 /data/brick1 xfs defaults 0 0 /etc/fstab三个节点都准备好以后创建复制卷。这是整个存储层面的核心步骤gluster volume create gv0 replica 3 \ node1:/data/brick1/gv0 \ node2:/data/brick1/gv0 \ node3:/data/brick1/gv0 gluster volume start gv0 gluster volume info gv0这里replica 3的含义是每个文件在三台上各保留一份副本任意一台宕机数据仍可从另外两台读取。如果只有两台机器可以改成replica 2但要注意仲裁问题两副本在脑裂时很难自动决定谁的数据新这也是我给中间节点配置存储角色的原因三副本在实验阶段省心很多。验证方式不复杂在任意节点用FUSE方式挂载写入文件后到另外两个节点的brick目录里看看有没有同样文件出现mount -t glusterfs node1:/gv0 /mnt for i in $(seq 1 100); do echo test$i /mnt/file$i; done ls /data/brick1/gv0/ | wc -l数到100个文件说明复制正常。之后把FUSE挂载写入开机自启或者干脆不做宿主机挂载依赖后边KVM层直接通过glusterfs协议读写这点在第3.3节配置时会说清楚。3.2 KVM虚拟化层安装GFS存储就绪后装KVM。node1和node2都需要安装它们都承担计算角色yum install -y qemu-kvm libvirt virt-install bridge-utils systemctl enable --now libvirtd确认KVM模块加载正常lsmod | grep kvm能看到kvm_intel或者kvm_amd就算正常。然后创建虚拟机网桥编辑/etc/libvirt/qemu/networks/vmnet.xml把桥接网络配成192.168.20.0/24网段启动NAT网络。实验环境用NAT即可生产环境建议直通物理网卡。创建一台带两块网卡一块管理用一块业务用的测试虚拟机模板。为了验证高可用接管建议在虚拟机里装好Linux系统然后关闭虚拟机把它的磁盘镜像拷贝到GFS卷里。注意拷的时候要把镜像的所有权改成qemu用户否则libvirt会报权限错误cp /var/lib/libvirt/images/testvm.qcow2 /mnt/vmimages/ chown -R qemu:qemu /mnt/vmimages/从这一步开始虚拟机的磁盘镜像和XML定义都交由GFS共享目录来管理KVM节点本身只提供CPU和内存资源。3.3 Pacemaker Corosync集群栈安装接下来是在node1和node2上装集群管理组件这是CPU计算以外另一块核心拼图yum install -y pacemaker corosync pcs fence-agents-all systemctl enable --now pcsdpcsd是Pacemaker的配置守护进程装好后用passwd hacluster给系统默认的hacluster管理账号设置密码。然后任意一个节点执行pcs host auth node1 node2 -u hacluster -p 你的密码 pcs cluster setup mycluster node1 node2 pcs cluster start --all pcs cluster status看到两节点Online说明集群通信正常。Corosync默认走的端口是5405注意别被防火墙挡掉。刚才关闭了防火墙所以这一步比较顺。Pacemaker初始会启动两台节点的所有资源暂时看不到任何资源定义所以要给它添加CIB配置项。这里涉及到核心的高可用逻辑我在第4节专门展开先不做解释。4. 高可用编排集群状态监控、资源约束与Fencing隔离4.1 虚拟机作为集群资源注册进去Pacemaker管理资源的方式是通过资源代理来执行的。KVM虚拟机的资源代理在pacemaker里叫VirtualDomain它会调用libvirt接口来启动、停止、监控虚拟机。首先要保证两台宿主机都能通过libvirt访问同一份虚拟机定义。把测试虚拟机的XML文件导出到GFS共享目录virsh dumpxml testvm /mnt/vmimages/testvm.xml然后在两个节点把libvirt默认的连接URI改成读该目录下的配置。这一步非常关键因为Pacemaker的VirtualDomain资源代理需要指定一个config参数告诉它XML文件在哪。我在两个节点都执行pcs resource create vm_test VirtualDomain \ config/mnt/vmimages/testvm.xml \ migration_transportssh \ meta migration-threshold3 \ op start interval0s timeout120s \ op stop interval0s timeout120s \ op monitor interval30s timeout60s创建成功后Pacemaker会自动把资源调度到某个节点去跑虚拟机启动的那一刻你就完成了从纯KVM单机到集群接管的第一步。migration_transportssh是给在线迁移用的这里先加上无妨。monitor操作是Pacemaker每30秒检查一次虚拟机是否存活判断机制是通过libvirt的API查询域状态而不是简单地ping虚拟机IP所以虚拟机内部即使网络卡死只要QEMU进程还在就不算故障。这点在挑选监视策略时要心里有数。4.2 约束规则宁可慢不可争Pacemaker默认状态下多个资源之间没有严格的摆放关系。如果你的环境里只有这一个虚拟机约束还不明显但当你加上存储挂载资源、VIP资源、多个虚拟机之后没有约束就会乱套。我给这个实验配置了两个最核心的约束# 顺序约束先确保存储卷挂载成功再启动虚拟机 pcs constraint order storage-vol then vm_test # 位置约束让存储资源和虚拟机尽量在同一节点 pcs constraint colocation add vm_test storage-vol INFINITY顺序约束解决依赖关系位置约束解决亲和性。INFINITY的含义是只有storage-vol在某节点成功激活vm_test才可能被调度到该节点。这样避免了一类典型故障存储没挂上虚拟机启动时打开磁盘镜像失败报一堆I/O错误。多虚拟机的场景下还需要考虑资源互斥。比如两套业务虚拟机不希望跑在同一台物理机上可以在约束里加负向colocation把INFINITY改成-INFINITY。实验环境中抓住两条主链先存储后虚拟机、存储和虚拟机不分离就能应付绝大多数情况。4.3 Fencing隔离高可用的最后一道保险丝很多第一次接触Pacemaker的人看到fencing会问为什么还要故意把一台物理机关掉原因在于集群脑裂。假设node1和node2之间的心跳网断了两个节点都认为自己活着、对方死了于是同时尝试启动同一个虚拟机。这时虚拟机磁盘镜像如果同时被两个QEMU进程打开并写入数据就完蛋了。Fencing就是用来解决这个问题的发现心跳异常后先确保故障节点彻底失去对外提供服务的能力再允许其他节点接管。主流方式是IPMI或网络交换机控制但实验环境里这两样未必都有。我用的是fence_sleep它模拟一种简易隔离手段在故障节点上强制睡眠断流pcs stonith create my_fence fence_sleep \ delay10 \ op start interval0s timeout60s \ op monitor interval120s timeout60s实验可以这么玩但生产环境必须改成真实可用的fence设备比如IPMI的fence_ipmilan。我不止一次看到有人把STONITH设为disabled觉得反正我的心跳网络很稳。结果全网抖了几秒双节点同时拉起同一台虚拟机镜像损坏数据恢复无门。Fencing配置不复杂真正难的是让非技术决策者理解它的价值。5. 故障演练实测从优雅关机到直接断电系统如何响应5.1 场景A宿主机优雅宕机高可用不是说配置完就完事必须反复演练才能验证逻辑正确。我第一次做故障切换测试时选择从node1优雅关机开始。先在node1上确认当前vm_test跑在哪个节点pcs status看到vm_test当前在node1上。然后执行shutdown -h now模拟计划内维护。等待大约30秒后回到node2上看状态。正常情况下Pacemaker检测到node1离线先在node1上不能执行任何操作于是直接跳过STONITH把资源调度到node2启动。虚拟机启动时间取决于磁盘大小和系统初始化速度我用的qcow2镜像大约3分钟完全拉起IP。在虚拟机内部确认业务服务正常curl http://192.168.20.50/ ping -c 3 192.168.20.50一切正常。这次切换的本质原因是Pacemaker通过Corosync心跳感知节点离线从而将资源标记为不可用再按约束规则重新调度。优雅关机不会触发STONITH因为corosync能确认节点是主动退出的。5.2 场景B强制断电模拟真故障优雅关机验证的是正常切换流程但生产事故可不会提前打招呼。接下来在node1上执行echo c /proc/sysrq-trigger模拟内核崩溃或者直接拔掉电源线。拔电源比任何软件模拟都真实因为心跳、存储、业务流量瞬间全断。插回电源后观察node2状态看到vm_test已经被接管虚拟机能正常访问。随后恢复node1供电并让系统自行启动启动完成后node1会以备用节点身份重新回集群不会抢占VM资源。这里有个细节值得注意即使在强制断电场景里虚拟机内的数据也没损坏。原因是testvm在创建时磁盘驱动用virtio文件系统日志靠的是快照层的顺序写。GFS侧三副本中至少有一个节点是完整的即使某个brick的实时写入滞后也能从另外一个副本恢复。但如果切换过程中出现两个节点同时写同一镜像那就是Fencing没起作用数据必损。5.3 数据一致性验证故障切换后第一件事必须验证数据而不是急着继续测别的。我用的方法是在虚拟机里写入一个带时间戳的测试文件然后检查它在三个brick上的副本是否一致# 虚拟机上执行 echo failover-test-$(date %s) /data/testfile # node1/node2的brick目录分别执行 cat /data/brick1/gv0/vmimages/testvm.qcow2 的同目录文件更严格的做法是用checksum对比md5sum /mnt/vmimages/testfile三台节点的结果必须完全一致。这里我踩过一个坑由于写入缓存和flush策略个别brick上的文件可能短暂滞后。GFS的replica卷是同步复制理论上每个写请求要等所有副本ACK才算完成但某些配置下性能优化会导致部分异步行为。所以验证数据一致性时一定要等IO稳定后再比对。6. 实验中的配置盲区与长期维护建议6.1 最容易踩的坑avahi和主机名解析我在第一次配置时明明/etc/hosts都写好了Pacemaker和GFS却总是出现节点连接失败的问题。后来翻文档发现是avahi服务在干扰。avahi是多播DNS服务会自动注册主机名和静态hosts配置冲突导致集群节点间解析到两个不同IP。解决办法很直接systemctl disable --now avahi-daemon另外如果有人习惯用IP地址而不是主机名来执行gluster peer probe也会在后续rebalance时出问题。GFS和Pacemaker都应该统一用主机名不要混用IP。6.2 性能盲区不要把重度数据库丢进这个方案文章开头提到GFS适合中小型环境这不代表所有工作负载都适合。虚拟机里跑轻量级Web服务、测试环境、CI构建机都没问题但如果要跑生产MySQL尤其是随机写入密集的业务GFS的同步复制机制会让每次写入性能大幅下降。我做过一个简单对比单机KVM本地磁盘跑MySQL的IO性能和通过GFS同步复制卷跑同样负载随机写TPS大概下降50%到60%。这不是说GFS烂而是它的设计偏向大文件顺序读写数据库这种大量小页随机写入的场景正好命中短板。如果业务对存储性能要求高可以考虑把数据库放在节点本地盘再配合逻辑备份和Pacemaker的资源监控做处理或者采购带企业级缓存的软件定义存储。6.3 监控体系与切换演练周期高可用集群配置完只是起点长期维护才是重头。我给自己定的纪律是每三个月做一次完整的断电演练记录从断电到业务恢复的总耗时看是否在可接受范围内。每天定时巡检GFS卷的健康状态gluster volume status里如果出现脱机节点立刻排查。每两周检查一次pcs状态和日志重点看有没有资源频繁重启记录。如果环境里有多台虚拟机建议给每个虚拟机配置独立的监控间隔虚拟机太密集会造成Pacemaker的monitor请求过载反而导致误判。实测下来单台宿主机上的虚拟机数量建议控制在5台以内每个虚拟机的磁盘镜像单独放在一个子目录里不要全堆在根目录这样未来做快照和恢复都方便。6.4 一点仓库级别的备份思维GFS三副本不等于备份。如果有人误执行了删除命令删除操作会同步到所有副本文件就真的没了。所以在GFS卷之外还要定期做虚拟机的离线快照或镜像档的异地拷贝。我习惯每周日凌晨在业务低峰期做一次qemu快照快照文件放在另一台物理盘的独立目录里而不是放在同一个GFS卷内。毕竟高可用解决的是宕机问题误删和逻辑损坏需要另一层保障。这套KVM加GFS加Pacemaker的组合从实验到小规模生产最大的价值在于把虚拟机的停机时间从小时级降到分钟级而且全程用的都是开源组件没有授权费的压力。我在实际维护中体会最深的一点是配置命令全记住并不重要更重要的是把边界画清楚——存储负责数据可用集群负责计算调度虚拟化层负责隔离故障域。只要这条逻辑链理清了遇到问题顺着节点状态、存储状态、集群状态逐层排查基本都能快速定位。最后再分享一个小技巧每次改完Pacemaker配置先执行pcs config show存个档再执行crm_mon -A观察资源变化这比直接改完就跑虚拟机要稳得多。