☰
基于KVM+GlusterFS+Pacemaker的开源虚拟化高可用方案实践
2026/10/2 14:09:52 网站建设 项目流程

1. 不选商业方案,为什么偏偏凑齐KVM + GFS

1.1 先还原一个我亲身经历的场景

不少团队走到虚拟化高可用这步,第一反应是上商业超融合,或者买一套集中式存储,再配两台物理机做集群。预算充足当然没问题,但如果你在中小型机房、分支机构,或者做实验环境,大概率卡在预算审核和审批周期上。我那次遇到的情况是:机房里十来台业务虚拟机跑在单台KVM宿主机上,半夜一块磁盘报废,整个业务线直接停摆,等配件加恢复系统花了将近两天。事后复盘结论很简单——虚拟化层做得再好,宿主机单点就是致命短板。

然后我开始研究开源方案的可行性。KVM本身是Linux内核自带的虚拟化模块,性能损耗小,命令行生态成熟;存储方面,GFS(这里说的是GlusterFS,不是谷歌那个GFS论文中的分布式文件系统)它在用户态跑,部署简单,不需要单独的内核模块,横向扩展能力也不错。把这两者组合起来,再配一套集群资源管理器,就能构建一个完整的虚拟机高可用环境。

1.2 高可用方案里最容易被忽略的核心:共享存储

做虚拟机高可用的关键是:当一台宿主机挂了,另一台宿主机要能立即接管虚拟机继续运行。但接管的前提是什么?是虚拟机磁盘文件在第二台宿主机上也能访问到。如果磁盘文件只存在于故障节点的本地硬盘里,第二台宿主机连文件都看不到,根本无从谈接管。

所以方案设计的核心就变成三件事:

  • 第一件事,把虚拟机磁盘文件放到共享存储上,两个节点都能访问;
  • 第二件事,用集群软件监控各节点的存活状态,心跳断了就触发切换;
  • 第三件事,接管时要保证存储侧的数据一致性,不能在两个节点同时写同一份磁盘镜像,否则文件系统会损坏。

KVM负责虚拟化计算,GlusterFS负责共享存储,Pacemaker + Corosync负责节点监控和资源调度,三者搭起来就是一条非常清晰的逻辑链。很多教程喜欢直接甩命令,我觉得理解这条逻辑链比命令本身重要——后边的每一步配置都是在落实这三件事。

1.3 GFS和KVM的契合点在哪

有人会问,选GFS而不是其他分布式存储,核心理由是什么?就我实验和后来生产环境的体会来说:

  1. 部署轻。GlusterFS服务端用yum或者apt装个包,起一个glusterd服务,然后命令行创建卷,全程不需要格式化和挂载底层文件系统,数据都以文件形式存放在各节点的普通目录中,排错思路非常直观。

  2. 原生支持KVM镜像。KVM的磁盘镜像既可以走网络块设备协议,也可以直接放在挂载了GlusterFS文件系统的目录里。实验中我选择把卷挂载到节点上的固定目录,然后把虚拟机的XML定义文件也放进去,两节点就能读到完全一致的虚拟机配置。

  3. 卷类型灵活。两节点环境可以用replica卷做数据冗余,如果担心脑裂时数据不一致,还能加arbiter机制做仲裁。实验阶段配置成本很低,跑通后再往生产迁移心里就有底。

当然,GFS也有短板,比如对高并发小文件的随机读写性能不如专业SAN阵列,但这部分我会在第6节展开讲,避免你踩到PT测试的坑。有了上面这些认知铺垫,下面开始搭建。

2. 实验环境准备:三张业务网络怎么接、存储盘怎么分

2.1 节点角色与硬件分配

我建议用三台物理机来搭这套实验环境,两两之间有区分度,又不至于太复杂。如果条件有限,两台也能跑通,但少一个场景验证位。下面是我实验环境的清单:

节点角色硬件配置硬盘规划
node1KVM计算节点 + GFS存储节点4核CPU / 16GB内存 / 2块1TB SATA系统盘用一块,另一块做brick目录
node2KVM计算节点 + GFS存储节点同node1同node1
node3仲裁或备用监控节点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的软件包都能直接装。装系统时特别注意两点:

  1. 用最小化安装,不要装带GUI的版本,图形界面会多占用内存不说,还会多出一堆用不到的组件。
  2. /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/^SELINUX=enforcing/SELINUX=disabled/' /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 pcsd

pcsd是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_transport=ssh \ meta migration-threshold=3 \ op start interval=0s timeout=120s \ op stop interval=0s timeout=120s \ op monitor interval=30s timeout=60s

创建成功后Pacemaker会自动把资源调度到某个节点去跑,虚拟机启动的那一刻,你就完成了从纯KVM单机到集群接管的第一步。

migration_transport=ssh是给在线迁移用的,这里先加上无妨。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 \ delay=10 \ op start interval=0s timeout=60s \ op monitor interval=120s timeout=60s

实验可以这么玩,但生产环境必须改成真实可用的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观察资源变化,这比直接改完就跑虚拟机要稳得多。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询