上手 Linux 原生的虚拟机工具 QEMU/KVM

因为疫情的关系有了更加自由地支配工作时间的机会,花了更多的时间来折腾工作工具。看网上说 QEMU/KVM 比 VirtualBox 的性能更好,就一直想尝试一下看看怎么样。这次就折腾虚拟机工具了。这里是一些关键操作记录和问题解决方法。

准备材料

Win10 安装源

这一次我打算直接上 Windows 10 了,使用了网上推荐的 Windows 10 Enterprise LTSC 版本。该版本算是微软官方的精简版,移除了微软商店(如果想使用 UWP 应用的话网上倒是也有办法来解决)、Edge 浏览器、智能助手小娜等,而且不提供功能性更新,只有十年时间的安全性补丁更新。使用该版本几乎意味着可以等换电脑的时候再更换/更新操作系统了。目前是 Windows 10 Enterprise LTSC 2019 版。微软官方提供的下载需要先在网上注册。不想注册的话可以去 MSDN ITellYou 看看。但是不管怎么样,安装这个都是需要激活的。目前微软提供 90 天的试用。而且据说可以连续试用三次。注册激活的命令操作参考了这里的方法,

slmgr /skms {kms服务器地址} //设置密钥服务器
slmgr /ipk M7XTQ-FN8P6-TTKYV-9D4CC-J462D //设置微软官方密钥
slmgr /ato //连接kms服务器激活
slmgr /xpr //查询激活到期时间

这个系统中安装运行 Falkon 浏览器的时候会提示 MSVCR120.DLL 丢失。此时需要安装 Microsoft Visual C++ 2013 再分发软件包,下载地址点这里。这是个精简的软件包,安装文件大约六七兆。

VirtIO 驱动

当然还有 Fedora 项目提供的 virtio 驱动。下载似乎移动到了 GITHUB 上的这里。没有驱动的话,安装操作系统都是个问题。

SPICE 工具

注意:Balloon 功能(安装后以 Windows 服务运行,让用户可以使用 virsh 来监控虚拟机资源占用)可能会导致 WMI Provider Host 服务(进程)占用较高的 CPU 资源

系统安装完成之后还需要在 Windows 10 客户机中下载和安装 SPICE 工具。参考链接如下:

  • Windows 客户机工具(仅当客户机为 Windows 系统时使用)
    • 页面:https://www.spice-space.org/download/binaries/spice-guest-tools/
    • 直接下载:https://www.spice-space.org/download/binaries/spice-guest-tools/spice-guest-tools-latest.exe
  • Windows QXL-WDDM-DOD 显示驱动
    • 页面:https://www.spice-space.org/download/windows/qxl-wddm-dod/
    • 直接下载(可能不是最新版本):https://www.spice-space.org/download/windows/qxl-wddm-dod/qxl-wddm-dod-0.21/QxlWddmDod_0.21.0.0_x64.msi

在 openSUSE Tumbleweed 中安装 QEMU/KVM

openSUSE 有专门的 pattern 来安装相关软件包。

$ sudo zypper install -y patterns-server-kvm_server patterns-server-kvm_tools bridge-utils

下面说的声音配置需要 qemu-audio-pa,但是默认没有安装,这里先安装上,

$ sudo zypper install -y qemu-audio-pa

启动 QEMU/KVM

在 Konsole 中开启 libvirtd 服务,

sudo systemctl start libvirtd && sudo systemctl enable libvirtd

QEMU/KVM 的图形化管理界面(类似于 VirtualBox 的图形界面)是 Virtual Machine Manager(virt-manager)。这个是基于 GTK 的。虽然有个 qt-virt-manager,可还没有进入 openSUSE 的官方软件仓库。安装完成之后居然没有自动创建桌面文件,所以我通过命令行启动管理器,

virt-manager

如果不希望每次都要输入 root 密码来启动 virt-manager,参考这里,可以将当前普通用户 usernamexxx 加入 libvirt 用户组和 qemu 用户组(虚拟机以 qemu 用户身份运行),

