☰
ARM服务器纯命令行虚拟化实战:从KVM到cloud-init完整指南
2026/10/21 9:28:17 网站建设 项目流程

1. 环境评估与选型:ARM服务器虚拟化前必须搞清的几件事

ARM服务器这几年在数据中心和机房里的出镜率越来越高,不管是低成本拿下一台物理机,还是云厂商提供的ARM实例,跑的都是Linux。你要在这类机器上做虚拟化,第一反应可能是装个带图形界面的管理工具,但生产环境里往往只有命令行——没有显示器、没有浏览器、没有VNC端口。所以这篇文章的核心就是:在ARM架构的Linux服务器上,靠纯命令行把虚拟机装好、跑起来,并且能长期稳定维护。

先解决一个最基础的问题:ARM服务器上的虚拟化方案该怎么选。

x86世界里主流的KVM、Xen、Hyper-V,在ARM上并不是完全对等的。ARM服务器目前最成熟、最通用的方案是KVM(Kernel-based Virtual Machine)加QEMU的组合,管理工具用libvirt。这个组合在ARM生态里的地位,基本等同于x86世界里KVM加libvirt的地位。

我自己在ARM服务器上装过不止一次虚拟机,踩过的坑主要集中在三块:一是固件和引导方式,ARM虚拟机和x86虚拟机在引导层面差异非常大;二是网络模式,默认的NAT没问题,但要接外部网络就得提前规划桥接;三是云镜像的选择,很多ARM镜像在纯命令行安装场景下比ISO安装要顺滑得多。

在开始安装之前,先评估一下这台机器适不适合做虚拟化。ARM服务器的虚拟化能力和x86一样,依赖CPU的硬件虚拟化扩展。ARMv8架构之后,虚拟化扩展几乎是标配,但有些低端ARM开发板、或者某些跑在非虚拟化固件上的机器,KVM可能起不来。

用下面几条命令快速体检一下:

# 查看CPU架构和虚拟化支持 lscpu | grep -E "Architecture|Virtualization" # ARM机器上最关键的判断:/dev/kvm是否存在 ls -l /dev/kvm # 内存和磁盘概况 free -h df -h /var/lib/libvirt

如果/dev/kvm存在,说明KVM内核模块已经加载,硬件虚拟化这一关就过了。很多人在这一步翻车,尤其是某些ARM开发板或者ODM厂商定制的服务器,内核编译选项里根本没开KVM,后面装什么都是白搭。

内存和磁盘也要心里有数。虚拟机的内存是物理内存硬切出来的,宿主机自己至少得留2GB左右。磁盘按虚拟机数量来算,比如每台云镜像虚拟机初始2GB,扩容后加到20GB,三台虚拟机就是60GB,加上镜像文件和系统本身,宿主机磁盘空闲容量至少要两倍以上才舒服。

选型环节另外重要的一点是确定发行版。宿主机的发行版决定了你装KVM组件的命令。Debian系的用apt,RHEL系的用dnf,我下面主要基于Debian系来演示,后面文件路径和命令会稍有差异,但思路完全一致。

1.1 分配资源前的预估口诀

准备创建几台虚拟机之前,先做个简单估算。我给一个我常用的保守分配方案:

用途vCPU内存系统盘说明
ARM架构测试机24GB20GB跑编译任务够用
微服务节点48GB40GB承载几个Java或Go服务
数据库节点416GB80GB注意给宿主机留余量

vCPU建议按物理核心数的1.5到2倍来分配,ARM服务器很多是核多单核性能一般,超分配太多会让所有虚拟机一起卡。

2. 宿主机环境准备:补齐虚拟化组件和网络基础

环境评估通过之后,下一步就是在宿主机上安装虚拟化所需的软件栈。这一步熟练的话十分钟不到,但选择安装哪些包,背后有门道。

2.1 安装KVM、QEMU和libvirt工具链

Debian系系统的安装命令如下:

apt update apt install -y qemu-kvm libvirt-daemon-system libvirt-clients virtinst cloud-image-utils

