24.7.使用 FreeBSD 上的 bhyve 虚拟机

bhyve 是一款 BSD 授权的虚拟化管理程序,已在 FreeBSD 10.0-RELEASE 中成为基本系统的一部分。该虚拟化管理程序支持多个操作系统,包括 FreeBSD、OpenBSD、许多 Linux® 发行版以及 Microsoft Windows®。默认情况下,bhyve 提供对串行控制台的访问,而不模拟图形控制台。通过使用新处理器的虚拟化卸载功能,避免了翻译指令和手动管理内存映射的传统方法。

bhyve 的设计要求:

  • 支持 Intel® 处理器的 Intel 扩展页表(EPT),

  • 或支持 AMD® 处理器的 AMD 快速虚拟化索引(RVI)或嵌套页表(NPT),

  • 或 ARM® aarch64 CPU。

在 ARM 上,仅支持纯 ARMv8.0 虚拟化,目前不使用虚拟化主机扩展。托管 Linux® 或 FreeBSD 虚拟机时,如果有多个 vCPU,则需要 VMX 非限制模式支持(UG)。

判断 Intel 或 AMD 处理器是否支持 bhyve 的最简单方法是运行 dmesg 或查看 /var/run/dmesg.boot 文件中的 POPCNT 处理器特征标志(对于 AMD® 处理器)或 EPTUG(对于 Intel® 处理器)的 VT-x 行。

24.7.1. 准备宿主机

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

# kldload vmm

将虚拟机与宿主的网络连接有几种方法;一种简单的方式是为虚拟机中的网络设备创建一个 tap 接口。为了让网络设备参与网络,还需要创建一个包含 tap 接口和物理接口作为成员的桥接接口。在此示例中,物理接口是 igb0

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

24.7.2. 创建 FreeBSD 虚拟机

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

下载 FreeBSD 安装镜像:

FreeBSD 附带了示例脚本 vmrun.sh,用于在 bhyve 中运行虚拟机。该脚本将启动虚拟机并在一个循环中运行,这样如果虚拟机崩溃,它会自动重新启动。vmrun.sh 接受多个选项来控制虚拟机的配置,包括:

  • -c 控制虚拟 CPU 的数量,

  • -m 限制分配给虚拟机的内存,

  • -t 定义使用的 tap 设备,

  • -d 指定使用的磁盘镜像,

  • -i 告诉 bhyve 从 CD 镜像而非磁盘启动,

  • -I 定义使用的 CD 镜像。

最后一个参数是虚拟机的名称,用于跟踪正在运行的虚拟机。以下命令列出了所有可用的程序参数选项:

此示例以安装模式启动虚拟机:

虚拟机将启动并开始安装。当系统在安装结束时询问是否进入 shell 时,选择 Yes

重启虚拟机。重启虚拟机会导致 bhyve 退出,但 vmrun.sh 脚本会在循环中运行 bhyve 并自动重新启动。当发生这种情况时,选择启动菜单中的重启选项以跳出循环。现在可以从虚拟磁盘启动虚拟机系统:

24.7.3. 创建 Linux® 虚拟机

Linux 虚拟机可以像其他常规 基于 UEFI 的虚拟机 虚拟机一样启动,或者可以使用 Port sysutils/grub2-bhyve

为此,首先确保已安装该 Port,然后创建一个文件作为虚拟机的虚拟磁盘:

使用 grub2-bhyve 启动 Linux 虚拟机是一个两步过程。

  1. 首先加载内核,然后启动虚拟机。

  2. 使用 sysutils/grub2-bhyve 加载 Linux® 内核。

创建一个 device.map 文件,grub 将用它来将虚拟设备映射到宿主系统上的文件:

使用 sysutils/grub2-bhyve 从 ISO 镜像加载 Linux® 内核:

这将启动 grub。如果安装 CD 包含 grub.cfg 文件,将显示一个菜单。如果没有,则必须手动定位并加载 vmlinuzinitrd 文件:

