最佳实践

概述

参考:

注意:本最佳实践仅适用于独立使用 qemu-img、qemu-system 等 KVM/QEMU 的命令行工具,不包括 libvirtd 的工具

QEMU 命令行的一般形式可以表示为:

$ qemu-system-x86_64 [machine opts] \
                [cpu opts] \
                [accelerator opts] \
                [device opts] \
                [backend opts] \
                [interface opts] \
                [boot opts]

在下面的示例中,我们首先定义一台机器,它是用于运行 Aarch64 来宾的通用平台。我们启用虚拟化,因此我们可以在模拟来宾中使用 KVM。由于机器带有一些内置的 pflash 设备,我们给它们命名,以便我们稍后可以覆盖默认值。virtvirt

$ qemu-system-aarch64 \
   -machine type=virt,virtualization=on,pflash0=rom,pflash1=efivars \
   -m 4096 \

然后,我们使用为我们提供 QEMU 能够模拟的所有 Arm 功能的选项定义 4 个 vCPU。我们启用了更加仿真友好的 Arm 指针身份验证算法实现。我们明确指定 TCG 加速,即使 QEMU 无论如何都会默认为它。

-cpu max,pauth-impdef=on \
-smp 4 \
-accel tcg \

由于平台没有任何默认网络或存储设备,我们需要定义它们。我们给他们 id 以便我们稍后可以将他们与后端链接。

-device virtio-net-pci,netdev=unet \
-device virtio-scsi-pci \
-device scsi-hd,drive=hd \

我们将用户模式网络连接到我们的网络设备。由于无法从外部直接访问用户模式网络,我们将本地主机端口 2222 转发到访客上的 ssh 端口。

-netdev user,id=unet,hostfwd=tcp::2222-:22 \

我们将来宾可见块设备连接到为来宾预留的 LVM 分区。

-blockdev driver=raw,node-name=hd,file.driver=host_device,file.filename=/dev/lvm-disk/debian-bullseye-arm64 \

然后我们告诉 QEMU 将 QEMU Monitor 与串行端口输出进行多路复用(我们可以使用字符后端多路复用器中的键在两者之间切换)。由于没有默认的图形设备,我们禁用了显示,因为我们可以完全在终端中工作。

-serial mon:stdio \
-display none \

最后,我们覆盖默认固件以确保我们有一些存储空间供 EFI 保留其配置。该固件负责查找磁盘、引导 grub 并最终运行我们的系统。

-blockdev node-name=rom,driver=file,filename=(pwd)/pc-bios/edk2-aarch64-code.fd,read-only=true \
-blockdev node-name=efivars,driver=file,filename=$HOME/images/qemu-arm64-efivars

全部(有问题)

qemu-system--x86_64 \
-machine type=virt,virtualization=on,pflash0=rom,pflash1=efivars \
-m 4096 \
-cpu max,pauth-impdef=on \
-smp 4 \
-accel tcg \
-device virtio-net-pci,netdev=unet \
-device virtio-scsi-pci \
-device scsi-hd,drive=hd \
-netdev user,id=unet,hostfwd=tcp::2222-:22 \
-blockdev driver=raw,node-name=hd,file.driver=host_device,file.filename=/dev/lvm-disk/debian-bullseye-arm64 \
-serial mon:stdio \
-display none \
-blockdev node-name=rom,driver=file,filename=(pwd)/pc-bios/edk2-aarch64-code.fd,read-only=true \
-blockdev node-name=efivars,driver=file,filename=$HOME/images/qemu-arm64-efivars

TODO:

qemu-system-x86_64 qemu-install-test.qcow2 \
-m 4096 \
-cdrom ./CentOS-7-x86_64-DVD-2009.iso \
-smp 2 \
-accel tcg \
-device virtio-net-pci,netdev=unet \
-device virtio-scsi-pci \
-netdev user,id=unet,hostfwd=tcp::2222-:22 \
-serial mon:stdio \
-vnc 0.0.0.0:10

应用示例

准备连接虚拟机的工具

  • 使用 vnc 客户端工具(vnc-viewer 等…) 连接并安装系统即可。
  • 我们也可以通过各种终端(XShell 等)的 X11 转发连接。在宿主机系统中安装 tigervnc 后,在系统下执行如下命令即可。
vncview :3

前期准备

创建 bridge 网络设备,并配置 ip,然后将宿主机的外部网卡关联的网络设备加入到 bridge 上。

export IPADDR="172.19.42.249/24"
ip link add br0 type bridge
ip addr add ${IPADDR} dev br0
ip link set dev eth0 master br0

创建一个 qcow2 格式的镜像文件,用作块设备

export VM_QCOW2_FILE="/var/lib/libvirt/images/test/qemu-install-test.qcow2"
qemu-img create -f qcow2 ${VM_QCOW2_FILE} 10G

准备一个用于安装系统的 iso