这几个包的分工大约是:

  • qemu-kvm:QEMU的KVM加速版本,是ARM虚拟化的核心模拟器。ARM架构下QEMU支持多种机器类型,最常用的是virt机器类型,对应虚拟化的通用机型。
  • libvirt-daemon-system:libvirt的后台守护进程和系统配置,负责统一管理虚拟机生命周期。
  • libvirt-clients:virsh等命令行管理工具。
  • virtinst:提供virt-install命令,这个命令行工具是创建虚拟机的关键入口。
  • cloud-image-utils:提供cloud-localds,用于生成cloud-init的种子镜像,这个对纯命令行安装至关重要。

装完之后检查一下libvirtd服务状态:

systemctl status libvirtd systemctl enable --now libvirtd

如果服务起来了,再确认:

virsh version

注意看输出的第一行有没有包含QEMU模拟器的信息,以及有没有提示KVM加速是否可用。ARM架构下如果KVM不可用,QEMU会退化成纯软件模拟(TCG模式),虚拟机跑起来会非常慢,几乎是幻灯片级别的卡顿。

还有一个ARM环境特有的坑:ARM虚拟机需要ARM版本的UEFI固件。x86环境下的固件是SeaBIOS或者OVMF,ARM下对应的是AAVMF(ARM Architecture Virtual Machine Firmware)。有的发行版在安装qemu-kvm时不会自动带上这个包,需要手动确认。

apt install -y qemu-efi-aarch64

没有这个固件,虚拟机创建后大概率无法启动,报错信息还很隐晦,只告诉你启动失败,却不告诉你固件缺失。这个坑我踩过一次,卡了差不多一个下午,后来翻/var/log/libvirt/qemu/下的日志才定位到。

RHEL系(比如某企业级Linux发行版)对应的包名是edk2-aarch64,装法类似。

2.2 网络模式怎么选:NAT、桥接还是macvtap

ARM服务器命令行装虚拟机,网络规划直接影响虚拟机能不能被外部访问。三种模式我实际对比过:

NAT模式是libvirt默认的方案。虚拟机通过宿主机上的virbr0虚拟网桥上网,宿主机做地址转换。这种模式好处是零配置,创建完虚拟机就能上网,但外部机器无法直接访问虚拟机内部的服务,适合只要出网、不要入网的场景。

桥接模式是生产环境用最多的方案。把宿主机的物理网卡桥接到一个虚拟网桥上,虚拟机直接拿到和宿主机同一网段的独立IP。外部机器可以直接访问虚拟机,和物理机几乎没有区别。配置方式稍复杂一点,需要改宿主机网络配置。

macvtap模式介于两者之间。直接把物理网卡映射给虚拟机,虚拟机获得宿主机同网段的IP,性能很好,但宿主机和虚拟机之间不能互相通信,因为macvtap的直通特性把宿主机的通道切断了。

考虑到大多数人的场景,我在后面的演示里用NAT模式起步,然后专门讲怎么改成桥接。ARM服务器上做桥接,Debian系的网络配置文件和x86环境没有本质区别,还是传统的那一套。

先看一下默认NAT网络是否存在:

virsh net-list --all

正常会有一个名为default的网络,状态是active。如果不存在,手动定义一下:

cat > /tmp/default-net.xml << 'EOF' <network> <name>default</name> <forward mode='nat'/> <bridge name='virbr0' stp='on' delay='0'/> <ip address='192.168.100.1' netmask='255.255.255.0'> <dhcp> <range start='192.168.100.100' end='192.168.100.200'/> </dhcp> </ip> </network> EOF virsh net-define /tmp/default-net.xml virsh net-start default virsh net-autostart default

这里我把默认网段改成了192.168.100.0/24,避免和常见的家庭路由网段(192.168.1.0/24)冲突。如果宿主机的物理网卡正好在192.168.1.x,虚拟机NAT网络再分配一个192.168.1.x的地址,就和外部网络撞车了,路由会很混乱。

桥接模式我单独说一下配置方法。先看一下宿主机物理网卡叫什么:

ip link show

假设物理网卡叫eth0,需要在系统网络配置里创建一个桥接接口br0,并把物理网卡绑定到桥上。Debian系的/etc/network/interfaces写法如下:

auto br0 iface br0 inet static address 192.168.1.100 netmask 255.255.255.0 gateway 192.168.1.1 dns-nameservers 8.8.8.8 bridge_ports eth0 bridge_stp off bridge_fd 0

