接手这个项目时,客户的需求很直接:现有业务要往ARM架构迁移,但测试环境紧缺,得先把一台华为TaiShan 200 2280服务器上的虚拟化搞定,让几套ARM版业务系统先跑起来。
这活儿看着不复杂,实际上坑不少。TaiShan 200 2280用的是鲲鹏920处理器,架构上跟x86完全不同,传统那套部署虚拟化的路径不能直接照搬,尤其是固件、引导模式、Guest OS适配这一块,稍微不留神就卡住。我前后鏖战了几天,把整套流程理顺了,从硬件准备、固件配置、宿主OS安装、KVM虚拟化组件搭建,到虚拟机创建、性能调优和问题排查,全部跑通。这里把完整过程沉淀下来,给后面接手ARM服务器虚拟化的朋友做个参考。
这套指南不仅适合数据中心运维、虚拟化工程师,也适合正在做ARM生态迁移的研发同学。
1. 项目背景与整体设计思路
1.1 为什么是TaiShan 200 2280
TaiShan 200 2280是华为基于鲲鹏920处理器打造的机架式服务器,定位就是数据中心场景的通用计算平台。它的核心优势在于高核心数、高内存带宽以及更低的功耗比,在一些云端原生应用、大数据、分布式存储等场景下表现相当不错。既然业务要朝ARM架构迁移,拿它来做宿主机是顺理成章的事。
但ARM服务器虚拟化有一个绕不开的问题:整个技术栈的成熟度不如x86。x86平台有成熟的VMware ESXi、KVM、Xen,生态早就跑顺了;ARM平台虽然也支持KVM,但需要宿主OS、内核模块、QEMU版本、Guest OS镜像全链路适配,任一个环节掉链子,虚拟机就起不来。
1.2 虚拟化方案选型:为什么走KVM路线
当前ARM服务器上主流的虚拟化方案有以下几类,我做了个对比评估:
| 方案 | 架构支持 | 成熟度 | 运维成本 | 适用场景 |
|---|---|---|---|---|
| KVM + QEMU + libvirt | ARM64原生支持 | 高 | 中 | 通用Linux虚拟化、CPU密集/内存密集业务 |
| OpenStack | ARM64支持但配置复杂 | 中 | 高 | 大规模云平台管理 |
| Docker/容器 | ARM64支持好 | 高 | 低 | 微服务、无状态应用 |
| VMware | 官方不支持ARM | 不适用 | 不适用 | 不适用 |
| Proxmox VE | 有限支持,需特定版本/架构 | 低 | 中 | 小规模实验环境 |
实际对比下来,KVM方案在ARM平台上是最稳的。KVM本身是Linux内核模块,内核主线对ARM64的虚拟化扩展(Virtualization Extensions)支持已经很成熟,QEMU也提供了完整的ARM64系统模拟能力,配合libvirt做统一管理,足够撑起一套可靠的小型虚拟化环境。
有人会问为什么不用容器直接搞定?容器方案虽然轻量,但客户这边有部分业务是完整操作系统级的需求,需要内核模块加载、传统系统服务运行,虚拟机是刚需。所以容器作为补充,KVM作为主力。
1.3 宿主机整体架构设计
整套部署架构大概是这样:
物理层:TaiShan 200 2280(鲲鹏920,64核,8通道DDR4) ↓ 宿主OS层:银河麒麟服务器操作系统V10 SP3(ARM版,内核4.19) ↓ 虚拟化层:KVM + QEMU + libvirt ↓ 管理工具:virsh/virt-manager(命令行为主) ↓ Guest OS:银河麒麟V10 SP3 ARM、openEuler ARM、统信UOS ARM这个架构的好处是每一层都能单独排查,哪层出问题直接定位。实际部署中我把网络桥接、存储池、虚拟机配置这三块拆开独立配置,后面出问题时排查速度快很多。
2. 硬件平台与固件系统准备
2.1 TaiShan 200 2280硬件盘点
动手之前,先把服务器硬件信息摸清楚:
- 处理器:鲲鹏920,单颗64核,主频2.6GHz,集成8通道DDR4内存控制器
- 内存:本次配置16条32GB DDR4,总共512GB
- 存储:前置12盘位,本次配置8块1.92TB SATA SSD,2块480GB SATA SSD(系统盘)
- RAID卡:板载LSI 3508(支持RAID 0/1/10/5/6/50/60)
- 网络:板载2个10GE光口 + 2个GE电口
- 固件:鲲鹏UEFI固件(区别于x86的BIOS)
这里有个很重要的点:鲲鹏服务器的固件是UEFI架构,不是传统的BIOS。很多从x86转过来的人下意识想找“BIOS设置”入口,概念上会有点别扭,实际上就是UEFI固件界面,操作逻辑接近但不完全一样。
2.2 UEFI固件配置:开启虚拟化能力
开机按Del或F2进入UEFI固件配置界面,关键需要处理两个地方:
开启CPU虚拟化功能
路径大致是:Processor Configuration -> Virtualization Technology,该项必须设为Enabled。虽然鲲鹏920的虚拟化扩展默认是开启的,但有些固件版本默认值是Disabled,没打开的话KVM模块直接加载失败。
调整启动模式
TaiShan 200 2280支持UEFI模式和Legacy模式两种启动方式,实测下来建议直接采用UEFI模式安装宿主OS。原因是后续Guest OS走UEFI引导更省心,尤其银河麒麟V10的ARM版在UEFI模式下兼容性最好。
注意:如果服务器之前被人改过启动模式,务必在安装宿主OS之前确认好,否则后面引导会出现“No bootable device”的尴尬情况。
2.3 RAID配置与操作系统安装
RAID配置我走的是板载LSI 3508阵列卡,开机按Ctrl+R或使用RAID卡的配置界面。
本次做的是8块SSD组RAID 10,2块480GB组RAID 1作为系统盘。选RAID 10是因为虚拟化对磁盘性能要求不低,RAID 10兼顾性能和冗余,比RAID 5在随机读写上表现稳定,而且重建时间也更短。
配置完RAID后,开始安装银河麒麟服务器操作系统V10 SP3 ARM版。安装过程本身跟着向导走就行,但有几个细节值得注意:
- 磁盘分区:建议/boot单独分一个区(500MB足够),/分区直接分给系统盘剩余空间,/var/lib/libvirt用于存放虚拟机镜像,如果是独立数据盘,最好单独挂载
- 软件包选择:安装时勾选“虚拟化”、“开发工具”和“系统管理”相关组件,减少后续手动安装依赖
- 网络配置:先配好管理口的IP,后续通过SSH操作会方便很多
系统装完后,通过uname -a确认内核架构是aarch64:
[root@taishan ~]# uname -a Linux taishan 4.19.90-52.22.v2207.ky10.aarch64 #1 SMP ...2.4 宿主OS基础环境检查
系统装好之后,先做一轮基础检查,确保硬件和OS层没有明显问题:
lscpu free -h lsblk ip addr重点关注三件事:CPU是否识别为64核、内存总量是否和物理配置一致、网卡是否正常加载。如果内存识别不对,可能是内存条接触问题或固件配置有误,先解决再去碰虚拟化。
另外,把yum源或dnf源配置成银河麒麟官方ARM源的镜像,后面安装KVM组件全程用得上。
3. 虚拟化软件栈搭建
3.1 安装KVM相关组件
在银河麒麟V10 SP3上安装KVM组件,可以用dnf直接拉取。这里我装的是完整的一套:
dnf install -y qemu-kvm qemu-img libvirt virt-install virt-manager bridge-utils逐个说下这些包干什么用的:
- qemu-kvm:QEMU的系统模拟器主体,处理CPU/内存/设备模拟
- qemu-img:磁盘镜像管理工具,支持qcow2、raw格式的创建和转换
- libvirt:虚拟化管理API和守护进程,提供virsh命令行工具
- virt-install:命令行创建虚拟机的工具
- virt-manager:图形化管理工具,虽然SSH环境下用不了,但在本地桌面或X11转发时能用
- bridge-utils:网桥管理工具,用来创建桥接网络
ARM平台注意一点:务必确认安装的是aarch64版本。如果环境里配置了x86的源,很容易装成x86版本,虚拟化起来就直接不兼容。
3.2 确认KVM内核模块加载
装完包之后,不要急着创建虚拟机,先确认KVM模块是否真的加载成功:
[root@taishan ~]# ls /dev/kvm /dev/kvm有输出说明KVM设备节点已创建。再用lsmod | grep kvm看看模块状态:
[root@taishan ~]# lsmod | grep kvm kvm_vcpu_loader 16384 0 kvm 614400 0如果/dev/kvm不存在,大概率是UEFI里虚拟化开关没打开,或者内核缺少KVM支持模块。这个之后再排查。
3.3 配置桥接网络
虚拟机要对外提供服务,不能靠NAT那套,必须走桥接模式,让虚拟机直接接入物理网络。
编辑网络配置文件,增加一个桥接接口br0。在银河麒麟V10 SP3上,网络配置还是走ifcfg文件的方式。
我这边做法是,把板载10GE光口其中一个作为业务口,创建/etc/sysconfig/network-scripts/ifcfg-br0:
DEVICE=br0 TYPE=Bridge BOOTPROTO=static IPADDR=192.168.10.10 NETMASK=255.255.255.0 GATEWAY=192.168.10.1 DNS1=223.5.5.5 ONBOOT=yes DELAY=0对应修改物理网卡配置ifcfg-enp189s0f0:
DEVICE=enp189s0f0 TYPE=Ethernet BOOTPROTO=none BRIDGE=br0 ONBOOT=yes配置完成后重启网络服务:
systemctl restart network ip addr show br0这里遇到一个问题:如果用的是NetworkManager管理网络,需要把NetworkManager停掉或者对br0做专门管理配置,否则网桥偶发失效。生产环境建议干脆用systemd-networkd或纯network服务,省掉一些不可控因素。
3.4 配置存储池
存储池我直接用的目录池,路径指向/var/lib/libvirt/images。先创建目录,再定义存储池:
mkdir -p /var/lib/libvirt/images virsh pool-define-as default dir - - - - "/var/lib/libvirt/images" virsh pool-start default virsh pool-autostart default用目录池就够了,不用额外配置LVM或Ceph,后面创建qcow2镜像都由libvirt统一管理。如果后面要考虑性能隔离,可以单独划一个LVM卷组给虚拟化用,这里先不做复杂化。
配置完之后,启动libvirtd服务并设置开机自启。
4. 虚拟机创建与配置实操
4.1 准备工作:ARM版Guest OS镜像
ARM虚拟机和x86虚拟机一个本质区别:Guest OS必须是ARM64版本。x86的ISO镜像塞进去是跑不起来的,CPU架构翻译模拟在纯虚拟化场景下不可行。
我准备了几种镜像:
- 银河麒麟服务器操作系统V10 SP3 ARM版ISO
- openEuler 22.03 LTS ARM版ISO
- 统信UOS 1050 ARM版ISO
镜像放到了/var/lib/libvirt/images/boot/下面。
提示:ARM版镜像通常命名会有明显的aarch64或arm64标识,下载时务必核对。装错了镜像,启动时就是一片黑屏或者直接报“Invalid instruction”之类的问题。
4.2 使用virt-install创建第一台虚拟机
直接用virt-install创建,命令如下:
virt-install \ --name kylin-vm01 \ --memory 16384 \ --vcpus 8 \ --disk path=/var/lib/libvirt/images/kylin-vm01.qcow2,size=200,format=qcow2,bus=virtio \ --network bridge=br0,model=virtio \ --os-variant centos7.0 \ --cdrom /var/lib/libvirt/images/boot/Kylin-Server-V10-SP3-2403-ARM64.iso \ --graphics vnc,listen=0.0.0.0 \ --boot uefi这里有几个参数值得展开讲:
--vcpus 8是给虚拟机8个vCPU,--memory 16384分配16GB内存。宿主机有64核512GB,第一台机器不需要太激进,温和一点方便观察虚拟化层是否稳定。
--disk的bus=virtio很关键,virtio半虚拟化磁盘性能远优于模拟IDE/SATA,ARM平台上virtio支持也很完善。
--network bridge=br0,model=virtio同理,virtio网卡性能接近物理网卡。
--boot uefi让虚拟机走UEFI引导。ARM平台基本都要求UEFI启动,这个参数不写,虚拟机可能找不到引导设备。
--os-variant centos7.0那一项其实可以不管,因为Linux发行版里没有直接对应银河麒麟的选项,随便给一个x86时代的参数不影响ARMGuest的启动。不过如果版本不匹配,virt-install会告警,忽略即可。
4.3 虚拟机无法安装时的应急方案
之前说过,KVM ARM环境里,virt-install配合本地ISO不是无脑能用。我第一台虚拟机装的时候就卡住了:VNC连上去黑屏,后来发现是--cdrom指定的ISO路径权限问题,libvirt的qemu进程没有读取权限。
如果在VNC里看到黑屏,先看看虚拟机的控制台日志:
virsh console kylin-vm01或者查看详细的启动日志:
journalctl -u libvirtd -f通常这俩能看到实际报错,权限问题、路径问题都能暴露出来。
4.4 虚拟机XML配置细节解析
virt-install生成的XML配置文件保存在/etc/libvirt/qemu/kylin-vm01.xml,修改后需要重载定义:
virsh shutdown kylin-vm01 virsh edit kylin-vm01 virsh define /etc/libvirt/qemu/kylin-vm01.xml virsh start kylin-vm01XML里有几个关键节点值得关注:
CPU和内存部分:
<vcpu placement='static'>8</vcpu> <memory unit='KiB'>16777216</memory> <cpu mode='host-passthrough' check='none'/>cpu mode='host-passthrough'意思是将宿主机的CPU特性直接透传给虚拟机。ARM平台下用host-passthrough比较稳妥,Guest能直接看到鲲鹏920的全部特性,性能损耗最小。如果默认用host-model反而可能出现CPU特性识别不全的问题。
UEFI引导部分:
<os> <type arch='aarch64' machine='virt-4.2'>hvm</type> <loader readonly='yes' type='pflash'>/usr/share/AAVMF/AAVMF_CODE.fd</loader> <nvram template='/usr/share/AAVMF/AAVMF_VARS.fd'>/var/lib/libvirt/qemu/nvram/kylin-vm01_VARS.fd</nvram> </os>AAVMF是ARM版的UEFI固件,如果没有安装AAVMF包,需要先装:
dnf install -y AAVMF缺少AAVMF包时,virt-install命令会报“Unable to find UEFI firmware”,这就是ARM平台特有的坑,x86平台装OVMF就行,ARM平台必须AAVMF。
磁盘和网卡设备部分:
<disk type='file' device='disk'> <driver name='qemu' type='qcow2' cache='none' io='native'/> <source file='/var/lib/libvirt/images/kylin-vm01.qcow2'/> <target dev='vda' bus='virtio'/> </disk> <interface type='bridge'> <source bridge='br0'/> <model type='virtio'/> </interface>cache='none' + io='native'是磁盘性能的推荐组合,避免页面缓存导致的数据一致性问题,同时native io方式绕过QEMU自身I/O调度,充分发挥SSD性能。
4.5 虚拟机全生命周期管理
虚拟机创建之后的日常管理,我用得最多的命令是这些:
virsh list --all # 查看所有虚拟机状态 virsh start kylin-vm01 # 启动虚拟机 virsh shutdown kylin-vm01 # 优雅关机 virsh destroy kylin-vm01 # 强制断电 virsh suspend kylin-vm01 # 暂停 virsh resume kylin-vm01 # 恢复 virsh snapshot-create-as kylin-vm01 base --disk-only # 创建快照 virsh restore kylin-vm01 /var/lib/libvirt/images/kylin-vm01-1.snap # 恢复快照 virsh console kylin-vm01 # 进入串口控制台,初始化时很有用初次安装Guest OS时,我强烈建议通过virsh console来操作,因为有些网络配置没起来之前VNC不一定能连通,串口控制台是最底层的访问方式。
5. 性能调优与稳定性保障
5.1 启用NUMA亲和与CPU绑定
鲲鹏920是NUMA架构(非一致性内存访问),多颗CPU共享内存会产生跨片访问延迟。让虚拟机的vCPU绑定在同一个NUMA节点上,内存访问速度会明显好过分散在不同节点。
先看宿主机的NUMA拓扑:
numactl --hardware假设输出显示node0和node1各有32个CPU核、256GB内存,分配虚拟机时让vCPU和内存尽量落在同一节点。
在XML里配置:
<numatune> <memory mode='strict' nodeset='0'/> </numatune> <vcpu placement='static'>8</vcpu> <cputune> <vcpupin vcpu='0' cpuset='0'/> <vcpupin vcpu='1' cpuset='1'/> <vcpupin vcpu='2' cpuset='2'/> <vcpupin vcpu='3' cpuset='3'/> <vcpupin vcpu='4' cpuset='4'/> <vcpupin vcpu='5' cpuset='5'/> <vcpupin vcpu='6' cpuset='6'/> <vcpupin vcpu='7' cpuset='7'/> </cputune>这样虚拟机8个vCPU全部锁定在node0的8个物理核上,memory mode='strict'保证内存也落在node0,整个虚拟机运行在同一个NUMA域内,杜绝跨节点访问。
5.2 大页内存配置
大页内存(HugePages)对虚拟化性能的提升是非常明显的,主要作用是降低TLB(Translation Lookaside Buffer)缺失率,让虚拟机内存访问更快。
检查宿主机大页配置:
cat /proc/meminfo | grep Huge默认情况下HugePagesize是2MB,可以配置更大的1GB页。在/etc/default/grub里设置:
GRUB_CMDLINE_LINUX="... default_hugepagesz=1G hugepagesz=1G hugepages=64"重新生成grub配置后重启生效:
grub2-mkconfig -o /boot/efi/EFI/ky10/grub.cfg reboot等宿主机起来后,确认大页数量是否生效:
cat /proc/sys/vm/nr_hugepages然后让虚拟机使用大页内存,在XML里加:
<memoryBacking> <hugepages/> </memoryBacking>需要注意,配置了大页之后,如果分配的总内存超过物理内存,虚拟机可能无法启动,要合理规划大页数量。我这边给虚拟化预留了64个1GB大页,其余内存留给宿主机自身和缓存。
5.3 virtio多队列网卡调优
虚拟机跑起来后,默认的virtio网卡是单队列的。对网络要求高的场景,可以开启多队列,把网络收包负载打散到多个vCPU上。
首先在宿主机层面对virtio-net模块开启多队列支持,Guest OS内核参数里也要加:
ethtool -L eth0 combined 4combined的意思是把RX和TX队列各开4个。如果网卡初始就支持多队列,还可以在虚拟机XML里指定队列数:
<interface type='bridge'> <source bridge='br0'/> <model type='virtio'/> <driver name='vhost' queues='4'/> </interface>driver name='vhost'走的是内核vhost-net路径,相比纯QEMU用户态模拟开销更低。实测开启多队列后,虚拟机的网络吞吐能从接近线速的单核瓶颈,提升到可以用满多个物理核的程度。
5.4 部署后的稳定性验证
全部配置完成后,我做了一轮稳定性压测,确保这套虚拟化环境不只是“能启动”,而是“能扛住业务”。
以下是我跑的验证项:
- 连续72小时运行虚拟机,观察是否出现死机、重启、OOM
- 使用UnixBench和fio测试虚拟机的CPU、磁盘性能基线
- 通过iperf3验证虚拟机的网络吞吐能力
- 测试删除、重建虚拟机的功能,确认存储池和网络配置没有依赖问题
- 用快照做了一次完整的备份恢复演练,确认业务虚拟机数据可恢复
整套跑下来,虚拟机的性能表现基本达到裸机90%以上,网络吞吐和磁盘IOPS也符合预期。
6. 常见问题排查与经验总结
6.1 /dev/kvm不存在
新装系统后,第一件事就该检查ls /dev/kvm。如果不存在,通常原因有两个:
第一,UEFI固件里虚拟化开关没打开,或者固件版本太老不支持。解决方法是进UEFI确认Virtualization Technology选项为Enabled,必要时升级固件版本。
第二,内核缺少KVM支持。加载下模块试试:
modprobe kvm modprobe kvm_arm银河麒麟V10 SP3的默认内核是带ARM KVM模块的,如果modprobe找不到模块,多半是内核配置裁剪掉虚拟化能力,这时候只能换内核或者换发行版。
6.2 虚拟机无法启动,VNC黑屏
创建虚拟机后,VNC连过去黑屏,这是最常见的ARM虚拟化问题。通常原因优先级从高到低是:
- ISO镜像不是ARM64版本,换镜像
- AAVMF固件没装,安装后重试
- ISO路径权限不足,执行
chmod +r或者调整虚拟机的<source>路径权限 - 启动模式不对,确认在XML里配置了UEFI引导
6.3 Guest OS看不到virtio磁盘或网卡
如果Guest内核版本太老,可能不带virtio驱动,导致虚拟机启动时找不到磁盘或没有网卡。解决办法是给虚拟机临时加一个SATA控制器挂载磁盘,先装上系统,再把virtio驱动编译进内核,最后切回virtio总线。ARM平台上,银河麒麟V10 SP3、openEuler这类新发行版都已经内置virtio驱动,比较省心。
6.4 磁盘性能不达预期
虚拟机磁盘跑不满物理SSD的性能,先检查虚拟机里面挂载参数,再用iotop看是不是有别的虚拟机在抢占I/O。如果宿主机侧用了cgroup配置,需要检查blkio限制:
virsh blkiotune kylin-vm01 --device-read-bytes-sec /dev/sda,1000000000再不行就把存储池换到更快的NVMe盘上,物理介质决定上限。
6.5 虚拟机磁盘空间不足
扩容qcow2镜像的标准步骤是先做物理扩容,再在Guest里扩展分区:
qemu-img resize /var/lib/libvirt/images/kylin-vm01.qcow2 +100G然后进虚拟机里执行:
growpart /dev/vda 2 resize2fs /dev/vda2这里有个实际经验:在UEFI引导的ARM虚拟机上,直接执行growpart可能会报“unrecognized partition table”,因为GPT分区表和MBR分区表兼容性差异导致的,需要先确认分区类型,再决定用gdisk还是部分命令。
6.6 网络丢包或延迟偏高
虚拟化环境网络问题排查路径:
先看宿主机侧有没有丢包:
ethtool -S br0 | grep -i error然后再看虚拟机内部是否有丢包:
netstat -i ip -s link如果宿主机干净,问题大概率在虚拟机网卡驱动或者队列配置。确认virtio多队列是否开启,同时确认qcow2镜像文件所在的磁盘I/O没有过度繁忙,因为网络收包也会触发磁盘写入。
这套部署过程走下来,我对ARM虚拟化的最大体会是:硬件本身已经完全可以支撑生产级虚拟化,真正考验人的是对ARM生态细节的把控。固件里多看一眼虚拟化开关、操作系统选了正确的ARM ISO、固件包AAVMF别漏装、guest镜像必须ARM64版,这些“小”地方往往才是决定成败的关键。如果已经有x86虚拟化经验,上手ARM服务器虚拟化最大的成本就是改掉“x86惯性”,剩下的无非是再走一遍熟悉的路而已。最后提醒一句,生产环境上线前,一定要做一次完整的备份恢复演练,确保虚拟机快照和备份策略真正可用。