sudo usermod -a -G libvirt usernamexxx
sudo usermod -a -G qemu usernamexxx

同时使用 ACL 配置该用户对 QEMU/KVM 虚拟机磁盘文件的读写权限。对于虚拟磁盘文件,则必须可读可写,

sudo setfacl -R -m u:usernamexxx:rwx /path-to-virtual-disks
sudo setfacl -R -m u:qemu:rwx /path-to-virtual-disks

/path-to-virtual-disks 默认是 /var/lib/libvirt/images,也可以自己将虚拟磁盘放置在其它地方,比如 ~/kvm-images

配置 QEMU/KVM

按照操作顺序,有如下几点基本配置。大部分都可以通过 virt-manager 提供的图形界面来设置。

– 创建 connection。File > Add Connection…。有多种可选的模式,一般选择 QEMU/KVM 或者 QEMU/KVM user session。前一种在 root 下运行,后一种运行在用户空间。创建的时候可以勾选 Autoconnect,这样在以后启动 Virt-Manager 的时候就会自动连接。

– 配置存储池(storage poll)。默认的存储池在 /var/lib/libvirt/images。可以删除,新建一个自己指定的位置存放虚拟机的虚拟磁盘。甚至可以指定本地位置,然后通过符号连接(ln)将 qcow2 磁盘放在外接驱动器上。因为虚拟磁盘一般都是几十个GB,需要考虑一下空间利用的问题。另外,默认的虚拟机状态保存位置在 /var/lib/libvirt/qemu/save,也可以删除该文件夹并通过符号连接将其改写到其它位置。后面有介绍。

在安装虚拟机客户机系统之前,直接先创建一个 qcow2 格式的虚拟磁盘。虽然都说 raw 格式的性能更高,但是虚拟机又不是要搞大量读写的常用机,我还是更在意硬盘占用。参考这里

sudo qemu-img create -f qcow2 -o cluster_size=2M /data/kvm-images/win10.qcow2 30G

– 创建虚拟机。这个按照提示一步步操作即可。参考:https://cubiclenate.com/2019/06/11/virtual-machine-manager-with-qemu-kvm-on-opensuse-tumbleweed/

– 默认的网络是 NAT 的,也可以使用 bridge-utils 来创建一个桥接的网络。如果使用 NAT 模式,则几乎不需要主动配置。关于 NAT 和桥接模式的区别,可以参考之前记录的在 VirtualBox 中配置网络的文章。也可参考这里。但是很遗憾的是,如果主机使用无线网络,则无法为客户机配置这样的桥接模式(参见 wiki.libvirt.org)。如果要创建桥接的话,需要注意的是,如果主机使用 NetworkManager 来管理网络,则应尽可能使用 NM 来创建桥接,否则可能会导致 NM 被禁用(参见 help.ubuntu.com)。

– 启用 Boot 选项。展开启动选项,将 Windows 10 安装光盘选上,将光驱也作为启动介质之一。同时再添加一个新的 SATA CDROM 设备(+Add HardWare > Storage,将 Device Type 改为 CDROM Device),也将前面下载的 Fedora 项目提供的 virtio 驱动光盘挂载上。

访问虚拟机系统

访问客户机的途径有 spice、vnc / rdp。openSUSE 中默认是使用 Spice。如果没有自动打开的话,也可以从命令行访问,

virt-viewer -c qemu:///system {{domain}}

还可以远程访问。比如查看远程主机上的虚拟机,

virsh -c qemu+ssh://root@homeserver.local/system list --all

启动一个客户机,

virsh -c qemu+ssh://root@homeserver.local/system start {{domain}}
Domain {{domain}} started

查看虚拟机中客户机桌面,

virt-viewer -c qemu+ssh://homeserver.local/system

安装 Windows 10

很久没有帮人安装 Windows 了,突然发现 Windows 10 的安装非常简单快捷。不过在使用了 VirtIO 的 QEMU/KVM 中安装 Windows 时需要先加载 VirtIO SCSI 控制器驱动。