配置好后重启网络服务,再用brctl show确认物理网卡已经挂在桥上。桥接模式对服务器网卡数量有要求——如果只有一张物理网卡,桥接配置期间要注意,一旦网络配置写错,远程SSH可能会直接断开。稳妥的做法是先在本地控制台操作,或者提前把配置写成一个脚本,断网后能一键恢复。

3. 下载ARM镜像并定制系统盘:云镜像和cloud-init是命令行装机的灵魂

ARM服务器上的虚拟机安装,我强烈建议使用云镜像而不是传统的ISO安装。原因很简单:纯命令行环境下,ISO安装意味着你得通过VNC或者串口控制台去一步步点安装界面,而云镜像配合cloud-init,可以实现全自动配置,从创建到系统就绪,全程不用人工介入。

3.1 选择合适的ARM云镜像

能用的ARM64云镜像挺多,常见的有Ubuntu Cloud Image、Debian generic cloud image。对国内服务器来说,选镜像要注意两点:一是镜像源的网络速度,二是镜像内自带的软件源是否可达。

以Ubuntu的ARM服务器云镜像为例,下载地址一般是:

mkdir -p /data/vm/images cd /data/vm/images wget https://cloud-images.ubuntu.com/releases/24.04/release/ubuntu-24.04-server-cloudimg-arm64.img

下载完之后,建议先验证一下校验和,防止下载中断导致镜像损坏:

wget https://cloud-images.ubuntu.com/releases/24.04/release/SHA256SUMS sha256sum -c SHA256SUMS 2>&1 | grep ubuntu-24.04-server-cloudimg-arm64

这个镜像是一个qcow2格式的磁盘镜像,默认大小通常在2GB左右,实际使用前必须先扩容,不然系统盘一下子就满了。

qemu-img create -f qcow2 -b ubuntu-24.04-server-cloudimg-arm64.img vm1-system.qcow2 20G

用这种带-b参数的后端镜像方式,好处是基础镜像只用一份,多个虚拟机在上面叠加自己的差异层。注意这个命令的第二个参数是覆盖镜像大小,但qcow2文件本身不会立刻占用20GB物理空间,只有虚拟机实际写入数据时才会增长。这在ARM服务器的磁盘规划上非常友好,十几台虚拟机可以共享同一个基础镜像,实际占用的容量加起来不超过基础镜像加增量。

如果不想用后端镜像,直接复制一份独立的qcow2文件也行:

cp ubuntu-24.04-server-cloudimg-arm64.img vm1-system.qcow2 qemu-img resize vm1-system.qcow2 20G

注意qemu-img resize只是把虚拟磁盘的大小标记扩容了,分区表不会自动调整,这个在后面对接cloud-init时再处理。

3.2 cloud-init自动配置:IP、密码、SSH密钥一次搞定

云镜像里预装了cloud-init,开机时它会读取一个叫cidata的配置源,把主机名、用户、密码、SSH密钥、网络配置全部初始化好。所以我们要做的,就是准备一个ISO格式的种子镜像,让虚拟机启动时自动加载。

先写cloud-init的配置文件。用户数据文件(user-data)里定义账号和初始化动作:

cat > /tmp/user-data << 'EOF' #cloud-config hostname: vm1 users: - name: ops sudo: ALL=(ALL) NOPASSWD:ALL groups: sudo shell: /bin/bash lock_passwd: false plain_text_passwd: 'YourPassw0rd@2025' ssh_pwauth: true chpasswd: expire: false runcmd: - sed -i 's/^#PermitRootLogin.*/PermitRootLogin yes/' /etc/ssh/sshd_config - systemctl restart sshd EOF

元数据文件(meta-data)里写实例ID和主机名:

cat > /tmp/meta-data << 'EOF' instance-id: vm1-001 local-hostname: vm1 EOF

然后生成ISO种子镜像:

cloud-localds /data/vm/images/vm1-seed.iso /tmp/user-data /tmp/meta-data

这个vm1-seed.iso就是虚拟机的“钥匙”,里面包含了初始密码、SSH公钥等敏感信息。装完系统后建议把它的引导权从虚拟机里移除,避免后续虚拟机重启时重复执行初始化脚本。

