22.6.使用 FreeBSD 上的 bhyve 虚拟机

BSD 许可的 bhyve 虚拟机管理器随着 FreeBSD 10.0-RELEASE 已经成为了底层系统的一部分。这个虚拟机管理器支持数种客户机,包括 FreeBSD,OpenBSD 和数个 Linux® 发行版。默认情况下,bhyve 提供对串行控制台的访问而不模拟图形化控制台。其利用较新的 CPU 上的 virtualization offload 功能来避免使用过时方法翻译指令和手动管理内存映射。

bhyve 的设计需要支持 Intel® Extended Page Tables(EPT)或 AMD® Rapid Virtualization Indexing(RVI)或 Nested Page Tables(NPT)的 CPU。以超过一枚 vCPU 运行 Linux 或者 FreeBSD 客户机需要 VMX unrestricted 模式支持(UG)。大多数新的处理器,特别是 Intel® Core™ i3/i5/i7 和 Intel® Xeon™ E3/E5/E7 都支持这些功能。对 UG 的支持是从 Intel® 的 Westmere 微架构引入的。若要查阅一份完整的支持 EPT 的 Intel® 处理器列表,请参见 https://ark.intel.com/content/www/us/en/ark/search/featurefilter.html?productType=873&0_ExtendedPageTables=True。RVI 在第三代以及之后的 AMD Opteron™(Barcelona)处理器中应用。判断处理器是否支持 bhyve 的方法是运行 demsg,或者在 /var/run/dmesg.boot 中的 Feature2 一行查找 POPCNT 处理器功能标识(针对 AMD® 处理器);或者在 VT-x 一行查找 EPTUG 处理器功能标识(针对 Intel® 处理器)。

22.6.1. 准备宿主机

在 bhyve 中创建虚拟机的第一步是配置宿主操作系统。首先,加载 bhyve 内核模块:

  1. # kldload vmm

然后,创建 tap 介面以供虚拟机中的网络设备进行连接。为了让该网络设备加入网络,另外创建一个桥接介面,其中包含 tap 介面和物理网络介面作为成员。在下面的例子中,物理网络介面为 igb0

  1. # ifconfig tap0 create
  2. # sysctl net.link.tap.up_on_open=1
  3. net.link.tap.up_on_open: 0 -> 1
  4. # ifconfig bridge0 create
  5. # ifconfig bridge0 addm igb0 addm tap0
  6. # ifconfig bridge0 up

22.6.2. 创建一个 FreeBSD 客户机

创建一个文件用作客户机的虚拟磁盘。指定这个虚拟磁盘的大小和名称:

  1. # truncate -s 16G guest.img

下载 FreeBSD 的安装镜像:

  1. # fetch ftp://ftp.freebsd.org/pub/FreeBSD/releases/ISO-IMAGES/12.2/FreeBSD-12.2-RELEASE-amd64-bootonly.iso
  2. FreeBSD-12.2-RELEASE-amd64-bootonly.iso 100% of 230 MB 570 kBps 06m17s

FreeBSD 附带了一个在 bhyve 中运行虚拟机的示例脚本。这个脚本将会启动虚拟机并循环执行,所以将在其崩溃时自动重新启动。这个脚本可接受数个选项来配置虚拟机:-c 控制虚拟 CPU 的数量,-m 限制客户机的可用内存,-t 定义使用的 tap 设备,-d 指定其使用的磁盘镜像,-i 控制 bhyve 从 CD 镜像引导,而非从磁盘引导,-I 指定要使用的 CD 镜像。最后一个参数是虚拟机的名称,该名称将被用来追踪运行中的虚拟机。这个例子将在安装模式下启动虚拟机:

  1. # sh /usr/share/examples/bhyve/vmrun.sh -c 1 -m 1024M -t tap0 -d guest.img -i -I FreeBSD-12.2-RELEASE-amd64-bootonly.iso guestname

这个虚拟机将会引导并启动安装器。在虚拟机中完成系统安装并进入最后阶段,当系统提示是否进入 shell 时,选择 Yes

重新启动虚拟机。重启会导致 bhyve 退出,这时循环执行 bhyvevmrun.sh 脚本会自动重启虚拟机。这时请从引导器菜单中选择重启来跳出循环。之后,客户机就可以从虚拟磁盘中启动了:

  1. # sh /usr/share/examples/bhyve/vmrun.sh -c 4 -m 1024M -t tap0 -d guest.img guestname

22.6.3. 创建一个 Linux® 客户机

要引导进入 FreeBSD 以外的系统,必须先安装 sysutils/grub2-bhyve port。

接下来,创建一个文件用作客户机的虚拟磁盘:

  1. # truncate -s 16G linux.img