virt-manager 提供的图形界面启动虚拟机。如果运行时遇到类似于下面的错误:

Unable to complete install: 'internal error: process exited while connecting to monitor: ioctl(KVM_CREATE_VM) failed: 16 Device or resource busy2020-04-23T07:04:10.478407Z qemu-system-x86_64: failed to initialize KVM: Device or resource busy'

则需可能是因为系统中正在运行别的虚拟机程序。根据 Brad 的说法,这多半是因为系统中正在运行其它的虚拟服务器,例如 VirtualBox。关掉那个 VirtualBox 虚拟机果然就没有问题了。

启动虚拟机后,此时会从刚才 Boot 选项中配置的 Windows 安装光盘启动。单击「现在安装」之后会提示找不到驱动器。此时选择「加载驱动程序」,找到 virtio 驱动光盘里的 VirtIO SCSI 控制器驱动(viostor\win10\amd64),如下列图片所示。

剩下的就是愉快地玩耍 Windows 10 了。

但是配置虚拟机里的 Windows 10 就是另外一个大坑了,需要各种优化甚至损失安全特性才能让它运行得顺化,例如,

  • 关闭防病毒组件 Windows Defender 的实时防护可以让运行安装在映射的网络驱动器上的程序不那么痛苦,还可以让虚拟磁盘文件的更新数据量不那么大;
  • 关闭文件索引可以让访问网络驱动器更顺畅;
  • 关闭鼠标指针轨迹可以让鼠标移动的时候不会有拖曳感;
  • 最好关闭自动更新,改为人工不定时更新;
  • ……

进一步的设置

共享宿主机文件夹给客户机

虽然 Spice 提供了通过 Spice WebDAV 直接共享文件夹的功能(只能在 virt-viewer 的菜单中访问到),但是我没有配置成功。配置方法参考了,

  • https://www.spice-space.org/spice-user-manual.html#_folder_sharing
  • https://www.guyrutenberg.com/2018/10/25/sharing-a-folder-a-windows-guest-under-virt-manager/

可是配置了之后,在虚拟机的信息里(virt viewer) Channel spice-webdav 一直都是未连接状态 (disconnected)。决定还是在主机里面创建一个密码保护的私有 Samba 共享来给客户机使用。参考了这篇文章来配置宿主机的 Samba 服务。下面是网络方面的设置。

在宿主机 openSUSE 中运行下面的指令看看默认的用于 KVM 虚拟机的防火墙 ZONE 是不是 libvirt,

sudo firewall-cmd --get-active-zones

确定之后,然后运行下面的指令,在 libvirt ZONE 上面开放 Samba 共享服务端口,

sudo firewall-cmd --zone=libvirt --add-service=samba

可以通过下面的指令来检查,

$ sudo firewall-cmd --zone=libvirt --list-services 
dhcp dhcpv6 dns samba ssh tftp

然后到客户机添加网络位置(参考探索 Windows 10 中映射网络驱动器的各种坑),成功的话给上面的防火墙规则加上 --permanent 再运行一次,如

$ sudo firewall-cmd --zone=libvirt --add-service=samba --permanent

添加访问 Samba 服务所需端口,

$ sudo firewall-cmd --zone=libvirt --add-port=137-139/tcp permanent
$ sudo firewall-cmd --zone=libvirt --add-port=137-139/udp permanent
$ sudo firewall-cmd --zone=libvirt --add-port=445/tcp permanent
$ sudo firewall-cmd --zone=libvirt --add-port=445/udp permanent

外部快照 vs. 内部快照

QEMU/KVM 有两种快照(备份)机制,称为外部快照(external snapshot)和内部快照(internal snapshot)。简单讲,外部快照就像是 VirtualBox 中的快照,将新内容和更改的内容都存储在一个新的虚拟磁盘中。而内部快照则是利用 COW 机制在当前的虚拟磁盘中创建一个记忆点(类似于 Windows 的系统还原)。详细说明可以参考这里

在当前主机上创建外部快照,参考了这里描述的操作。另外可以参考关于内部和外部快照、base 和 top、前向合并和后向合并的概念:Snapshots Handouts。这篇文章较旧了,但是讲得非常清晰。