关于cloud-init,我补充一个常见误区:云镜像默认的用户名不是root。Ubuntu云镜像默认用户是ubuntu,Debian云镜像默认用户是debian。在cloud-init配置里,我特意创建了一个ops用户,并且在runcmd里开启了root的SSH登录权限,这样实际使用会更方便。密码里别用太简单的组合,ARM服务器暴露在公网上之后,扫描机器人的暴力破解是常态。

3.3 处理系统盘分区表

有一个细节容易忽略:qcow2镜像本身带了GPT分区表,你用qemu-img resize扩容后,分区表里的分区大小还是原来的2GB,系统根本用不到扩容出来的空间。解决方式是让cloud-init在开机时自动执行分区扩容。

Ubuntu的云镜像默认装了cloud-guest-utils包,里面带了growpart工具。在cloud-init配置里加一行:

runcmd: - growpart /dev/vda 1 - resize2fs /dev/vda1

如果镜像里没有growpart的ARM版本,也可以手动在宿主机上用libguestfs工具来改,但没这个必要,用后端的qcow2叠加镜像方式,配合cloud-init自动扩容就够了。我实际测试下来,Ubuntu云镜像在ARM虚拟机上开机自动扩容的成功率很高。

4. virt-install创建虚拟机:参数详解和一次成功的装机演示

环境、镜像、种子文件都齐了,接下来就是把虚拟机真正创建出来。关键的指挥官是virt-install,参数虽然多,但核心的没几个。

4.1 virt-install核心参数逐个拆

下面这个是我实测可用的ARM虚拟机创建命令:

virt-install \ --name vm1 \ --memory 4096 \ --vcpus 2 \ --arch aarch64 \ --machine virt \ --os-variant ubuntu24.04 \ --disk path=/data/vm/images/vm1-system.qcow2,format=qcow2,bus=virtio \ --disk path=/data/vm/images/vm1-seed.iso,device=cdrom \ --network network=default,model=virtio \ --boot uefi \ --graphics none \ --noautoconsole \ --import

逐项说明一下为什么这么写:

  • --arch aarch64:指名架构。宿主机是ARM64,虚拟机也必须是aarch64,写成x86_64就会默认用纯软件模拟,极其痛苦。
  • --machine virt:ARM环境下QEMU推荐的通用虚拟化机型,类似x86下的pc。如果固件或者内核不认这个机型,虚拟机起不来。
  • --os-variant ubuntu24.04:这个参数不只是给系统起个名字,它会自动匹配虚拟机所需的固件类型、QEMU机器类型、virtio驱动等优化项。可以先执行osinfo-query os查看系统支持的变体列表。
  • --disk path=...,bus=virtio:磁盘和网卡都选择virtio半虚拟化总线,性能接近物理机。ARM虚拟化场景下,virtio是标准选择,没人会用模拟的IDE或E1000。
  • --boot uefi:强制使用UEFI引导。ARM的现代Linux发行版基本都支持UEFI引导,尤其是云镜像,几乎都是UEFI启动。这也呼应了前面说的AAVMF固件依赖。
  • --graphics none --noautoconsole:纯命令行的核心参数。--graphics none是说虚拟机不需要任何显示设备,--noautoconsole是说创建完不自动进入控制台,直接回到Shell。
  • --import:告诉libvirt,磁盘里已经有一个可启动的系统镜像,不需要通过安装介质去安装。

关于--vcpus 2和--memory 4096,需要注意的是ARM服务器单核性能通常低于同频x86,但核心数往往很多。我一般建议vCPU数不超过物理核心数的两倍,内存分配不超过物理内存减去宿主机保留内存后的70%。

4.2 创建后的验证和控制台登录

执行完virt-install之后,用virsh list --all看一下虚拟机状态:

virsh list --all

正常会看到vm1是running状态。如果状态是shut off,说明创建过程出了问题,查日志:

journalctl -u libvirtd -f tail -50 /var/log/libvirt/qemu/vm1.log