export OS_ISO_PATH="/root/iso/CentOS-7-x86_64-DVD-2009.iso"

启动虚拟机并安装系统

使用 qemu-install-test.qcow2 文件启动虚拟机,并挂载系统镜像。

注意

  • 如果不分配内存,会提示无法加载 VFS 导致无法启动微型系统来安装系统。默认模拟 1 个 CPU。
  • :3 为 vncview 的访问时的端口,3 默认为 5903
qemu-system-x86_64 ${VM_QCOW2_FILE} \
-m 4096 \
-smp 1 \
-vnc 0.0.0.0:3 \
-cdrom ${OS_ISO_PATH}

连接虚拟机

完成安装

安装完成后,不用指定 cdrom 即可启动虚拟机

qemu-system-x86_64 ${VM_QCOW2_FILE} -m 4096 -vnc 0.0.0.0:3

启动一个正常可用的虚拟机

生成脚本

生成 -netdev 选项所用的启动脚本

cat > /etc/qemu-ifup <<\EOF
#!/bin/bash
BRIDGE=br0
if [ -n $1 ]; then
  ip link set dev $1 master ${BRIDGE}
  ip link set dev $1 up
[ $? -eq 0 ] && exit 0 || exit 1
else
  echo "Error: no interface specified."
exit 1
fi
EOF
chmod 755 /etc/qemu-ifup

启动虚拟机

qemu-system-x86_64 -m 4096 -smp 2 -name test \
-drive file=${VM_QCOW2_FILE},format=qcow2,if=virtio \
-netdev tap,id=n1 \
-device virtio-net,netdev=n1 \
-vnc :3

连接虚拟机

结语

现在是使用 qemu-system-x86_64 工具自动创建的 tap 设备,若是使用已经已经存在的网络设备,那么还需要创建一个 downscript 脚本,以便可以在虚拟机关闭时,自动处理,将网络设备从 bridge 上拆下来,否则下次再次启动,网络设备已经在 bridge 上,就会报错,导致虚拟机无法启动。

通过 virsh domxml-to-native 命令,转换出 wiki 来的 qemu-system 命令行其中一部分

其中使用 ... 省略了很多无用参数

# 使用 qemu-system 程序创建一个名为 desistdaydream.bj-net 的虚拟机
qemu-system-x86_64 -name desistdaydream.bj-net
# 虚拟机使用哪种类型的机器,这里是 i440fx 红帽7
-machine pc-i440fx-rhel7.0.0,accel=kvm,usb=off,dump-guest-core=off
# 虚拟机所使用的 CPU 类型
-cpu Skylake-Server-IBRS,-ds,-acpi,+ss,-ht,-tm,-pbe,-dtes64,-monitor,-ds_cpl,-vmx,-smx,-est,-tm2,-xtpr,-pdcm,-dca,-osxsave,-tsc_adjust,+clflushopt,-intel-pt,+pku,-ospke,+avx512vnni,+md-clear,+stibp,+ssbd,+hypervisor,-arat
# 虚拟机的内存大小
-m 4096
-realtime mlock=off
# 虚拟机有2个CPU,模拟成2个插槽,每个插槽的CPU有一个核心,每个核心1个线程
-smp 2,sockets=2,cores=1,threads=1
......
# 虚拟机中 virtio-blk-pci 设备,是 VM 的硬盘
# 宿主机中 qcow2 文件,id 为 drive-virtio-disk0
# 两者通过 drive 中的 id参数 与 device 中的 drive 参数保持一致,进行关联
-drive file=/var/lib/libvirt/images/master-3.bj-net.qcow2,format=qcow2,...,id=drive-virtio-disk0,...
-device virtio-blk-pci,...,drive=drive-virtio-disk0,...

# 略 drive 中的 id 与 device 中的 drive 相同。
-drive if=none,id=drive-ide0-0-0,...
-device ide-cd,bus=ide.0,...,drive=drive-ide0-0-0,...

# 虚拟机中 virtio-net-pci 设备,是 VM 中的网卡
# 宿主机中 tap 设备,id 为hostnet0
# 两者通过 netdev 中的 id参数 与 device 中的 netdev 参数保持一致,进行关联。
-netdev tap,...id=hostnet0,...
-device virtio-net-pci,...,netdev=hostnet0,...

# 略,chardev 中的 id 与 device 中的 chardev 相同
-chardev pty,id=charserial0
-device isa-serial,chardev=charserial0,...

# 略,chardev 中的 id 与 device 中的 chardev 相同
-chardev socket,id=charchannel0,path=/var/lib/libvirt/qemu/channel/target/domain--1-desistdaydream.bj-net/org.qemu.guest_agent.0,server,nowait
-device virtserialport,bus=virtio-serial0.0,nr=1,chardev=charchannel0,id=channel0,name=org.qemu.guest_agent.0
.....

最后修改 May 15, 2024: git, gitlab (c414ecd2)