对 QEMU/KVM 来说,管理外部快照的操作通过命令行似乎更方便。当然也可以写成脚本文件来批处理。

例如,

DOMAIN=Win10work
SNAPSHOT_NAME="Win10work_2_install_base_work_suite"
VM_FOLDER="/data/kvm-images"
SNAPSHOT_FOLDER="`echo $VM_FOLDER`/`echo $DOMAIN`/snapshots"
mkdir -p $SNAPSHOT_FOLDER
virsh snapshot-create-as \
--domain $DOMAIN $SNAPSHOT_NAME \
--diskspec vda,file=$DISK_FILE,snapshot=external \
--disk-only \
--atomic

其它关于快照的参考文章:

  • http://teknoarticles.blogspot.com/2018/12/create-and-restore-external-backups-of.html
  • https://blog.programster.org/kvm-external-snapshots
  • https://elvishomelab.com/2019/09/16/kvm-external-snapshots-and-backups-part-1-using-virsh-blockcommit/
  • https://wiki.libvirt.org/page/I_created_an_external_snapshot,_but_libvirt_will_not_let_me_delete_or_revert_to_it

容灾备份

外部快照用来做备份是非常合适的。在搞一堆定制之前先做个外部备份。所谓外部备份是在虚拟机之外来备份,比如备份其配置文件(xml)和虚拟磁盘文件(这里是 qcow2 文件)。将它们保存到外部驱动器上,可以用来作为该虚拟机的容灾备份,也可以用来在别的电脑上建虚拟机,这样就不需要再安装一次系统。

查询该虚拟机的磁盘,

virsh domblklist {{domain}} --details | grep 'disk' | awk '{print $3}'

将其中 {{domain}} 替换成虚拟机的名字,下同。如 Windows10。显示的结果应该是类似于 vda、hda 之类的。这个在上面的命令输出上是叫做 Target 的。注意不是文件路径。这里假设就是 vda,下面要用到的时候会以 {{DISK_NAME}} 代替。

先备份虚拟机配置文件,

virsh dumpxml --inactive {{domain}} > /path/to/backups/{{domain}}_backup.xml

然后就可以将该虚拟机的那个虚拟磁盘文件复制到别的地方了,比如 U 盘或者家庭服务器上。需要注意的是,如果有外部快照,则根据引用关系,靠后面的快照,备份时也需要同时备份前面的快照。

virsh blockcopy {{domain}} {{DISK_NAME}} /path/to/backups/{{domain}}_backup.qcow2 --wait --verbose --finish

声音配置

关于声音配置,这里有较为详细的说明。但是实际上,随着 QEMU 4.2 的发布,这个声音的的配置就简单多了。可以参考 Archlinux 上的说明来操作。

如果想使用 PulseAudio (pa)来支持声音,还需要安装 qemu-audio-pa。默认没有安装。据说 pa 带来的声音延迟比较大。但是 Spice 自带的声音服务延迟更大,而且是必须开着显示窗口才有声音。另外,我遇到的虚拟机中的声音输入会在运行一小段时间之后不工作的问题,比如钉钉直播中的麦克风过一会儿之后就没反应了,也许也是这个造成的。

QEMU 4.2 以后的声音配置就比较简单了。首先需要修改 QEMU 的配置文件,告诉它使用哪个 Linux 系统用户的 pulseaudio 源。将 QEMU 配置文件 /etc/libvirt/qemu.conf 中的 #user = "root" 开头的那一行取消注释并将等号后面的 root 换成自己的用户名,如这里的 usernamexxx

该配置除了可以在通过命令行启动时指定。我们使用 libvirt 来配置的话就需要使用 Virtual Machine Manager(virt-manager)图形界面管理器中的 XML 编辑功能。当然也可以直接在命令行编辑 virsh edit {domain-name}。在虚拟机 {domain-name} 的 XML 配置文件的开头有如下的标记,