现在内核已加载,可以启动虚拟机系统:

系统将启动并开始安装。在虚拟机中安装系统后,重启虚拟机。重启虚拟机会导致 bhyve 退出。虚拟机实例需要被销毁才能重新启动:

现在,可以直接从虚拟磁盘启动虚拟机。加载内核:

启动虚拟机:

Linux® 将在虚拟机中启动并最终显示登录提示。登录后使用虚拟机。完成后,重启虚拟机以退出 bhyve。销毁虚拟机实例:

24.7.4. 使用 UEFI 固件启动 bhyve 虚拟机

除了 bhyveloadgrub-bhyve,bhyve 超级虚拟机还可以使用 UEFI 固件启动虚拟机。这种选项可能支持其他加载器不支持的客操作系统。

要使用 bhyve 中的 UEFI 支持,首先需要获取 UEFI 固件镜像。可以通过安装 Port 或包sysutils/bhyve-firmware 来完成。

将固件安装好后,在 bhyve 命令行中添加 -l bootrom,/path/to/firmware 标志。实际的 bhyve 命令可能如下所示:

为了允许虚拟机存储 UEFI 变量,可以将变量文件附加到 -l 标志中。请注意,bhyve 将会将对虚拟机变量的修改写入指定的变量文件。因此,请确保首先为每个虚拟机创建一个变量模板文件的副本:

然后,在 bhyve 参数中添加该变量文件:

注意

某些 Linux 发行版要求使用 UEFI 变量存储其 UEFI 启动文件的路径(例如,使用 linux64.efigrubx64.efi,而不是 bootx64.efi)。因此,建议使用变量文件来避免手动修改启动分区文件。

要查看或修改变量文件的内容,可以使用 efivar(8) 从主机进行操作。

sysutils/bhyve-firmware 还包含支持 CSM 的固件,用于以传统 BIOS 模式启动没有 UEFI 支持的虚拟机:

24.7.5. 为 bhyve 虚拟机启用图形 UEFI 帧缓存

UEFI 固件支持对于主要以图形为主的操作系统(如 Microsoft Windows®)特别有用。

可以通过添加 -s 29,fbuf,tcp=0.0.0.0:5900 标志启用 UEFI-GOP 帧缓存。可以使用 w=800h=600 配置帧缓存分辨率,并且可以通过添加 wait 指示 bhyve 在启动虚拟机之前等待 VNC 连接。帧缓存可以从主机或通过网络通过 VNC 协议进行访问。此外,可以添加 -s 30,xhci,tablet 以实现与主机的精确鼠标光标同步。

最终的 bhyve 命令将如下所示:

请注意,在 BIOS 模拟模式下,若控制从固件传递给客操作系统,帧缓存将停止更新。

24.7.6. 创建 Microsoft Windows® 虚拟机

为 Windows 10 或更早版本的操作系统设置虚拟机,可以直接使用原始安装媒体,过程相对简单。除了最低资源要求,运行 Windows 作为虚拟机还需要:

  • 配置虚拟机内存(使用 -w 标志)

  • 使用 UEFI 启动固件启动。

以下是使用 Windows 安装 ISO 启动虚拟机虚拟机的示例:

在安装过程中,建议只使用一个或两个 VCPU,但安装完成后可以增加此数量。

必须安装 VirtIO 驱动,以使用定义的 virtio-net 网络接口。另一种选择是通过将 virtio-net 更改为 e1000(Intel E82545)模拟来切换到 E1000 模式,但性能会受到影响。

24.7.6.1. 创建 Windows 11 虚拟机

从 Windows 11 开始,Microsoft 引入了 TPM 2 模块的硬件要求。bhyve 支持将硬件 TPM 传递到虚拟机。可以修改安装媒体以禁用相关的硬件检查。有关此过程的详细说明,请参见 FreeBSD Wiki