ARM虚拟化场景下,最容易在启动阶段吊链的就是固件。AAVMF的路径配置如果不匹配,日志里会提示找不到AAVMF_CODE.fd之类的错误,解决方法就是确认qemu-efi-aarch64安装正确。

虚拟机启动了,怎么登录?两种方式。

第一种是用virsh自带的串口控制台:

virsh console vm1

但前提是虚拟机内部配置了串口登录。刚才的cloud-init配置里没有显式配置getty服务,有的云镜像默认不开放串口终端。可以在cloud-init的runcmd里加上:

runcmd: - systemctl enable serial-getty@ttyAMA0.service - systemctl restart serial-getty@ttyAMA0.service

ARM虚拟机上串口设备名通常是ttyAMA0或ttyS0,取决于机器类型。virt机器类型下一般对应ttyAMA0。

第二种方式更常用:直接通过IP地址SSH登录。先查到虚拟机拿到的IP:

virsh net-dhcp-leases default

这个命令会把NAT网络里当前分配的DHCP租约列出来,能看到虚拟机的IP和MAC地址。拿到IP之后,用cloud-init里设置的用户和密码SSH进去:

ssh ops@192.168.100.x

如果是桥接网络,直接在宿主机上用arp-scan或者去路由器的DHCP列表里找IP。

4.3 从ISO安装ARM系统的备选路子

云镜像不是唯一选择,有些特殊发行版不在云镜像列表里,就必须用ISO安装。ARM环境下ISO安装比x86麻烦一些,因为很多系统ISO不提供ARM版本,或者只提供网络安装模式。

如果手头有ARM的ISO,virt-install命令把--import去掉,换成指定引导介质:

virt-install \ --name vm1 \ --memory 4096 \ --vcpus 2 \ --arch aarch64 \ --machine virt \ --os-variant alios7 \ --disk path=/data/vm/images/vm1-system.qcow2,format=qcow2,bus=virtio \ --cdrom /data/vm/images/xxx-arm64.iso \ --network network=default,model=virtio \ --boot uefi \ --graphics vnc,port=5901 \ --noautoconsole

注意--graphics我改成了vnc。为什么?因为纯文本控制台对大多数ISO安装器来说不够用,图形安装界面需要一个显示终端。VNC就是那个显示终端。宿主机的5901端口开出来,从本机用VNC客户端连过去,就能看到虚拟机屏幕,操作方式和本地物理机的图形安装完全一样。

如果在远程环境里不方便直接连VNC,可以用SSH端口转发:

ssh -L 5901:127.0.0.1:5901 ops@宿主机IP

然后VNC客户端连127.0.0.1:5901就行。这个操作在ARM服务器上没有区别,但要注意ISO安装完成后,记得在virt-install命令里追加--boot hd或者之后用virsh edit vm1调整启动顺序,否则虚拟机会反复从ISO启动。

5. 从NAT到桥接调整:让虚拟机真正接入局域网

前面创建的虚拟机走的是NAT模式,能在宿主机内部上网,但外部机器访问不了。如果虚拟机要承担真实的业务服务,必须切换到桥接网络。

5.1 宿主机侧创建桥接网卡

先回到宿主机网络配置。Debian系的/etc/network/interfaces我前面已经给了示例。这里再补充一个系统d-networkd风格的配置方法,某些新版本的系统默认不用interfaces文件:

cat > /etc/systemd/network/10-bridge.netdev << 'EOF' [NetDev] Name=br0 Kind=bridge EOF cat > /etc/systemd/network/10-bridge.network << 'EOF' [Match] Name=br0 [Network] Address=192.168.1.100/24 Gateway=192.168.1.1 DNS=8.8.8.8 EOF cat > /etc/systemd/network/20-eth0.network << 'EOF' [Match] Name=eth0 [Network] Bridge=br0 EOF

配置完重启网络:

systemctl restart systemd-networkd

验证一下桥接是否生效:

bridge link show

看到eth0处于master br0状态就成了。

5.2 libvirt网络XML定义桥接网络

宿主机物理桥接创建好之后,还要在libvirt里定义一个桥接网络。这个桥接网络不是libvirt创建的网桥,而是直接复用宿主机已有的br0。