使用 bhyve 启动虚拟机分为两个步骤。首先要加载一个内核,然后启动客户机。Linux® 内核通过 sysutils/grub2-bhyve 加载。创建 device.map 令 grub 将虚拟设备映射到宿主机上的镜像文件:

  1. (hd0) ./linux.img
  2. (cd0) ./somelinux.iso

使用 sysutils/grub2-bhyve 将 Linux® 内核从 ISO 文件中加载:

  1. # grub-bhyve -m device.map -r cd0 -M 1024M linuxguest

这将启动 grub。如果安装 CD 中包含 grub.cfg,将会显示一个菜单;否则你需要手动指明并加载 vmlinuzinitrd 文件:

  1. grub> ls
  2. (hd0) (cd0) (cd0,msdos1) (host)
  3. grub> ls (cd0)/isolinux
  4. boot.cat boot.msg grub.conf initrd.img isolinux.bin isolinux.cfg memtest
  5. splash.jpg TRANS.TBL vesamenu.c32 vmlinuz
  6. grub> linux (cd0)/isolinux/vmlinuz
  7. grub> initrd (cd0)/isolinux/initrd.img
  8. grub> boot

现在 Linux® 内核就加载完毕了,可以启动客户机:

  1. # bhyve -A -H -P -s 0:0,hostbridge -s 1:0,lpc -s 2:0,virtio-net,tap0 -s 3:0,virtio-blk,./linux.img \
  2. -s 4:0,ahci-cd,./somelinux.iso -l com1,stdio -c 4 -m 1024M linuxguest

系统将会引导进入并启动安装器。在虚拟机中完成系统安装之后,重新启动虚拟机。bhyve 会在这时退出。请先销毁这个虚拟机实例,然后重新启动:

  1. # bhyvectl --destroy --vm=linuxguest

现在客户机可以直接从磁盘镜像中启动了。首先加载内核:

  1. # grub-bhyve -m device.map -r hd0,msdos1 -M 1024M linuxguest
  2. grub> ls
  3. (hd0) (hd0,msdos2) (hd0,msdos1) (cd0) (cd0,msdos1) (host)
  4. (lvm/VolGroup-lv_swap) (lvm/VolGroup-lv_root)
  5. grub> ls (hd0,msdos1)/
  6. lost+found/ grub/ efi/ System.map-2.6.32-431.el6.x86_64 config-2.6.32-431.el6.x
  7. 86_64 symvers-2.6.32-431.el6.x86_64.gz vmlinuz-2.6.32-431.el6.x86_64
  8. initramfs-2.6.32-431.el6.x86_64.img
  9. grub> linux (hd0,msdos1)/vmlinuz-2.6.32-431.el6.x86_64 root=/dev/mapper/VolGroup-lv_root
  10. grub> initrd (hd0,msdos1)/initramfs-2.6.32-431.el6.x86_64.img
  11. grub> boot

引导进入虚拟机:

  1. # bhyve -A -H -P -s 0:0,hostbridge -s 1:0,lpc -s 2:0,virtio-net,tap0 \
  2. -s 3:0,virtio-blk,./linux.img -l com1,stdio -c 4 -m 1024M linuxguest

虚拟机中的 Linux® 将会启动并提示你登录。当你不再使用虚拟机时,重新启动虚拟机来退出 bhyve,并销毁虚拟机实例:

  1. # bhyvectl --destroy --vm=linuxguest

22.6.4. 使用 UEFI 固件引导 bhyve 虚拟机

除了 bhyveload 和 grub-bhyve,bhyve 虚拟机管理器还可使用 UEFI 用户态固件来引导虚拟机。这可能兼容其他引导器不支持的客户机操作系统。

要利用 bhyve 的 UEFI 支持,首先要生成 UEFI 固件镜像。你可以通过 sysutils/bhyve-firmware port 或 package 来实现。

准备好固件之后,向 bhyve 命令添加标识 -l bootrom,/path/to/firmware,例如:

  1. # bhyve -AHP -s 0:0,hostbridge -s 1:0,lpc \
  2. -s 2:0,virtio-net,tap1 -s 3:0,virtio-blk,./disk.img \
  3. -s 4:0,ahci-cd,./install.iso -c 4 -m 1024M \
  4. -l bootrom,/usr/local/share/uefi-firmware/BHYVE_UEFI.fd \
  5. guest

sysutils/bhyve-firmware 还包含了启用 CSM 的固件,若要在 legacy BIOS 模式中启动不支持 UEFI 的客户机,请这样运行:

  1. # bhyve -AHP -s 0:0,hostbridge -s 1:0,lpc \
  2. -s 2:0,virtio-net,tap1 -s 3:0,virtio-blk,./disk.img \
  3. -s 4:0,ahci-cd,./install.iso -c 4 -m 1024M \
  4. -l bootrom,/usr/local/share/uefi-firmware/BHYVE_UEFI_CSM.fd \
  5. guest

