资讯动态

ARM虚拟化实战:鲲鹏+openEuler上KVM部署与优化

发布时间:2026/10/17 1:27:28 来源:尧图企业网站定制
1. 为什么我选了鲲鹏Arm64 openEuler 这套组合学虚拟化说实话前几年我一直是在x86平台上折腾虚拟化KVM、QEMU、libvirt这些工具链闭着眼睛都能配好。但最近一年身边的项目越来越频繁地提到ARM服务器尤其是鲲鹏这类国产Arm64芯片配套的操作系统也在往openEuler上靠。一开始我多少有点换汤不换药的心态觉得虚拟化无非就是CPU、内存、磁盘、网络那几件事换个架构能有多大差别。真正上手之后才发现这套组合从安装引导到性能调优每一个环节都有和x86完全不同的脾气踩坑无数但也确实值得好好学一遍。1.1 从x86迁移到ARM虚拟化学习为什么非换不可先说个大背景。虚拟化技术本身是分层的硬件提供虚拟化扩展Hypervisor比如KVM负责接管和调度客户机操作系统跑在虚拟CPU上。x86下的KVM依赖Intel VT-x或者AMD-V这两类指令扩展而ARM平台走的是另一个体系叫Arm Virtualization Extensions核心是提供EL2Exception Level 2特权级别。也就是说KVM在ARM上的运作方式、中断处理、内存映射方式都和x86有本质区别。如果不亲手在ARM上装一次KVM你永远只会用Intel模式的思维去理解虚拟化。但我发现很多人会问学虚拟化不是有一台普通PC就行了吗当然可以x86上装KVM、VirtualBox也能学到不少东西。可如果你把视野放到国产替代、信创适配这个方向上鲲鹏Arm64 openEuler基本是绕不开的主流组合。与其等项目真正来了才恶补不如自己先搭一套环境摸清底细。从技术角度看ARM虚拟化还有一个让x86玩家很新鲜的特点ARMv8架构的虚拟化支持比x86更偏向于硬件辅助到底。例如客户机页表的维护、虚拟中断GICGeneric Interrupt Controller的路由在ARM上都更依赖硬件完成。这意味着你在调优时需要关注的参数、设计的网络拓扑、选择的设备模型都要重新学一遍。1.2 openEuler在ARM生态里的作用到底是什么openEuler你可能听说过它是面向服务器场景的Linux发行版在鲲鹏平台上属于亲儿子级别的适配。很多人在学习时有个误区我随便装一个Ubuntu ARM版不也能跑KVM吗当然能但openEuler在鲲鹏上有几个明显的优势内核版本带来的KVM支持更完整很多针对鲲鹏芯片的补丁和优化会直接进到openEuler内核里另外openEuler自带的工具链对KVM、容器、虚拟化周边组件的集成度也高用起来顺手。我个人的学习路线是宿主机用openEuler 22.03 LTS客户机也用openEuler这样双端同源排查问题时干扰最少。你完全可以在宿主机上跑Ubuntu ARM版、客户机跑openEuler但第一轮学习不建议这么干因为你很难分辨是内核的差异还是配置的问题。等把openEuler这套跑通之后再去做跨发行版的测试会更省时间。1.3 硬件与网络准备物理机方案和纯模拟方案怎么选做ARM虚拟化实验硬件方案很关键。财力允许的话弄一台鲲鹏服务器主机是最直接的CPU核数多、内存大跑虚拟机很舒服。但大多数个人学习者没有这个条件这时候可以走两条路一是买一块Arm64的开发板/迷你主机性能和真实服务器有差距但指令集是一致的学原理完全够用二是用QEMU在x86机器上模拟aarch64架构速度慢但那也是真ARM环境。这里我不建议上来就搞模拟环境因为它对虚拟化嵌套的支持更脆弱很多问题会被模拟层掩盖。我自己用的是二手Arm64开发板配了32GB内存四个大核跑KVM虽说不算快但跑两三个测试虚拟机没问题。网络方面建议准备一个独立的交换机或者用网线直连虚拟机的桥接网络会用到。2. 宿主机环境从零搭建那些网上教程不会细说的细节这章直接给实操。我以openEuler 22.03 LTS为基准来说明命令在21.03或更新的LTS版本上基本通用。装系统这步看似简单但在Arm64平台上有几个和x86完全不同的注意事项先把这些说清楚能少走一半弯路。2.1 安装openEuler时容易被忽略的引导方式和分区选择openEuler在Arm64上的安装镜像分为两种格式一种是标准的ISO适合光驱或虚拟光驱安装另一种是带UEFI引导的qcow2镜像可以直接用做云主机镜像。初次学习我建议用ISO手动装一遍整个过程能让你熟悉固件层面的差异。安装时最容易踩的坑是引导方式。x86安装时大家习惯用Legacy BIOS或者UEFI都可以但Arm64平台没有Legacy BIOS它只能走UEFI引导。如果你的板子或者固件设置不对装完系统会卡在引导阶段屏幕上只显示一个grub提示符没有任何引导项。这时候别急着怀疑系统坏了先去固件里看启动项设置然后确认分区表是GPT且存在ESP分区大小建议256MB以上。分区方案我用了两个分区ESP分区挂到 /boot/efi剩下的全部作为根分区。这套简单方案在虚拟化宿主机上够用。需要注意的是别把 /boot 和根分区合在同一个普通分区里因为Arm64的引导流程对ESP依赖更强分开更稳。2.2 验证虚拟化能力和安装KVM软件栈的标准步骤系统装好后先别急着装虚拟化软件第一步是验证CPU虚拟化扩展是否真的被KVM识别。在x86上我们会去grep /proc/cpuinfo里面的vmx或svm在ARM上则是检查ARMv8的虚拟化扩展更直观的方法是看看内核模块加载状态。ls /dev/kvm如果这个设备文件存在说明KVM模块已经加载。没有的话先执行modprobe kvmArm64的KVM模块是统一的不像x86那样分kvm_intel和kvm_amd。如果modprobe之后还是找不到/dev/kvm很可能是内核编译时没有打开KVM支持需要重新编译内核或者换一个内核版本。我用openEuler默认内核没遇到这个问题但如果你用的是编译自订内核的发行版就得注意这一点。然后安装虚拟化管理组件dnf install -y qemu-kvm libvirt virt-install virt-manager bridge-utils装完后把libvirtd服务拉起来并设置开机自启systemctl enable --now libvirtd可以用virsh来验证virsh version virsh list --all2.3 为什么要在宿主机上单独建桥接网络学习虚拟化时很多人图省事直接用NAT网络虚拟机只要能联网就觉得万事大吉。但如果想深入学习网络虚拟化尤其是后面要做的性能对比、流量隔离测试NAT会带来很多干扰。我建议直接建一个Linux网桥让虚拟机和宿主机处于同一个二层网络。下面是在openEuler上的操作方法。先把物理网卡配置成bridge从设备编辑 /etc/sysconfig/network-scripts/ 下的配置文件。openEuler默认使用NetworkManager需要停掉或者改成通过配置文件接管nmcli con add type bridge ifname br0 con-name br0 nmcli con add type ethernet ifname enp1s0 con-name enp1s0 nmcli con modify enp1s0 master br0 nmcli con up br0创建完成后用 brctl show 确认桥接状态。有了br0之后后续创建虚拟机时网卡直接挂到br0上虚拟机之间以及虚拟机与宿主机之间就是纯二层互通很多抓包分析工具能直接用对理解虚拟交换机原理特别有帮助。3. 制作Arm64虚拟机镜像的完整流程有了宿主机之后下一步就是装客户机。这一步看似简单但我发现绝大多数新手都会卡在没有对应的Arm64安装镜像或者镜像引导失败这类问题上。这一章我按我自己的实操路径来写把两种方案都覆盖到。3.1 手动安装一台openEuler客户机的关键交互参数如果只是学习用手动安装一台虚拟机就够了。用virt-install命令创建时有几个参数在ARM平台尤其关键virt-install \ --name openeuler-vm1 \ --memory 4096 \ --vcpus 4 \ --disk path/var/lib/libvirt/images/openeuler-vm1.qcow2,size50,formatqcow2,busvirtio \ --network bridgebr0,modelvirtio \ --cdrom /path/to/openEuler-aarch64-dvd.iso \ --os-variant openeuler-unknown \ --boot uefi这里注意两点--boot uefi 必须显式指定因为Arm64平台没有BIOS引导模式不指定的话libvirt会默认尝试去找x86的SeaBIOS直接报错。另外 --os-variant 如果识别不了 openEuler可以用 --os-variant openeuler-unknown 或者 centos-stream9不会影响安装只影响系统参数的匹配度。安装过程中的交互界面跟x86版本差不多也是选择语言-设置时区-分区-设置root密码这一套。分区那里建议用自动分区因为KVM虚拟磁盘很小手动分反而容易出问题。根文件系统我用ext4为主学习阶段用xfs也没毛病两者在ARM下都有完整驱动支持。装完之后重启进系统第一件事是确认内核架构uname -m输出应该是aarch64不是armv8l或者arm64之类五花八门的写法。如果这里输出aarch64那说明整个链路是干净的真ARM环境。3.2 virtio驱动模型为什么在ARM虚拟化里更重要安装界面上可能不会提示你选择磁盘和网卡模型virt-install默认已经用了virtio。在x86平台我们经常能看到默认的SATA控制器或e1000网卡因为兼容性好但在ARM平台virtio不仅是性能更快的问题更是兼容性和稳定性的关键。原因在于ARM的KVM虚拟化对virtio的依赖度更高。ARM平台上模拟e1000这种老设备需要额外翻译许多中断和I/O操作虽然也能跑但每次网络中断都会在GIC这一层产生额外的开销。而virtio是半虚拟化设备客户机知道自己是虚拟机通过共享队列直接跟宿主机通信不需要模仿真实硬件的所有细节。实测下来同一个虚拟机在virtio网卡下跑iperf3带宽能到接近线速而e1000基本只能跑一半。磁盘同理virtio-blk的性能和稳定性都比SATA模拟好很多。首次安装完系统之后我强烈建议检查一下客户机的设备列表确认所有设备都是用virtio模型否则后续做性能测试的时候还真说不准瓶颈在哪。3.3 无人值守安装让镜像制作进入批量可复制的阶段手动安装适合第一台虚拟机但做实验或者测试集群的时候一台台去点安装界面效率太低。openEuler官方和社区都提供了automatic installation的机制本质上是kickstart文件。在Arm64平台上写法跟x86大体一样但也有一个明显区别bootloader部分要指定UEFI相关的引导程序。一个精简的ks.cfg大致长这样%packages base virtualization qemu-guest-agent %end %post systemctl enable --now qemu-guest-agent %end clearpart --none part /boot/efi --fstypeefi --size512 part / --fstypeext4 --grow这里最容易被忽略的是第一个分区必须是EFI分区而且文件系统类型要写成efi。如果漏掉了ESP分区系统装完重启时会卡在固件引导阶段。很多人把x86的ks.cfg直接拿过来改删掉BIOS的grub配置却没补上EFI分区然后就各种引导不了。无人值守安装的好处是你可以把ks.cfg放到HTTP服务器上virt-install的时候通过 --location 和 --extra-args 指定virt-install \ --location http://192.168.x.x/openEuler-install/ \ --extra-args inst.kshttp://192.168.x.x/ks.cfg consolettyS0这样就可以批量创建同一配置的虚拟机。我学习的时候用这个方法一口气建了三台一模一样的测试机后面做集群实验时就能专注研究虚拟化本身而不用反复装系统。4. 虚拟机日常管理virsh命令、XML配置和快照的实战用法环境搭好之后真正的学习才刚刚开始。日常管理虚拟机的工具有virsh、virt-manager图形界面以及OpenStack这类更上层的管理平台。这一章不聊OpenStack只把libvirt这一层讲透因为它是所有上层平台的基础。4.1 virsh常用操作与资源定义的字节级陷阱virsh是libvirt的命令行前端创建、启动、关闭、删除虚拟机都能干。我列一份自己常用的命令清单配合解释比只列命令更实用virsh list --all # 查看所有虚拟机包括已关闭的 virsh start openeuler-vm1 # 启动虚拟机 virsh shutdown openeuler-vm1 # 优雅关机需要客户机装ACPI驱动 virsh destroy openeuler-vm1 # 强制断电相当于拔电源 virsh suspend openeuler-vm1 # 暂停 virsh resume openeuler-vm1 # 恢复 virsh vcpuinfo openeuler-vm1 # 查看vCPU映射状态 virsh dommemstat openeuler-vm1 # 查看内存统计 virsh dumpxml openeuler-vm1 # 导出完整XML配置如果在x86上用过virsh shutdown且好用到了ARM上可能会发现执行shutdown没反应。原因通常不是系统没装好而是客户机缺乏ACPI电源管理支持。在ARM虚拟化里要让客户机响应shutdown必须在启动命令里加上ACPI控制器virsh edit openeuler-vm1在XML里找到如下片段并确认存在controller typeacpi index0/如果没有这个controller加进去再重启虚拟机。这是ARM平台特别容易漏掉的一个配置因为很多骑士教程是从x86直接搬过来的根本不会提ACPI在ARM KVM中的重要性。配置里的内存单位也值得一提。libvirt的XML里默认以KiB为单位如果写 4096 你实际上只分配了4MiB。我一开始就被这个坑过装出来的虚拟机启动卡死日志里疯狂报内存不足。正确写法是显式带上单位memory unitGiB4/memory或者写成 4194304 。同理 默认只看CPU数量单位不涉及但需要注意客户机最大vcpu数和当前vcpu数的概念差异修改当前数后必须重启才生效。4.2 快照的两种用法学习试错阶段最强的后悔药学虚拟化的时候我强烈建议善用快照。每次要做一个不确定的操作之前拍一张快照翻车了直接回滚省下大把重装系统的时间。virsh快照分两种磁盘层面和域层面。磁盘快照直接针对qcow2文件命令是virsh snapshot-create-as openeuler-vm1 snap-before-test --disk-only这条命令会生成一个新的qcow2快照文件原文件变成快照的base修改只写入新文件里。回滚的时候virsh blockcommit openeuler-vm1 /var/lib/libvirt/images/openeuler-vm1.qcow2 --active --pivotblockcommit会把数据合并回基础磁盘完成回滚。在ARMV平台下qcow2格式本身没有架构相关的限制所以这两种操作跟x86一致。如果你想连内存状态一起保存可以去掉--disk-only参数这样快照会同时锁存客户机的运行状态恢复之后就像穿越回之前那一刻一样。代价是快照文件体积大恢复速度也慢一些。4.3 虚拟机开机自启宿主机重启后别让服务断片学校里的测试环境经常遇到宿主机断电重启的情况如果每一台虚拟机都要手动启动那真的很耽误事。libvirt提供了autostart机制一条命令就能搞定virsh autostart openeuler-vm1这个命令的本质是在 /etc/libvirt/qemu/autostart 目录下生成一个指向虚拟机的符号链接。默认情况下它只在libvirtd启动的时候生效所以如果你改了虚拟机的XML配置建议先 virsh destroy 再自动启动确认一次性拉起。有一个经验不要在宿主机开机时把所有虚拟机都设成autostart。如果一台虚拟机因为磁盘损坏或者配置错误导致开机即崩溃它会反复尝试拉起把宿主机CPU和磁盘IO拖累得很惨。我一般只会给状态稳定的基础设施虚拟机开自启测试用的虚拟机不给宁可手动启动。5. 性能调优让KVM虚拟化在ARM上跑出该有的水平跑通一个虚拟机是学习阶段的目标但真正衡量你虚拟化学得如何还是要看性能和稳定性。ARM平台的性能调优路径跟x86有部分重叠但也有不少独门要诀。这一章结合实战挑几个最影响体感的方向来讲。5.1 vCPU绑核与NUMA亲和性为什么不能只靠虚拟化层很多ARM服务器尤其是鲲鹏这类多路产品内部是NUMA架构的不同CPU簇访问本地内存和其他簇内存的延迟差异很大。虚拟机如果不做绑核vCPU可能一会儿跑到这个簇一会儿跑到那个簇TLB和L1/L2缓存的命中率会直线下降。绑核操作分两步第一步确认虚拟机的vCPU编号virsh vcpupin openeuler-vm1 --vcpu 0 --cpuset 0这会把vcpu 0绑定到宿主机的物理CPU 0上。如果你有4核可以依次把4个vcpu绑到连续的4个核上for i in 0 1 2 3; do virsh vcpupin openeuler-vm1 --vcpu $i --cpuset $i; done接着处理内存亲和性。libvirt的numatune配置在XML里可以临时用virsh设置virsh numatune openeuler-vm1 --mode strict --nodeset 0strict模式表示所有内存都从NUMA node 0分配。如果只有一颗物理CPU这个配置意义不大如果是多路服务器这个配置能明显提升内存带宽稳定性。ARM服务器还有个特点某些型号的CPU把集群cluster作为调度和功耗管理的基本单位。绑核时尽量把vCPU绑到同一个cluster的物理核上跨cluster调度导致的L2缓存共享中断在ARM上比x86更明显。这个可以查阅你的CPU拓扑信息lscpu -e注意看每个CPU Core关联的是哪个NODE和Cluster据此设计绑核策略。5.2 磁盘和网络虚拟化的正确姿势virtio优化到队列级磁盘方面除了选virtio-blk模型还要考虑是否开启writeback模式以及IO调度器。客户机里把磁盘IO调度器改成nonenoop通常对虚拟磁盘最友好因为宿主机一侧已经有完整的调度逻辑客户机再做一层排队属于重复劳动反而增加延迟echo none /sys/block/vda/queue/scheduler如果希望持久化需要改内核启动参数在grub里给内核传 elevatornone。网络方面主要关注多队列。virtio-net默认单队列在高流量场景下会成为瓶颈。开启多队列的方法是给网卡配置指定rx和tx队列数在virt-manager图形界面里或者在XML的interface节点中设置interface typebridge source bridgebr0/ model typevirtio/ driver namevhost queues4 host csumon gsoon tso4on tso6on/ guest csumon tso4on tso6on/ /driver address typepci domain0x0000 bus0x01 slot0x00 function0x0/ /interface配置完后客户机里的网卡会对应出现多个虚拟队列流量按哈希分散到各队列能充分利用多vCPU并发处理网络包。我在一台4vCPU虚拟机上开启4队列后iperf3吞吐从接近1.8Gbps直接跳到接近线速3.7Gbps千兆网口环境效果非常直观这也侧面说明单队列模式在ARM KVM里的瓶颈比很多教程描述的还要严重。5.3 使用qemu-guest-agent优化虚拟机时钟与同步ARM虚拟化环境下有一个x86上不明显、但实际很影响体感的问题虚拟机时钟漂移。特别是虚拟机长时间跑高负载业务时系统时间会慢慢偏离真实时间。原因很复杂跟ARM的系统计数器system counter以及客户机里时钟源选择有关。解决方案是在客户机里安装qemu-guest-agent然后让宿主机通过virtserial通道跟客户机通信提供时钟同步等服务。安装方式dnf install -y qemu-guest-agent systemctl enable --now qemu-guest-agent同时检查虚拟机XML里是否启用了channel设备virsh edit openeuler-vm1找到如下配置并确认存在channel typeunix source modebind path/var/lib/libvirt/qemu/channel/target/openeuler-vm1.org.qemu.guest_agent.0/ target typevirtio nameorg.qemu.guest_agent.0/ /channel有了guest agent后宿主机侧还能执行 virsh setvcpus --live 等一些在线调整操作这在x86平台的KVM上是常规操作ARM平台原理相同但很多虚拟机镜像默认不装这个agent需要自己加上。6. 我在这轮KVM学习中踩过的坑与个人复盘学习ARM虚拟化的过程有一半时间其实是在处理各种环境问题和架构差异。这一章写几个让我印象最深的坑以及我最后的排查思路和优化心得希望帮你省下这部分时间。6.1 常见坑位速查从引导失败到网络不通的问题清单我整理了一张排查表如果你在鲲鹏Arm64 openEuler上遇到类似问题可以直接对照着找方向现象大概率原因快速处理虚拟机启动后黑屏无输出缺少UEFI引导配置或ESP分区缺失确认XML里 重建ESP分区virsh shutdown无响应客户机缺少ACPI控制器配置XML中加上网络能通但速度极低网卡还是e1000等模拟设备改成virtio模型确认驱动加载虚拟机时间经常漂移未安装qemu-guest-agent安装agent并检查channel配置内存参数怎么看都不对XML里内存单位默认是KiB显式写 unitGiB 或者换算成KiB创建虚拟机报找不到安装源下载错了x86_64的ISO确认下载的是aarch64架构的ISO虚拟机启动非常慢NUMA绑定冲突或跨cluster调度用 vcpupin 绑核numatune 固定内存这张表不是用来让你背的而是提醒你在ARM虚拟化里遇到问题时先怀疑架构差异再怀疑配置错误顺序对了排查就很快。6.2 一段真实排查案例一台虚拟机开机即重启的完整定位链路这里分享一次对我帮助最大的排错经历。有台测试虚拟机的XML是从x86环境拷贝过来的只是改了几个基础参数结果启动后几秒钟就自动重启guest agent也连接不上。一开始我以为是镜像问题重新创建了一台也没用这才意识到可能是XML里某些设备模型不兼容。我的排查步骤是这样的先看日志执行virsh start openeuler-vm1 --console journalctl -u libvirtd -f日志里明确提示客户机向串口输出了硬件错误信息内容指向PCI中断路由。这时我猜测是PCI设备地址分配与ARM的GIC路由不兼容于是用virsh dumpxml检查设备地址发现网卡和显卡的PCI地址存在冲突。解决办法很简单去掉XML里手动指定的PCI地址让libvirt根据ARM的PCI拓扑自动分配。改完重启虚拟机顺利进入系统。更长远的教训是跨架构复用XML是一场豪赌。一个从x86拷贝的XML表面上只多了一行 qemu:arg value-M .../但在ARM平台上可能就是致命的。所以后面但凡创建新虚拟机我都从virt-install全新生成再去编辑需要改的字段。6.3 学习路线建议从单机虚拟化到集群调度下一步怎么走跑通单机KVM虚拟化之后可以往两个方向深入。第一个是把虚拟机数量加上去学透资源隔离和过载比比如在一台16核宿主机上跑8台2核虚拟机观察调度和抢资源的实际表现。第二个方向是学习上层调度平台比如OpenStack或者更轻量的容器化调度方案理解虚拟机与容器共享同一套内核虚拟化能力时的差异点。我个人觉得ARM虚拟化学习不像x86那样有大量现成资料可以闭眼照抄它更合适手动折腾-记录-复盘的学习方式。如果你把上面提到的每个点都亲手操作一遍遇到问题再回到日志和文档里去查会比看任何教程都学得更牢。我在实际学习过程中最大的体会是不要把ARM当成另一个x86而是把它当成第一回接触的虚拟化架构来对待把所有默认假设都重新验证一遍这样收获会最大。

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