<domain xmlns:qemu="http://libvirt.org/schemas/domain/qemu/1.0" type="kvm">

其中 xmlns:qemu="http://libvirt.org/schemas/domain/qemu/1.0" 默认情况下是没有的,需要自行添加。否则会导致下面的修改在保存后丢失。

在虚拟机 {domain-name} 的 XML 配置文件中看看是否有 pc-q35-4.2 或者 pc-i440fx-4.2 字样。新版本的 libvirt 和 qemu 创建的虚拟机默认情况下基本就是这两者之一了。

然后,在 XML 文件结尾处的 <qemu:commandline></qemu:commandline> 两行之间增加下面的内容。如果没有这两行就增加这两行,一定要注意行首的缩进。下面标记为蓝色的部分就是针对 pulseaudio 的传入的。整体上看来是下面的这种感觉(已有的就不要重复添加了)。

  </devices>
  <qemu:commandline>
    <qemu:arg value="-cpu"/>
    <qemu:arg value="host,hv_time,kvm=off,hv_vendor_id=null"/>
    <qemu:arg value="-device"/>
    <qemu:arg value="ich9-intel-hda,bus=pcie.0,addr=0x1b"/>
    <qemu:arg value="-device"/>
    <qemu:arg value="hda-micro,audiodev=hda"/>
    <qemu:arg value="-audiodev"/>
    <qemu:arg value="pa,id=hda,server=unix:/run/user/1000/pulse/native"/>
  </qemu:commandline>
</domain>

其中那个 1000 是使用虚拟机的用户的 id。

pulseaudio 的配置没必要更改,默认的就是。Windows 里的也没必要改,默认的就是一样的。不过虚拟机配置里面之前默认创建的音频硬件可以需要删除了。在 Virtual Machine Manager 的图形界面上,虚拟机详情那里的侧边栏中可以看到的那个 Sound HDA(ICH9),右键单击选删除,保存配置即可。

另外,如果使用麦克风的时候需要降噪,可以试试,

echo 'load-module module-echo-cancel aec_method=webrtc aec_args="analog_gain_control=0 digital_gain_control=1"' >> /etc/pulse/default.pa

然后重启 pulseaudio 服务或者重启系统,

pulseaudio -k

重定向USB设备

如果希望将某个 USB 设备直接转给客户机(会自动从宿主机中卸载)的时候遇到类似下面的错误信息,

usb redirection error g-exec-error-quark spice-client-glib-usb-acl-helper permission denied

根据 Reddit 上的讨论,运行虚拟机的宿主机用户 usernamexxx 除了需要在 libvirt 用户组之外还需要在 kvm 用户组中。

usermod -a -G kvm usernamexxx

如果重定向的 USB 设备过多,会提示「no free USB channels」。此时可以切换到虚拟机详细信息页面,从左边栏的底部选择「+Add Hardware」,在弹出的窗口中点选左边栏中的「USB Redirection」,右边会显示类型 Type 为「Spice channel」,然后单击完成即可。这个不需要重启虚拟机就能生效。

变更 save 路径

前面提到了保存虚拟机运行状态时,临时文件的保存位置可以使用符号连接来更改位置,下面根据这里的评论提供一个示例,

$ sudo rmdir /var/lib/libvirt/qemu/save
$ mkdir -p /path/to/new_save
$ sudo chown usernamexxx:qemu /path/to/new_save
$ sudo ln -s /path/to/new_save /var/lib/libvirt/qemu/save

同时,为了方便普通用户查看保存的文件,还可以将 qemu 配置为使用普通用户的身份运行(在当前系统只有一个普通用户使用的情况下,这里假设为 usernamexxx),

sudo sed '^#user = "root"/a user = "usernamexxx"' /etc/libvirt/qemu.conf

调整 QXL 显示的内存

使用 VirtIO 会有 3D 加速,使用后面提到的显卡passthrough更可以让客户机使用高性能显卡,但是使用 QXL 可以让虚拟机显示大小跟随窗口大小自动调整。

显存的最小用量跟屏幕分辨率有关。参考这里,给 Windows 客户机的推荐值是,