cat > /tmp/bridge-net.xml << 'EOF' <network> <name>bridge</name> <forward mode='bridge'/> <bridge name='br0'/> </network> EOF virsh net-define /tmp/bridge-net.xml virsh net-start bridge virsh net-autostart bridge

注意这里的<forward mode='bridge'/>是告诉libvirt:宿主机已经自己把网桥搭好了,libvirt只管把虚拟机网卡接入这个网桥。如果宿主机网桥配置有问题,虚拟机创建时就会报“接口不存在”的错误。

5.3 给虚拟机切换到桥接网络

有两种方式切换。第一种是创建新虚拟机时,把网络参数从network=default改成network=bridge:

virt-install \ ... \ --network network=bridge,model=virtio

第二种是给已存在的虚拟机修改网卡。先关掉虚拟机:

virsh shutdown vm1 virsh edit vm1

在XML配置里找到<interface>段落,把:

<source network='default'/>

改成:

<source bridge='br0'/>

或者是:

<source network='bridge'/>

具体写法取决于libvirt版本,用virsh edit保存后,再启动虚拟机:

virsh start vm1

启动后,虚拟机的DHCP请求就会从virbr0的NAT网段,跳到br0所在的物理局域网。如果虚拟机没自动拿到IP,进虚拟机内部重新获取一下:

dhclient eth0

5.4 固定IP的一个推荐做法

桥接模式下DHCP分配的IP会变化,生产环境肯定要固定IP。推荐两个层面同时固定:虚拟机内部用 netplan 或者 interfaces 配置静态IP,同时在宿主机DHCP服务器(如果有的话)里绑定MAC地址。

虚拟机内部配置静态IP,以Ubuntu 24.04的netplan为例,编辑/etc/netplan/01-netcfg.yaml:

network: version: 2 ethernets: eth0: dhcp4: no addresses: - 192.168.1.150/24 routes: - to: default via: 192.168.1.1 nameservers: addresses: - 8.8.8.8 - 114.114.114.114

保存后执行netplan apply。

如果希望libvirt帮我们固定IP,也可以在NAT网络里给特定MAC绑定固定租约:

virsh net-edit default

在<dhcp>节点里加上:

<host mac='52:54:00:xx:xx:xx' ip='192.168.100.50'/>

这里MAC地址是虚拟机的网卡MAC,查询方式:

virsh dumpxml vm1 | grep "mac address"

6. 实操中的常见问题与排查实录

命令行装ARM虚拟机,坑不算少,但每个坑都有规律可循。我把实操中最常遇到的问题整理成一张速查表,后面再单独讲几个我记忆深刻的排查案例。

故障现象可能原因排查方式
虚拟机创建后全是shut offAAVMF固件缺失或启动固件配置错误查/var/log/libvirt/qemu/下的虚拟机日志
虚拟机启动但控制台黑屏串口控制台未启用或参数配置错误检查cloud-init的getty服务配置
虚拟机无法获取IPNAT网络未启动或网卡型号不匹配virsh net-list --all和net-dhcp-leases
SSH连接超时cloud-init未正确执行或用户名错误检查用户数据和种子镜像
虚拟机磁盘满但实际分区没变大分区表未扩容在虚拟机内执行growpart和resize2fs
virt-install报“domain already exists”同名虚拟机已存在执行virsh destroy 名称; virsh undefine 名称

6.1 固件缺失导致虚拟机上电即死

这是ARM服务器虚拟化最典型的故障。第一次在ARM机上用virt-install创建虚拟机,执行完看状态是running,结果两秒后变成shut off。查日志:

cat /var/log/libvirt/qemu/vm1.log

日志里通常会有这样一行:

qemu-system-aarch64: -bios /usr/share/AAVMF/AAVMF_CODE.fd: No such file or directory

原因就是宿主机没有安装ARM的UEFI固件,QEMU找不到引导固件。解决方式就是前面说的安装qemu-efi-aarch64,如果已经装了但路径不对,可以用find / -name "AAVMF_CODE.fd"找到实际路径,然后在virt-install命令里通过--boot uefi,firmware=efi或者直接指定--boot loader=/path/to/AAVMF_CODE.fd搞定。

6.2 云镜像里没有默认密码

