基于内核的虚拟机 Kernel-based Virtual Machine(KVM)是一种内建于 Linux® 中的开源虚拟化技术。具体而言,KVM 可帮助您将 Linux 转变为虚拟机监控程序,使主机计算机能够运行多个隔离的虚拟环境,即虚拟客户机或虚拟机(VM)。KVM 是 Linux 的一部分,Linux 也是 KVM 的一部分,Linux 有的 KVM 全都有。KVM 的某些特点让它成为了企业的首选虚拟机监控程序,比如在安全性、存储、硬件支持、内存管理、实时迁移、性能和可扩展性、调度和资源控制,以及更低延迟,更高优先级等方面均具有企业级的可靠性。
对于性能过剩的盒子,可以先安装 Armbian 系统,再安装 KVM 虚拟机实现多系统使用。其中 OpenWrt 系统的编译可以使用本仓库的 mk_qemu-aarch64_img.sh 脚本进行制作,更多系统如 Debian、Ubuntu、OpenSUSE、ArchLinux、Centos、Gentoo、KyLin、UOS 等可在相关网站查阅安装与使用说明。
- 在 KVM 虚拟机中安装使用 OpenWrt 的说明
- 目录
以 Armbian/Debian/ubuntu 为例(其它操作系统请查询对应的命令)。首先验证物理机是否支持 kvm 虚拟化,如果结果如下图,那 kvm 支持就没问题,否则一般是物理机的内核没开启kvm支持(需要重新换个支持kvm的内核),或者是物理机根本就不支持 kvm!按照 arm 的官方文档,cortex-a53 以上的 cpu 都是支持 kvm 的。
ls -l /dev/kvm
dmesg | grep kvm
在物理机系统中安装 KVM 依赖包(ubuntu jammy):
sudo apt-get install -y gconf2 qemu-system-arm qemu-utils qemu-efi ipxe-qemu libvirt-daemon-system libvirt-clients bridge-utils virtinst virt-manager seabios vgabios gir1.2-spiceclientgtk-3.0 xauth
安装 x11 字库(可选)
sudo apt-get install -y fonts-noto*
安装桌面环境(可选)
sudo apt-get install -y tasksel
运行 tasksel
命令,选择至少一个桌面环境即可。
对于性能过剩的盒子,可以先安装 Armbian 系统,再安装 KVM 虚拟机实现多系统使用。其中 OpenWrt 系统的编译可以使用本仓库的 mk_qemu-aarch64_img.sh 脚本进行制作,更多系统如 Debian、Ubuntu、OpenSUSE、ArchLinux、Centos、Gentoo、KyLin、UOS 等可在相关网站查阅安装与使用说明。
分别在 Armbian 服务器和本地个人电脑安装服务端和客户端。
首先确认远程 Armbian 等服务器上的 SSH 服务端开启了 X11Forwarding 功能:
# 编辑 /etc/ssh/sshd_config 文件
X11Forwarding yes
# 如果之前未开启,保存配置文件后重启 sshd
sudo systemctl restart sshd
运行带桌面环境的 Ubuntu、Debian、Fedora、CentOS 等 Linux 发行版的本地电脑,已经自带X Server,可以略过这一步。Windows、MacOS 系统需要自行下载 X Server 程序:MacOS 可到 https://www.xquartz.org/ 下载 XQuartz 程序,Windows 可到 https://sourceforge.net/projects/vcxsrv/ 下载 VcXsrv,或到 https://sourceforge.net/projects/xming/ 下载 Xming,安装并运行 X Server 程序。
Ssh 客户端是 linux 时,开启 X11 Forwarding 选项,ssh 连接到远程服务器,运行GUI程序:
# -X 选项开启X11 Forwarding
ssh -X user@host
# 运行远程 GUI 程序,界面将在本地电脑上显示出来
virt-manager
ssh 客户端是 windows 时,ssh 工具可以用 putty、xshell、securecrt 等等,x11 server 可以用 xshell 自带的,或者 xming、vcxsrv、cygwin x11 等。以 securecrt+xming 为例,先双击 Xming 图标,启动之后,图标在右下角(需要防火墙入站规则允许xming)。然后打开 Securecrt session 配置,按图开启 forward x11 选项。接下来,ssh连接到 Armbian 服务器,在#提示符下运行 virt-manager
命令,并等待十多秒钟,就会看到虚拟机管理的图形界面了。如下图所示:
注意:如果物理机只有 1 张网卡的话,要把 eth0 网络改成桥接,以便与虚机共用网卡。以 Armbian/Debian/ubuntu 为例:(其它操作系统请自行查询网桥配置方式)。
/etc/network/interfaces.d/br0
# eth0 setup
allow-hotplug eth0
iface eth0 inet manual
pre-up ifconfig $IFACE up
pre-down ifconfig $IFACE down
# Bridge setup
auto br0
iface br0 inet static
bridge_ports eth0
bridge_stp off
bridge_waitport 0
bridge_fd 0
address 192.168.3.22
broadcast 192.168.3.255
netmask 255.255.255.0
gateway 192.168.3.1
dns-nameservers 192.168.3.1
物理机有 2 张网卡时,eth1 可以提供给虚拟机做 macvtap 口,但此时物理机自身就不能再使用 eth1 了,需要停用 NetworkManager.service, 并把 eth1 设置为手动:
/etc/network/interfaces.d/eth1
allow-hotplug eth1
iface eth1 inet manual
pre-up ifconfig $IFACE up
pre-down ifconfig $IFACE down
systemctl restart networking.service
如果NetworkManager.service
是启用状态的话,需要关闭 NetworkManager.service
systemctl stop NetworkManager.service
systemctl disable NetworkManager.service
init 6
qemu
的固件镜像后缀是 .qcow2
,如果下载得到的格式是.7z/.zip/.gz/.xz等,则需要先解压。把镜像上传到物理机的 /var/lib/libvirt/images/
目录下,名字可以任意改,如 openwrt.qcow2
等。运行 virt-manager
命令启动 GUI 图形界面(或桌面环境下点击 虚拟机管理
图标)。
当虚拟机成功启动之后,即可关闭virt-manager图形窗口,虚拟机仍然在后台运行。第一次运行需要修改虚拟机的ip地址,可以用virsh命令连接到虚拟机的console:
virsh console openwrt
之后就进入openwrt的shell提示符,就如同在 ssh 或 ttyd 里一样,非常方便! 退出 virsh console 请按 ctrl + ] 快捷键, 即按下ctrl,再按]键。
常见故障如cpu模式不对、虚拟机服务未启动等解决方法如下。
提示 cpu mode host-mode not supported
,解决方法:把cpu模式手动改成 host-passthrough
(如果下拉选项没这个,那就手动输入),如下图:
使用 sudo systemctl status libvirtd
命令查看正常情况应该这样:
# 如果服务未激活,请手动激活并启动服务
sudo systemctl enable libvirtd
sudo systemctl restart libvirtd
sudo systemctl status libvirtd
解决方法:删除虚拟机重建,多试几次,或者给虚拟机改个名,如下图:
表现为:虚拟机能ping通主机,主机也能ping通虚拟机,但虚机ping不通外网。
解决方法1:物理机防火墙的默认规则会阻止虚拟机访问外网,标准的解决方案是在物理机的 /etc/sysctl.conf
里添加以下内容, 以禁用网桥上的 netfilter:
net.bridge.bridge-nf-call-ip6tables = 0
net.bridge.bridge-nf-call-iptables = 0
net.bridge.bridge-nf-call-arptables = 0
然后运行 sysctl -p
以上内容参考了 https://wiki.libvirt.org/page/Net.bridge.bridge-nf-call_and_sysctl.conf
解决方法2: 有另一种方法可以让所有路由桥接 KVM 虚拟机完全不受限制地访问互联网,而无需处理防火墙规则。
默认情况下,所有接口都绑定到公共防火墙区域。但是有多个区域,即firewall-cmd --list-all-zones
其中一个称为trusted
,这是一个未过滤的防火墙区域,默认情况下接受所有数据包。因此,您可以将网桥接口绑定到该区域。
firewall-cmd --remove-interface br0 --zone=public --permanent
firewall-cmd --add-interface br0 --zone=trusted --permanent
firewall-cmd --reload
解决方法3: 关闭防火墙服务
systemctl stop firewalld.service
systemctl disable firewalld.service
有条件的可以给虚拟机添加第2张网卡等,实现更多玩法,方法如下。
前提是物理机有多余的网卡可用,无论是usb扩展的还是pcie扩展的都行,假设物理机的第二张网卡是eth1,那么:
新添的网卡要关闭虚拟机之后才会出现,下次启动生效。
此功能可以把物理机的一个文件夹共享给虚拟机使用,实现物理机、虚拟机之间文件共享,或多个虚拟机之间文件共享,非常实用!首先要关闭虚拟机,把内存的shared memory选项打开。
然后添加硬件,选择“文件系统”, 驱动程序选择“virtiofs”, 源路径选择物理机上已存在的某个文件夹,目标路径随便编个名字(例如data)
然后启动虚拟机,在虚机中输入命令:
mkdir /mnt/data
mount -t virtiofs data /mnt/data
df -h
挂载成功,如果想要开机自动挂载的话,可以把挂载命令添加到 /etc/rc.local
里。
虽然对于openwrt没用,但对于其它linux发行版有用,如果想在armbian里运行另一个linux(debian,ubuntu,openSUSE,archlinux,centos,gentoo,国产麒麟,国产统信uos等等),那这一步是必需的,添加鼠标、键盘、显卡的方法如下:
这需要物理机支持iommu,一般的电视盒子就别想了,目前即使正规的arm64服务器也很少支持。
如果出现这个提示就是不支持直通。
用 virsh
命令可以对虚拟机进行很多操作。
virsh list # 显示已启动的虚拟机, 如果要显示所有虚机,用 virsh list --all
virsh edit vm_name # 修改虚拟机的配置文件(/etc/libvirt/qemu/vm_name.xml),有些更改会立即生效,而大多数更改需关闭虚拟机后才生效,此功能不建议初学者使用
virsh console vm_name # 连接到虚拟机的控制台(/dev/ttyAMA0), 可执行 shell 命令, 类似于 docker exec -it container bash 的功能
virsh start vm_name # 启动虚拟机
virsh start --console vm_name # 启动虚拟机,同时连接到虚拟机控制台
virsh reboot vm_name # 重启虚拟机,需要虚拟机中有 acpid 服务的支持,否则此命令无效。 建议在固件底包中加入 acpid
virsh shutdown vm_name # 正常停止虚拟机,需要虚拟机中有 acpid 服务的支持,否则此命令无效。 建议在固件底包中加入 acpid
virsh destroy vm_name # 强行停止虚拟机
virsh autostart vm_name
vi /etc/default/libvirt-guests
# set on boot action
ON_BOOT=start
# set on shutdown action
ON_SHUTDOWN=shutdown
注意:在类似于S922X、RK3399这样的大小核物理机下,虚拟机自动重启有一定概率不成功。可能会报如下错误:
kvm: kvm_init_vcpu: kvm_arch_init_vcpu failed (0): Invalid argument
解决方法:可以手动修改虚拟机控制文件,静态绑定 cpu 核心,示例如下:
运行 virsh edit vm_name(虚拟机名称),然后修改 vcpu 小节, 默认:
<vcpu placement='static'>6</vcpu>
改为:
<vcpu placement='static' cpuset='0-5'>6</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'/>
<emulatorpin cpuset='0-5'/>
</cputune>
假设虚拟机只想分配4核,2小核加2大核(以s922x为例, 0-1 是小核, 2-5是大核)
<vcpu placement='static' cpuset='0,1,4,5'>4</vcpu>
<cputune>
<vcpupin vcpu='0' cpuset='0'/>
<vcpupin vcpu='1' cpuset='1'/>
<vcpupin vcpu='2' cpuset='4'/>
<vcpupin vcpu='3' cpuset='5'/>
<emulatorpin cpuset='0,1,4,5'/>
</cputune>
修改完毕之后,要关闭虚拟机才生效。
优化建议:以 GT-King Pro (Amlogic S922X-H 为例), 保留物理机的 cpu0 不分配,而把 cpu1-5 分配给虚拟机,其配置如下:
<vcpu placement='static' cpuset='1-5'>5</vcpu>
<cputune>
<vcpupin vcpu='0' cpuset='1'/>
<vcpupin vcpu='1' cpuset='2'/>
<vcpupin vcpu='2' cpuset='3'/>
<vcpupin vcpu='3' cpuset='4'/>
<vcpupin vcpu='4' cpuset='5'/>
<emulatorpin cpuset='1-5'/>
</cputune>
本节内容参考了 华为云: 虚拟机绑核
运行 armbian-config, 选择 system -> cpu -> minfreq -> maxfreq -> governor ( 建议选择 schedutil )
另外, 对于一般的arm64 soc,采用内置网卡或usb网卡时,通常会挤占cpu0, 导致整体网络性能不佳(如果网卡是 pcie 接口,并且支持 rss 多队列的话则不存在此问题)
armbian 有一个系统服务: armbian-hardware-optimize.service , 可以对 softirq 等进行一些必要的优化,可以选择开启:
systemctl status armbian-hardware-optimize.service
systemctl enable armbian-hardware-optimize.service
systemctl start armbian-hardware-optimize.service
如果 armbian-hardware-optimize.service 没有达到预期效果,或是没采用 armbian的话,可以用我写的脚本: balethirq.pl + 配置文件:balance_irq demo:
cp balethirq.pl /usr/sbin
# 创建 /etc/balance_irq, 进行中断配置,简而言之就是把两张网卡产生的中断分散到两个cpu里(默认情况下是共用第1个cpu)
# 设备名称(devname) cpu亲和性(cpu affinity)
vi /etc/balance_irq
eth0 1
xhci-hcd:usb1 6
balance_irq配置参考:
root@gtking-pro:~# cat /proc/interrupts
CPU0 CPU1 CPU2 CPU3 CPU4 CPU5
9: 0 0 0 0 0 0 GICv2 25 Level vgic
11: 17580788 33822531 34847277 34198393 34319935 34518922 GICv2 30 Level arch_timer
12: 0 229777 183235 217133 194856 188719 GICv2 27 Level kvm guest vtimer
14: 0 0 0 640860 0 0 GICv2 40 Level eth0
15: 15 0 0 0 0 0 GICv2 89 Edge dw_hdmi_top_irq, ff600000.hdmi-tx
21: 0 0 0 0 0 0 GICv2 235 Edge ff800280.cec
22: 40 0 0 0 0 0 GICv2 225 Edge ttyAML0
23: 8 0 0 0 0 0 GICv2 228 Edge ff808000.ir
24: 0 0 0 0 0 0 GICv2 76 Edge vdec
25: 0 0 0 0 0 0 GICv2 64 Edge esparserirq
26: 314 0 0 0 0 0 GICv2 35 Edge meson
27: 1126 0 0 0 0 0 GICv2 71 Edge ffd1c000.i2c
28: 4778 0 0 0 0 0 GICv2 58 Edge ttyAML6
29: 3468797 0 0 0 0 0 GICv2 221 Edge ffe03000.sd
30: 0 0 0 0 0 0 GICv2 222 Edge ffe05000.sd
31: 5233 91355 0 0 0 0 GICv2 223 Edge ffe07000.mmc
33: 0 0 0 0 0 0 GICv2 194 Level panfrost-job
34: 0 0 0 0 0 0 GICv2 193 Level panfrost-mmu
35: 4 0 0 0 0 0 GICv2 192 Level panfrost-gpu
36: 0 0 0 0 0 0 GICv2 63 Level ff400000.usb, ff400000.usb
37: 535 0 3031088 0 0 0 GICv2 62 Level xhci-hcd:usb1
38: 1 0 0 0 0 0 meson-gpio-irqchip 26 Level mdio_mux-0.0:00
IPI0: 113124 140214 3226181 993988 1235145 1329681 Rescheduling interrupts
IPI1: 66072 703466 282906 331811 3737960 332876 Function call interrupts
IPI2: 0 0 0 0 0 0 CPU stop interrupts
IPI3: 0 0 0 0 0 0 CPU stop (for crash dump) interrupts
IPI4: 0 0 0 0 0 0 Timer broadcast interrupts
IPI5: 3761 15984 378310 823397 1323761 923798 IRQ work interrupts
IPI6: 0 0 0 0 0 0 CPU wake-up interrupts
Err: 0
需要注意到两张网卡: 内置网卡eth0
,以及外置网卡(USB RTL8153), 表现为 xhci-hcd:usb1
在上例的 balance_irq
里, eth0 -> cpu1
xhci-hcd:usb1 -> cpu6
在这里,cpu编号是从1开始,而不是从0开始
最后,把 /usr/sbin/balethirq.pl
添加到 /etc/rc.local
里以实现开机启动。
root@gtking-pro:~# cat /etc/rc.local
#!/bin/sh -e
#
# rc.local
#
# This script is executed at the end of each multiuser runlevel.
# Make sure that the script will "exit 0" on success or any other
# value on error.
#
# In order to enable or disable this script just change the execution
# bits.
#
# By default this script does nothing.
/usr/sbin/balethirq.pl
exit 0
root@gtking-pro:~# balethirq.pl
irq name:eth0, irq:14, affinity: 1
irq name:xhci-hcd:usb1, irq:37, affinity: 20
Set the rps cpu mask of eth0 to 0x1e
Set the rps cpu mask of eth1 to 0x1e
KVM虚拟机采用virtio-net-pci半虚拟化网卡驱动,在虚拟机中装载virtio_net模块,而在物理机中装载vhost_net模块。
- 使用多队列 virtio-net (即虚拟网卡开启rss),通过向虚拟机 XML 配置 queues='N'(最多可以与vcpu数量相等,建议从 N=2 开始尝试),运行
virsh edit vm_name
:
<interface type='network'>
<source network='default'/>
<model type='virtio'/>
<driver name='vhost' queues='N'/>
</interface>
本节参考了 虚拟化调试和优化指南
- 启用打包的虚拟队列
传统上,Virtio 在主机和虚拟机之间共享拆分队列。Packed virtqueues 是另一种紧凑的 virtqueue 布局,主机和来宾都可以读取和写入,在性能方面更有效。运行virsh edit vm_name
<interface type='network'>
<source network='default'/>
<model type='virtio'/>
<driver name='vhost' packed='on'/>
</interface>
本节参考了基于第三代英特尔® 至强® 可扩展处理器的平台上的 KVM/Qemu 虚拟化调整指南
本节和上节的方案可以联合使用,即:
<interface type='network'>
<source network='default'/>
<model type='virtio'/>
<driver name='vhost' queues='N' packed='on'/>
</interface>
- 启用 vhost-net 零拷贝
零复制传输(Bridge Zero Copy Transmit)模式对于大尺寸的数据包较为有效。通常在客机网络和外部网络间的大数据包传输中,它对主机 CPU 负荷的减少可达到 15%,对吞吐量没有影响。 它不对客机到客机、客机到主机或小数据包负载造成影响。
在 Linux 中,默认禁用 vhost-net 零拷贝。要永久启用此操作,请添加一个包含以下内容 vhost-net.conf 的新文件:/etc/modprobe.d/vhost-net.conf
options vhost_net experimental_zcopytx=1
然后重启系统,验证零拷贝是否开启:
cat /sys/module/vhost_net/parameters/experimental_zcopytx
值为1则启用,为0则未启用
本节参考了 虚拟化调试和优化指南
每次固件发布会有2个文件, 例如:openwrt_qemu-aarch64_generic_R22.7.7_k5.18.15-flippy-75+_update.img
和 openwrt_qemu-aarch64_R22.7.7_k5.18.15-flippy-75+.qcow2
其中,后缀为.qcow2的文件是首次创建虚拟机用的,而另一个后缀为.img的文件就用于升级的。
把 openwrt_qemu-aarch64_generic_vm_k5.18.13-flippy-75+.img 及附带的升级脚本上传至虚拟机的 /mnt/vda4
目录下(7z压缩包里也会同时包含一个升级脚本:update-kvm-openwrt.sh,与/usr/sbin/openwrt-update-kvm是同一个文件,但版本可能更新一些)
cd /mnt/vda4
/usr/sbin/openwrt-update-kvm openwrt_qemu-aarch64_generic_vm_k5.18.13-flippy-75+.img
# 或者
./update-kvm-openwrt.sh openwrt_qemu-aarch64_generic_vm_k5.18.13-flippy-75+.img
现在终于有后悔药可以吃了,送你个无敌风火轮...
内核升级即:只升级kernel,不升级openwrt的应用。
把 boot-xxxx.tar.gz
、modules-xxxx.tar.gz
两个内核压缩包上传至 /mnt/vda4
, 然后运行:openwrt-kernel-kvm
使用方法基本与 7.2 相同
默认的img镜像是 1057MB(SKIP_MB=16 BOOT_MB=16 ROOTFS_MB=1024 TAIL_MB=1, 16+16+1024+1=1057), 包括 efi + rootfs1 等2个分区;
默认的qcow2镜像,由于是动态分配,其初始尺寸比较小(<1057MB),但实际在虚拟机中的磁盘容量却很大(默认是 16385MB, QCOW2_MB="+15328M", 1057+15328=16385),且包含4个分区(efi + rootfs1 + rootfs2 + share), 在使用过程中,qcow2 占用物理机磁盘的空间会逐渐变大,直至撑满16385MB的最大尺寸。
在使用 mk_qemu-aarch64_img.sh 创建镜像时,可以自定义分区尺寸以符合个性化需求, 一般情况下可以修改 ROOTFS_MB(略大于 rootfs + kernel 解压以后所占的空间 * 0.6 即可,btrfs + zstd 压缩率大约是 40-50% )及 QCOW2_MB 两个变量, 例如:
ROOTFS_MB=640 QCOW2_MB="+2048M" ./mk_qemu-aarch64_img.sh
上述命令可创建出 16+16+640+1=673MB 的 img 镜像,以及 673+2048=2721MB 的 qcow2 镜像, 在qcow2镜像中,4个分区尺寸分别为: 16MB、640MB、640MB、1408MB