vgamem = screen_width * screen_height * 4 byte
ram = 4 * vgamem
vram unimportant (can be e.g. 8 MB)

所以我这里调整为,

vgamem = 2560 * 1440 * 4 / 1024 / 1024= 14745600 = 14.0625 MB 约 16MB = 16384 KB
ram = 4 * vgamem = 64 MB = 65536 kB
vram unimportant (can be e.g. 8 MB)

看来 libvirt 的自动计算值还是挺靠谱的。本着多了也没啥浪费的原则, 目前配置的是比这个估测值大了 4 倍。

调整 clock 参数

解决客户机没什么任务但是宿主机中 qemu-system-x86 却占用较高 CPU 资源(我这里是约 35%)的问题。

参考 redditaskubuntu 上的讨论,直接修改 XML 文件,将其中的,

<clock offset='localtime'>
    <timer name='rtc' tickpolicy='catchup'/>
    <timer name='pit' tickpolicy='delay'/>
    <timer name='hpet' present='no'/>
    <timer name='hypervclock' present='yes'/>
</clock>

修改为,

<clock offset='localtime'>
    <timer name='rtc' tickpolicy='delay'/>
    <timer name='pit' tickpolicy='discard'/>
    <timer name='hpet' present='yes'/>
    <timer name='hypervclock' present='yes'/>
</clock>

即可将客户机空闲时宿主机中的 qemu-system-x86 占用 CPU 资源降到约 5% 以下。

参考的资料

直接的参考资料都已经在文中给了链接了。其它的还有一些,最权威的当然就是那几个官方文档了。还有一些网友的经验总结和社区问答。都一并列在下面。

  • https://virt-manager.org/
  • https://doc.opensuse.org/documentation/leap/virtualization/single-html/book.virt/index.html
  • https://wiki.archlinux.org/index.php/QEMU
  • https://wiki.archlinux.org/index.php/Libvirt
  • https://jlk.fjfi.cvut.cz/arch/manpages/man/qemu.1
  • https://www.thegeekyway.com/kvm-vs-qemu-vs-libvirt/
  • https://www.tecmint.com/install-and-configure-kvm-in-linux/
  • https://www.packetflow.co.uk/what-is-the-difference-between-qemu-and-kvm/

我都快折腾完了,结果看到了一篇详尽的介绍,虽然其主要目的是介绍 VGA passthrough 的配置。但是没有细看,不知道怎么样。不过看排版就觉得写得非常用心。

再解决一些问题

后记

其实 VirtualBox 很好用,特别是它的沉浸模式(无缝模式)可以将 Windows 的任务栏显示在 KDE 的任务栏上面而不用单独开个窗口。这种模式下,开始菜单、打开的窗口以及任务栏图标都跟在单独的虚拟机窗口中没什么分别,特别方便。QEMU/KVM 却还没有这种呈现模式。下决心尝试 QEMU/KVM 是因为 VirtualBox 中的 Windows 7 里安装的钉钉居然无法开启直播 —— 当然后来发现即使在 QEMU/KVM 中也是不行的,限制来源于 Windows 而不是虚拟机(关于这一点,后来通过使用LTSC版本的Windows 10来解决了,可能是因为 Windows 10 支持较为高级的图形模式)。©

本文发表于水景一页。永久链接:<http://cnzhx.net/blog/try-to-use-qemu-kvm-on-opensuse-tumbleweed/>。转载请保留此信息及相应链接。

4 条关于 “上手 Linux 原生的虚拟机工具 QEMU/KVM” 的评论

  1. 引用通告: 转换 VirtualBox 的 VDI 虚拟硬盘到 QEMU/KVM | 水景一页

  2. 引用通告: KVM 客户机 Windows 系统中的光标不随手写板笔尖移动 | 水景一页

  3. 引用通告: virt-manager 的 AT-SPI 服务警告 | 水景一页

时间过去太久,评论已关闭。
如果您有话要说,请到讨论区留言并给出此文章链接。
谢谢您的理解 :-)