22.6.5. 为客户机设置图形化 UEFI Framebuffer

UEFI 固件支持对主要使用图形化界面的客户机操作系统格外有用,例如 Microsoft Windows®。

对 UEFI-GOP framebuffer 的支持也可以用标识 -s 29,fbuf,tcp=0.0.0.0:5900 来启用。framebuffer 的分辨率可以通过如 w=800 h=600 来设置。bhyve 也可以通过添加 wait 来等待 VNC 连接,之后再引导进入客户机。framebuffer 可以从宿主机或者 VNC 协议的网络连接访问。此外,可以添加 -s 30,xhci,tablet 来实现与宿主机之间精确的鼠标同步。

最后的 bhyve 命令可能类似这样:

  1. # bhyve -AHP -s 0:0,hostbridge -s 31:0,lpc \
  2. -s 2:0,virtio-net,tap1 -s 3:0,virtio-blk,./disk.img \
  3. -s 4:0,ahci-cd,./install.iso -c 4 -m 1024M \
  4. -s 29,fbuf,tcp=0.0.0.0:5900,w=800,h=600,wait \
  5. -s 30,xhci,tablet \
  6. -l bootrom,/usr/local/share/uefi-firmware/BHYVE_UEFI.fd \
  7. guest

提示:

在模拟 BIOS 模式下,当控制从固件移交到宿主机操作系统后,framebuffer 会停止接收更新。

22.6.6. 在 bhyve 客户机中使用 ZFS

如果在宿主机上可以使用 ZFS,在客户机上使用 ZFS 卷取代磁盘镜像可以显著提高性能。你可以这样新建一个 ZFS 卷:

  1. # zfs create -V16G -o volmode=dev zroot/linuxdisk0

启动虚拟机时,指定 ZFS 卷作为磁盘驱动器:

  1. # bhyve -A -H -P -s 0:0,hostbridge -s 1:0,lpc -s 2:0,virtio-net,tap0 -s3:0,virtio-blk,/dev/zvol/zroot/linuxdisk0 \
  2. -l com1,stdio -c 4 -m 1024M linuxguest

22.6.7. 虚拟机控制台

你可以使用类似于 sysutils/tmux 或者 sysutils/screen 这样的管理工具将 bhyve 控制台封装在一个会话中用来连接和断开控制台,这将带来一些好处。你还可以将一个空的调制解调器作为 bhyve 的控制台,然后使用 cu 来访问。若要这样做,请加载 nmdm 内核模块,然后将 -l com1,stdio 替换为 -l com1,/dev/nmdm0A。设备 /dev/nmdm 会自动按需创建并组成一对,对应着空的调制解调器的两端(/dev/nmdm0A/dev/nmdm0B)。查阅 nmdm(4) 获取更多信息。

  1. # kldload nmdm
  2. # bhyve -A -H -P -s 0:0,hostbridge -s 1:0,lpc -s 2:0,virtio-net,tap0 -s 3:0,virtio-blk,./linux.img \
  3. -l com1,/dev/nmdm0A -c 4 -m 1024M linuxguest
  4. # cu -l /dev/nmdm0B
  5. Connected
  6. Ubuntu 13.10 handbook ttyS0
  7. handbook login:

22.6.8. 管理虚拟机

对于每个虚拟机,在 /dev/vmm 下都会创建一个设备节点。这使得管理员很容易就能看到运行中的虚拟机列表:

  1. # ls -al /dev/vmm
  2. total 1
  3. dr-xr-xr-x 2 root wheel 512 Mar 17 12:19 ./
  4. dr-xr-xr-x 14 root wheel 512 Mar 17 06:38 ../
  5. crw------- 1 root wheel 0x1a2 Mar 17 12:20 guestname
  6. crw------- 1 root wheel 0x19f Mar 17 12:19 linuxguest
  7. crw------- 1 root wheel 0x1a1 Mar 17 12:19 otherguest

通过 bhyvectl 可以销毁指定的虚拟机:

  1. # bhyvectl --destroy --vm=guestname

22.6.9. 持久化配置

要配置系统在启动时运行 bhyve 虚拟机,请进行如下配置:

  1. /etc/sysctl.conf

    1. net.link.tap.up_on_open=1
  2. /etc/rc.conf

    1. cloned_interfaces="bridge0 tap0"
    2. ifconfig_bridge0="addm igb0 addm tap0"
    3. kld_list="nmdm vmm"