云镜像的设计理念是“不要默认密码”,全部依赖cloud-init注入。如果种子镜像没做好,或cloud-init没读到配置,虚拟机起来了也进不去。

排查思路:

  1. 确认种子ISO挂载上了:virsh dumpxml vm1 | grep -A2 "seed"看有没有<disk type='file' device='cdrom'>。
  2. 确认cloud-init在新实例上执行了:登录不了,可以在宿主机上用guestfish修改虚拟机磁盘内的cloud-init配置,重置密码。

重置密码的操作稍微有点绕,但纯命令行环境下很实用。先安装libguestfs工具包:

apt install -y libguestfs-tools

然后挂载虚拟机的qcow2镜像:

guestfish -a /data/vm/images/vm1-system.qcow2 -i << 'EOF' sh /usr/bin/echo "root:NewPassword" | chpasswd EOF

或者更直接一点,用guestfish修改/etc/cloud/cloud.cfg,设置disable_root: false,让root能够直接登录。

6.3 串口控制台没有登录提示符

virsh console vm1连上之后,屏幕上什么都没有,敲命令也没反应。这个八成是系统的getty服务没有绑定串口设备。

解决方式:用guestfish挂载磁盘后,创建一个新文件/etc/systemd/system/serial-getty@ttyAMA0.service.d/override.conf,内容:

[Service] ExecStart= ExecStart=-/sbin/agetty --keep-baud 115200,38400,9600 ttyAMA0 vt220

然后把systemd配置重载并启动服务。多数发行版其实默认有serial-getty@ttyS0的服务模板,只是需要你手动启用。这里有个细节,ARM virt机器里的串口设备节点可能是ttyAMA0,也可能被映射到了ttyS0,不确定时两个都试试。

6.4 cloud-init扩容分区卡住

在第一次启动时,cloud-init的growpart任务有的版本会失败,日志里写“partition cannot be resized”。原因通常是后端镜像的qcow2文件格式或者设备映射有点差异。

手动在虚拟机里执行:

growpart /dev/vda 1 resize2fs /dev/vda1

如果是ARM + virtio设备,分区设备名可能是/dev/vda或者/dev/vda1。确认一下:

lsblk

这个格式在ARM服务器上跟x86基本一致,但优先级是vda、vdb这种virtio命名,而不是sda。

6.5 网卡收不到DHCP租约

虚拟机启动了,但virsh net-dhcp-leases default看不到它的MAC。通常是libvirt默认NAT网络的DHCP地址池不匹配。比如虚拟机使用了桥接网络,但网络XML里定义成了NAT模式,虚拟机发出的DHCP广播到了virbr0,但查询接口却指向了br0。

统一调整方式是打开虚拟机的接口XML,确保<source network=或<source bridge=的配置和虚拟机连接的实体网络一致。另外,有些ARM服务器上物理网卡的名字不是eth0,而是enp1s0f0这种预测性命名,桥接配置时注意别写错。

7. 日常维护和性能调优:让ARM虚拟机长期稳定运行

虚拟机创建好、网络通、能SSH了,这才算第一步。后续的日常维护和性能调优才是真正考验功底的地方。

7.1 常用virsh命令速查

每天都在用的命令我列了一套:

virsh list --all # 查看所有虚拟机状态 virsh start vm1 # 启动虚拟机 virsh shutdown vm1 # 优雅关机 virsh destroy vm1 # 强制关机 virsh suspend vm1 / virsh resume vm1 # 挂起/恢复 virsh dumpxml vm1 # 导出虚拟机XML配置 virsh edit vm1 # 在线编辑虚拟机配置 virsh autostart vm1 # 设置开机自启 virsh vcpuinfo vm1 # 查看vCPU状态 virsh domstats vm1 # 查看资源统计

注意virsh destroy是强制断电,不到万不得已别用,容易损坏虚拟机内的文件系统。ARM虚拟机的qemu进程在宿主机上表现的和x86完全一样,用ps aux | grep qemu就能看到对应的进程。

7.2 ARM虚拟机的性能调优建议

ARM服务器的性能调优和x86有相似之处,也有独特的地方:

CPU方面:ARM的NUMA拓扑在部分多路服务器上很明显,虚拟机的vCPU最好绑定到同一个物理CPU簇上,避免跨簇访问内存。libvirt里设置vCPU pinning:

virsh vcpupin vm1 0 2 virsh vcpupin vm1 1 3

内存方面:开启透明大页(THP)对虚拟机的内存性能有帮助:

echo always > /sys/kernel/mm/transparent_hugepage/enabled

磁盘方面:确保 virtio 磁盘的 cache 模式设置为 none 或 writeback,而不是默认的 write-through。可以在virsh edit里的<driver name='qemu' type='qcow2' cache='none' io='native'/>中调整。ARM服务器的SD卡或eMMC存储带宽有限,I/O模式配置得当能明显缓解卡顿。

网络方面:多队列virtio网卡可以提升ARM虚拟机的网络吞吐:

virsh edit vm1

在<interface>的驱动部分加:

<driver name='vhost' queues='4'/>

队列数量等于虚拟机vCPU数,然后虚拟机内部也要相应的打开多队列。ARM服务器要跑高负载网络服务时,这个性能差距感受很直观。

7.3 备份和迁移的省钱方案

ARM虚拟机的备份,最简单的方式是直接拷贝qcow2文件。如果虚拟机正在运行,直接拷贝可能得到不一致的状态。稳妥的做法是先关虚拟机再拷贝,或者用libvirt的快照功能:

virsh snapshot-create-as vm1 snap-before-update --disk-only --atomic

这个命令创建一个磁盘快照,后续的写操作都进一个新qcow2文件,原文件保持原状。备份时可以只管拷贝原文件,完成后删除快照:

virsh snapshot-delete vm1 snap-before-update --metadata

迁移到另一台宿主机,标准做法是virsh migrate;没有共享存储的机器之间,更简单的是把qcow2文件和XML配置一起打包,拷贝到目标机器上执行virsh define。

ARM虚拟机的qcow2文件里有固件启动变量,迁移时要把AAVMF变量一并确认,不同版本固件可能不兼容。如果目标机器日志里出现UEFI变量相关的报错,删除虚拟机后用--boot uefi重新定义一次配置就好。

8. 经验总结与补充心得

我实际在ARM服务器上用命令行装虚拟机的过程中,最后沉淀下来几条经验:

第一,优先用云镜像加cloud-init的组合,而不是ISO安装。这不是说ISO不行,而是在纯命令行环境里,云镜像能做到无人值守,出错的概率最小。ISO安装只要涉及到图形界面或者交互式选项,就意味着你得有VNC、有窗口、有物理终端,这在ARM服务器场景下都是稀缺资源。

第二,ARM虚拟化链条上任何一环都要确认架构。从内核KVM模块、QEMU模拟器、UEFI固件到guest镜像,全部都得是aarch64版本。这个跟x86很大不同,x86的组件在x86上就永远是对的,而ARM有些组件可能拿到了armhf版本(32位ARM),在64位宿主机上就用不了。

第三,命名和目录规范从一开始就要定好。我习惯把所有虚拟机的磁盘镜像放在/data/vm/images/,种子ISO放在/data/vm/seeds/,日志在/var/log/libvirt/qemu/下按虚拟机名区分。这个习惯在出现问题时能快速定位,不会被一堆qcow2文件淹没。

第四,关于安全。ARM服务器上跑的虚拟机如果是暴露在公网上的,一定要改掉默认SSH端口、禁止密码登录、只保留密钥认证。cloud-init注入的初始密码一定在首次登录后立即修改。

最后再分享一个小技巧。ARM服务器的电源管理策略默认可能偏保守,如果你希望虚拟机CPU性能拉满,在宿主机上把CPU调成性能模式:

cpufreq-set -g performance

或者安装cpufrequtils后修改默认governor。这一步对ARM服务器的虚拟机响应速度提升非常明显,尤其是跑编译和计算密集任务时,体感差别一两倍的都有。

命令行装ARM虚拟机这事,一旦把环境、镜像、网络这三件套理清楚,后续操作比x86还要顺手。毕竟ARM服务器的生态越来越成熟,虚拟化能力也是标准配置,真正卡人的地方不是不会敲命令,而是对整个链路没有一个全局的认识。希望这篇文章能把这条链路完整地串起来。

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

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

立即咨询