警告

制造商并不支持修改 Windows 安装媒体以在没有 TPM 模块的情况下运行 Windows 虚拟机。实施此类方法前,请考虑你的应用程序和使用案例。

24.7.7. 在 bhyve 虚拟机中使用 ZFS

如果主机上可用 ZFS,使用 ZFS 卷而不是磁盘镜像文件可以为虚拟机提供显著的性能提升。可以通过以下方式创建 ZFS 卷:

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

如果你在主机和虚拟机中都使用 ZFS,请记住两者都会缓存虚拟机的内容,可能会造成内存竞争压力。为缓解此问题,考虑将主机的 ZFS 文件系统设置为仅缓存元数据。为此,请对主机上的 ZFS 文件系统应用以下设置,将 <name> 替换为特定虚拟机 zvol 数据集的名称:

24.7.8. 创建虚拟机快照

现代超级虚拟机允许用户创建“快照”,该快照包括虚拟机的磁盘、CPU 和内存内容。快照通常可以在虚拟机运行或关闭的情况下创建。然后,可以将虚拟机恢复到快照拍摄时的精确状态。

24.7.8.1. ZFS 快照

使用 ZFS 卷作为虚拟机的后端存储可以进行虚拟机磁盘的快照。例如:

虽然可以在虚拟机运行时对 ZFS 卷进行快照,但请注意,在虚拟机活跃时,虚拟磁盘的内容可能处于不一致的状态。因此,建议在执行此命令之前,先关闭或暂停虚拟机。默认情况下,不支持暂停虚拟机,需要先启用(参见 内存和 CPU 快照

警告

在虚拟机使用 ZFS zvol 时回滚到快照可能会破坏文件系统内容并导致虚拟机崩溃。所有未保存的数据将丢失,且自上次快照以来的修改可能会被销毁。

若虚拟机关闭,可能需要第二次回滚以恢复文件系统的可用状态。这最终会摧毁自快照以来所做的所有更改。

24.7.8.2. 内存和 CPU 快照(实验性功能)

从 FreeBSD 13 开始,bhyve 具有一个实验性的“快照”功能,可以将虚拟机的内存和 CPU 状态转储到文件中,然后暂停虚拟机。稍后可以从快照文件恢复虚拟机。

然而,此功能默认情况下未启用,并且需要从源代码重新编译系统。有关编译内核和自定义选项的详细描述,请参见 从源代码构建

警告

此功能尚不适合生产环境使用,并且仅适用于特定的虚拟机配置。存在多个限制:

  • nvmevirtio-blk 存储后端目前无法工作

  • 快照仅在虚拟机使用每种设备的一种类型时支持,即如果附加了多个 ahci-hd 磁盘,快照创建将失败

  • 此功能在 Intel 系统上可能较为稳定,但可能在 AMD CPU 上无法工作。

注意

在执行以下步骤之前,请确保 /usr/src 目录是最新的。请参见 更新源代码 以了解如何操作的详细步骤。

首先,在 /etc/src.conf 文件中添加以下内容:

注意

如果系统进行了部分或完全重建,建议在继续操作前运行

然后按照 从源代码更新 FreeBSD 快速入门部分 中的步骤构建并安装世界和内核。

要验证快照功能是否成功启用,可以输入:

并检查输出中是否列出了 --suspend 标志。如果没有该标志,说明该功能未正确激活。

然后,你可以快照并暂停你选择的运行中的虚拟机:

注意

提供绝对路径和文件名给 --suspend。否则,bhyve 将把快照数据写入启动 bhyve 时所在的目录。

确保将快照数据写入安全目录。生成的输出包含虚拟机的完整内存转储,因此可能包含敏感数据(例如密码)!

此操作会生成三个文件:

  • 内存快照 - 文件名与 --suspend 输入相同

  • 内核文件 - 文件名与 --suspend 输入相同,后缀为 .kern

  • 元数据 - 包含系统状态的元数据,后缀为 .meta

要从快照恢复虚拟机,可以使用 -r 标志与 bhyve

在不同 CPU 架构上恢复虚拟机快照将无法工作。通常,尝试在与创建快照的系统不同的系统上恢复将会失败。

24.7.9. 在 Jail 中运行 bhyve

为了提高安全性并将虚拟机与主机操作系统隔离,可以将 bhyve 运行在 jail 中。有关 jail 及其安全性好处的详细描述,请参见 Jails

24.7.9.1. 为 bhyve 创建 Jail

首先,创建一个 jail 环境。如果使用 UFS 文件系统,直接运行:

如果使用 ZFS 文件系统,请使用以下命令:

然后为虚拟机 bhyvevm0 创建一个 ZFS zvol:

如果不使用 ZFS,使用以下命令直接在 jail 目录结构中创建一个磁盘映像文件:

下载一个 FreeBSD 镜像,最好是与主机版本相同或更旧的版本,并将其解压到 jail 目录中:

注意

在 jail 中运行高版本的 FreeBSD(例如,在 13.2-RELEASE 主机中运行 14.0-RELEASE)是不支持的。

接下来,向 /etc/devfs.rules 添加 devfs 规则集:

注意

如果 /etc/devfs.rules 文件中已有其他编号为 100 的 devfs 规则,请将列表中的 ID 替换为另一个尚未使用的 ID 号码。

注意

如果不使用 ZFS 文件系统,请跳过 /etc/devfs.rules 中与 zvol 相关的规则:

这些规则将导致 bhyve

  • 创建名为 bhyvevm0bhyvevm1 的虚拟机磁盘卷,

  • 使用名为 tap10tap 网络接口。即有效的接口名称将是 tap10tap100tap101、… tap109tap1000 等。限制对可能的 tap 接口名称的访问将防止 jail(从而 bhyve)看到主机和其他 jails 的 tap 接口。

  • 使用以“bhyve”开头的 nmdm 设备,即 /dev/nmdmbhyve0

可以根据需要扩展和更改这些规则,以适应不同的虚拟机和接口名称。

注意

如果你打算在主机和一个或多个 jails 中使用 bhyve,请记住,tapnmdm 接口名称将在共享环境中操作。例如,你只能将 /dev/nmdmbhyve0 用于主机或 jail 中的 bhyve。

重新启动 devfs 以加载更改:

然后,在 /etc/jail.conf/etc/jail.conf.d 中为新的 jail 添加定义。将接口编号 $if 和 IP 地址替换为你的个人变体。

示例 1. 使用 NAT 或路由流量与防火墙

此示例假设使用类似 pfipfw 的防火墙来 NAT 你的 jail 流量。有关实现此功能的更多详细信息,请参见 防火墙 章节。

示例 2. 使用桥接网络连接

注意

如果你之前将 /etc/devfs.rules 中的 devfs 规则集 ID 100 替换为你自己的唯一数字,请记得在 jails.conf 中也替换相应的数字 ID。

24.7.9.2. 配置 Jail

首次启动 Jail 并进行一些额外的配置工作时,输入以下命令:

重启并启用 Jail:

之后,你可以在 Jail 内创建虚拟机。首先,下载 FreeBSD 虚拟机操作系统的安装 ISO 文件:

24.7.9.3. 在 Jail 内创建虚拟机

使用 bhyvectl 初始化虚拟机:

注意

在 Jail 中启动虚拟机时,可能需要使用 bhyvectl 创建虚拟机。跳过此步骤可能会导致启动 bhyve 时出现以下错误信息:

最后,使用你喜欢的方式启动虚拟机。

示例 3. 使用 vmrun.sh 和 ZFS 启动

在 ZFS 文件系统上使用 vmrun.sh

示例 4. 使用 vmrun.sh 和 UFS 启动

在 UFS 文件系统上使用 vmrun.sh

示例 5. 使用 ZFS 启动 UEFI 虚拟机操作系统

如果你希望使用 UEFI 虚拟机操作系统,请首先在 Jail 内安装所需的固件包 sysutils/bhyve-firmware

然后直接使用 bhyve 启动:

这能让你通过 VNC 连接到虚拟机 bhyvevm0,并通过 /dev/nmdbbhyve0B 使用串行控制台。

24.7.10. 虚拟机控制台

将 bhyve 控制台包裹在一个会话管理工具中,如 sysutils/tmuxsysutils/screen,是非常有利的,这样可以断开并重新连接到控制台。也可以将 bhyve 的控制台设置为一个 null modem 设备,并通过 cu 访问。要实现这一点,请加载 nmdm 内核模块,并将 -l com1,stdio 替换为 -l com1,/dev/nmdm0A/dev/nmdm 设备会根据需要自动创建,每对设备对应于 null modem 电缆的两端(/dev/nmdm0A/dev/nmdm0B)。更多信息请参见 nmdm(4)

要从控制台断开连接,输入换行符(即按 RETURN),然后是波浪符(~),最后是点符号(.)。请记住,只有连接会被断开,而登录会话仍然保持活动状态。因此,其他用户连接到同一控制台时,可以在无需重新认证的情况下继续使用任何活动会话。出于安全考虑,建议在断开连接前注销。

nmdm 设备路径中的编号必须对每个虚拟机唯一,并且在 bhyve 启动之前,不能被任何其他进程使用。编号可以随意选择,无需来自连续的数字序列。设备节点对(即 /dev/nmdm0a/dev/nmdm0b)是在 bhyve 连接其控制台时动态创建的,并在 bhyve 关闭时销毁。在创建启动虚拟机的脚本时,请牢记这一点:你需要确保所有虚拟机都分配有唯一的 nmdm 设备。

24.7.11. 管理虚拟机

每个虚拟机都会在 /dev/vmm 中创建一个设备节点。这使得管理员可以轻松查看正在运行的虚拟机列表:

可以使用 bhyvectl 销毁指定的虚拟机:

以这种方式销毁虚拟机会立即将其杀死。任何未保存的数据将丢失,打开的文件和文件系统可能会被损坏。要优雅地关闭虚拟机,请向其 bhyve 进程发送 TERM 信号。这会触发虚拟机的 ACPI 关机事件:

24.7.12. 工具和实用程序

有许多工具和应用程序可在 ports 中找到,帮助简化设置和管理 bhyve 虚拟机:

表 1. bhyve 管理工具

名称
许可证
软件包
文档

24.7.13. 持久化配置

为了在启动时自动启动 bhyve 虚拟机,需要进行一些配置文件的修改。

  1. /etc/sysctl.conf 当使用 tap 接口作为网络后端时,你需要手动将每个使用的 tap 接口设置为 UP,或者仅需设置以下 sysctl 参数:

  2. /etc/rc.conf 要通过 桥接 将虚拟机的 tap 设备连接到网络,你需要在 /etc/rc.conf 中持久化设备设置。此外,你可以通过 kld_list 配置变量加载必要的内核模块 vmm 用于 bhyve 和 nmdm 用于 nmdm 设备。在配置 ifconfig_bridge0 时,确保将 <ipaddr>/<netmask> 替换为物理接口(在此示例中为 igb0)的实际 IP 地址,并从物理设备中移除 IP 设置。

示例 6. 设置桥接设备的 IP 地址

对于一个与网络连接的 igb0 接口,其 IP 为 10.10.10.1,子网掩码为 255.255.255.0,你将使用以下命令:

警告

修改系统的 IP 地址配置可能会导致远程连接中断(例如通过 SSH 连接)!在执行这些命令时,请确保采取必要的预防措施以保持系统访问,或在本地终端会话中进行这些修改。

最后更新于