# 24.8.基于 FreeBSD 的 Xen™ 虚拟机

Xen 是一款 GPLv2 许可的 [类型 1 虚拟机监控程序](https://en.wikipedia.org/wiki/Hypervisor#Classification)，适用于 Intel® 和 ARM® 架构。FreeBSD 自 8.0 版本起支持 i386™ 和 AMD® 64 位 [DomU](https://wiki.xenproject.org/wiki/DomU) 和 [Amazon EC2](https://en.wikipedia.org/wiki/Amazon_Elastic_Compute_Cloud) 非特权域（虚拟机），并在 FreeBSD 11.0 中引入了 Dom0 控制域（主机）支持。FreeBSD 11 中已移除对 para-virtualized (PV) 域的支持，转而采用硬件虚拟化 (HVM) 域，这提供了更好的性能。

Xen™ 是一种裸金属虚拟机监控程序，意味着它是 BIOS 启动后加载的第一个程序。然后，启动一个特殊的特权虚拟机，称为 Domain-0（简称 Dom0）。Dom0 使用其特殊权限直接访问底层物理硬件，使其成为一种高性能解决方案。它能够直接访问磁盘控制器和网络适配器。Xen™ 管理工具由 Dom0 用来创建、列出和销毁虚拟机。Dom0 为非特权域提供虚拟磁盘和网络，通常称为 `DomU`。Xen™ Dom0 可以与其他虚拟化解决方案的服务控制台进行比较，而 DomU 是运行个别虚拟机的地方。

Xen™ 可以在不同的 Xen™ 服务器之间迁移虚拟机。当两个 Xen 主机共享相同的底层存储时，可以在不关闭虚拟机的情况下进行迁移。相反，迁移在 DomU 正常运行时进行，且无需重新启动或规划停机时间。这在维护或升级窗口中非常有用，确保 DomU 提供的服务仍然在运行。更多的 Xen™ 功能可以在 [Xen Wiki 概述页面](https://wiki.xenproject.org/wiki/Category:Overview) 上找到。请注意，并非所有功能在 FreeBSD 上都得到支持。

## 24.8.1. Xen™ Dom0 硬件要求

要在主机上运行 Xen™ 虚拟机监控程序，需要特定的硬件功能。将 FreeBSD 作为 Xen 主机（Dom0）运行时，主机处理器需要支持 Intel 扩展页表（[EPT](https://en.wikipedia.org/wiki/Extended_Page_Table)）或 AMD 嵌套页表（[NPT](https://en.wikipedia.org/wiki/Rapid_Virtualization_Indexing)）以及输入/输出内存管理单元（[IOMMU](https://en.wikipedia.org/wiki/List_of_IOMMU-supporting_hardware)）功能。

> **注意**
>
> 为了运行 FreeBSD 13 Xen™ Dom0，系统必须使用传统引导（BIOS）启动。FreeBSD 14 及更高版本支持在 BIOS 和 UEFI 模式下作为 Xen™ Dom0 启动。

## 24.8.2. Xen™ Dom0 控制域设置

用户应安装 [emulators/xen-kernel](https://cgit.freebsd.org/ports/tree/emulators/xen-kernel/) 和 [sysutils/xen-tools](https://cgit.freebsd.org/ports/tree/sysutils/xen-tools/) 包，这些包基于 Xen™ 4.18。

在安装 Xen 包之后，必须编辑配置文件，以准备主机进行 Dom0 集成。**/etc/sysctl.conf** 文件中的一个条目将禁用内存页数限制。否则，具有更高内存需求的 DomU 虚拟机将无法运行。

```sh
# echo 'vm.max_wired=-1' >> /etc/sysctl.conf
```

另一个与内存相关的设置是更改 **/etc/login.conf**，将 `memorylocked` 选项设置为 `unlimited`。否则，创建 DomU 域时可能会失败，出现 `Cannot allocate memory` 错误。更改 **/etc/login.conf** 后，运行 `cap_mkdb` 更新能力数据库。有关详细信息，请参阅 [资源限制](https://docs.freebsd.org/en/books/handbook/security/#security-resourcelimits)。

```sh
# sed -i '' -e 's/memorylocked=64K/memorylocked=unlimited/' /etc/login.conf
# cap_mkdb /etc/login.conf
```

在 **/etc/ttys** 文件中添加一个 Xen™ 控制台条目：

```sh
# echo 'xc0     "/usr/libexec/getty Pc"         xterm   onifconsole  secure' >> /etc/ttys
```

在 **/boot/loader.conf** 文件中选择一个 Xen™ 内核来激活 Dom0。Xen™ 还需要主机机器的 CPU 和内存资源供自己和其他 DomU 域使用。具体需要多少 CPU 和内存取决于个别需求和硬件能力。在此示例中，为 Dom0 分配了 8GB 内存和 4 个虚拟 CPU。还激活了串口控制台，并定义了日志选项。

对于 Xen 4.7 包，使用以下命令：

```sh
# echo 'hw.pci.mcfg=0' >> /boot/loader.conf
# echo 'if_tap_load="YES"' >> /boot/loader.conf
# echo 'xen_kernel="/boot/xen"' >> /boot/loader.conf
# echo 'xen_cmdline="dom0_mem=8192M dom0_max_vcpus=4 dom0pvh=1 console=com1,vga com1=115200,8n1 guest_loglvl=all loglvl=all"' >> /boot/loader.conf
```

对于 Xen 版本 4.11 及更高版本，应该使用以下命令：

```sh
# echo 'if_tap_load="YES"' >> /boot/loader.conf
# echo 'xen_kernel="/boot/xen"' >> /boot/loader.conf
# echo 'xen_cmdline="dom0_mem=8192M dom0_max_vcpus=4 dom0=pvh console=com1,vga com1=115200,8n1 guest_loglvl=all loglvl=all"' >> /boot/loader.conf
```

> **技巧**
>
> Xen™ 为 DomU 虚拟机创建的日志文件存储在 **/var/log/xen** 中。如果遇到问题，请务必检查该目录的内容。

在系统启动时启用 xencommons 服务：

```sh
# sysrc xencommons_enable=yes
```

这些设置足以启动一个启用 Dom0 的系统。但是，它缺少为 DomU 虚拟机提供网络功能。为了解决这个问题，定义一个桥接接口与主机系统的 NIC 连接，供 DomU 虚拟机使用。将 *em0* 替换为主机网络接口的名称。

```sh
# sysrc cloned_interfaces="bridge0"
# sysrc ifconfig_bridge0="addm em0 SYNCDHCP"
# sysrc ifconfig_em0="up"
```

重启主机以加载 Xen™ 内核并启动 Dom0。

```sh
# reboot
```

在成功启动 Xen™ 内核并再次登录系统后，使用 Xen™ 管理工具 `xl` 查看域的信息。

```sh
# xl list
Name                                        ID   Mem VCPUs      State   Time(s)
Domain-0                                     0  8192     4     r-----     962.0
```

输出确认了 Dom0（称为 `Domain-0`）的 ID 为 `0`，并且正在运行。它还具有在 **/boot/loader.conf** 中定义的内存和虚拟 CPU。更多信息可以参考 [Xen™ 文档](https://www.xenproject.org/help/documentation.html)。现在可以创建 DomU 虚拟机。

## 24.8.3. Xen™ DomU 虚拟机配置

无特权域由配置文件和虚拟或物理硬盘组成。DomU 的虚拟磁盘存储可以是通过 [truncate(1)](https://man.freebsd.org/cgi/man.cgi?query=truncate\&sektion=1\&format=html) 创建的文件，也可以是如在 [“创建和销毁卷”](https://docs.freebsd.org/en/books/handbook/zfs/#zfs-zfs-volume) 中所描述的 ZFS 卷。在这个例子中，使用了一个 20 GB 的卷。创建一个虚拟机，使用 ZFS 卷、FreeBSD ISO 镜像、1 GB 的内存和两个虚拟 CPU。ISO 安装文件通过 [fetch(1)](https://man.freebsd.org/cgi/man.cgi?query=fetch\&sektion=1\&format=html) 下载并保存在名为 **freebsd.iso** 的本地文件中。

```sh
# fetch https://download.freebsd.org/releases/ISO-IMAGES/14.0/FreeBSD-14.0-RELEASE-amd64-bootonly.iso -o freebsd.iso
```

创建一个名为 **xendisk0** 的 20 GB ZFS 卷，用作虚拟机的磁盘空间。

```sh
# zfs create -V20G -o volmode=dev zroot/xendisk0
```

新的 DomU 虚拟机在一个文件中定义。也定义了一些特定的设置，如名称、键盘映射和 VNC 连接细节。以下 **freebsd.cfg** 包含了此示例的最小 DomU 配置：

```ini
# cat freebsd.cfg 
builder = "hvm" ①
name = "freebsd" ②
memory = 1024 ③
vcpus = 2 ④
vif = [ 'mac=00:16:3E:74:34:32,bridge=bridge0' ] ⑤
disk = [
'/dev/zvol/tank/xendisk0,raw,hda,rw', ⑥
'/root/freebsd.iso,raw,hdc:cdrom,r' ⑦
  ]
vnc = 1 ⑧
vnclisten = "0.0.0.0"
serial = "pty"
usbdevice = "tablet"
```

这些行的详细解释：

* ① 这定义了使用何种虚拟化方式。`hvm` 表示硬件辅助虚拟化或硬件虚拟机。具有虚拟化扩展的 CPU 上，来宾操作系统可以不加修改地运行，提供几乎与运行在物理硬件上一样的性能。`generic` 是默认值，创建 PV 域。
* ② 用于区分其他正在同一个 Dom0 上运行的虚拟机的名称。是必需的。
* ③ 为虚拟机分配的内存量（以兆字节为单位）。该内存量从虚拟化管理程序的总可用内存中减去，而不是从 Dom0 的内存中减去。
* ④ 分配给虚拟机的虚拟 CPU 数量。为了获得最佳性能，不要为来宾创建比物理 CPU 更多的虚拟 CPU。
* ⑤ 虚拟网络适配器。这是连接到主机网络接口的桥接。`mac` 参数设置虚拟网络接口的 MAC 地址。此参数是可选的，如果没有提供 MAC，Xen™ 会生成一个随机的。
* ⑥ 磁盘的完整路径、文件或 ZFS 卷，作为该虚拟机的磁盘存储。选项和多个磁盘定义之间由逗号分隔。
* ⑦ 定义引导介质，用于安装初始操作系统。在此示例中，是之前下载的 ISO 镜像。请参考 Xen™ 文档了解其他类型的设备和选项设置。
* ⑧ 控制 VNC 连接到 DomU 的串行控制台的选项。依次是：启用 VNC 支持，定义监听的 IP 地址，串行控制台的设备节点，以及输入方法（用于精确定位鼠标和其他输入方式）。`keymap` 定义使用的键盘映射，默认为 `english`。

创建好包含所有必要选项的文件后，可以通过将其作为参数传递给 `xl create` 来创建 DomU。

```sh
# xl create freebsd.cfg
```

> **注意**
>
> 每次重启 Dom0 后，必须将配置文件传递给 `xl create` 以重新创建 DomU。默认情况下，重启后只会创建 Dom0，而不会创建单独的虚拟机。虚拟机可以继续运行，因为它们的操作系统存储在虚拟磁盘上。虚拟机配置可能会随着时间的推移而变化（例如，添加更多内存）。虚拟机配置文件必须妥善备份并保留，以便在需要时重新创建虚拟机。

执行 `xl list` 命令的输出确认已创建 DomU。

```sh
# xl list
Name                                        ID   Mem VCPUs      State   Time(s)
Domain-0                                     0  8192     4     r-----  1653.4
freebsd                                      1  1024     1     -b----   663.9
```

要开始安装基础操作系统，启动 VNC 客户端，并将其指向主机的网络地址或在 **freebsd.cfg** 中定义的 `vnclisten` 行的 IP 地址。操作系统安装完成后，关闭 DomU 并断开 VNC 查看器。编辑 **freebsd.cfg**，删除或注释掉 `cdrom` 定义的行（在行首插入 `#` 字符）。要加载新的配置，需要先使用 `xl destroy` 销毁旧的 DomU，传递虚拟机名称或 ID 作为参数。然后，使用修改后的 **freebsd.cfg** 重新创建虚拟机。

```sh
# xl destroy freebsd
# xl create freebsd.cfg
```

此时，可以再次使用 VNC 查看器访问虚拟机。这次，它将从安装有操作系统的虚拟磁盘启动，并可以作为虚拟机使用。

## 24.8.4. 故障排除

本节包含了一些基本信息，旨在帮助排除在使用 FreeBSD 作为 Xen™ 主机或虚拟机时可能遇到的问题。

### 24.8.4.1. 主机启动故障排除

请注意，以下故障排除技巧适用于 Xen™ 4.11 或更新版本。如果你仍在使用 Xen™ 4.7 并遇到问题，建议迁移到更新版本的 Xen™。

为了解决主机启动问题，你可能需要串行电缆或调试 USB 电缆。可以通过在 **loader.conf** 中添加选项来获取详细的 Xen™ 启动输出。以下是一些相关的调试选项：

* `iommu=debug`：可以用来打印关于 IOMMU 的额外诊断信息。
* `dom0=verbose`：可以用来打印关于 Dom0 构建过程的额外诊断信息。
* `sync_console`：强制同步控制台输出的标志。对于调试非常有用，可以避免因限速而丢失消息。切勿在生产环境中使用此选项，因为它可能允许恶意的虚拟机通过控制台对 Xen™ 发起拒绝服务攻击。

FreeBSD 还应该以详细模式启动，以便识别任何问题。要激活详细启动模式，可以运行以下命令：

```sh
# echo 'boot_verbose="YES"' >> /boot/loader.conf
```

如果这些选项都无法帮助解决问题，请将串行启动日志发送到 <freebsd-xen@FreeBSD.org> 和 <xen-devel@lists.xenproject.org> 以便进一步分析。

### 24.8.4.2. 虚拟机创建故障排除

在创建虚拟机时也可能会遇到问题，以下提供了一些帮助，帮助诊断虚拟机创建问题。

虚拟机创建失败的最常见原因是 `xl` 命令输出某些错误，并以非 0 的返回码退出。如果提供的错误信息不足以帮助识别问题，可以通过多次使用 `v` 选项从 `xl` 获取更详细的输出。

```sh
# xl -vvv create freebsd.cfg
Parsing config from freebsd.cfg
libxl: debug: libxl_create.c:1693:do_domain_create: Domain 0:ao 0x800d750a0: create: how=0x0 callback=0x0 poller=0x800d6f0f0
libxl: debug: libxl_device.c:397:libxl__device_disk_set_backend: Disk vdev=xvda spec.backend=unknown
libxl: debug: libxl_device.c:432:libxl__device_disk_set_backend: Disk vdev=xvda, using backend phy
libxl: debug: libxl_create.c:1018:initiate_domain_create: Domain 1:running bootloader
libxl: debug: libxl_bootloader.c:328:libxl__bootloader_run: Domain 1:not a PV/PVH domain, skipping bootloader
libxl: debug: libxl_event.c:689:libxl__ev_xswatch_deregister: watch w=0x800d96b98: deregister unregistered
domainbuilder: detail: xc_dom_allocate: cmdline="", features=""
domainbuilder: detail: xc_dom_kernel_file: filename="/usr/local/lib/xen/boot/hvmloader"
domainbuilder: detail: xc_dom_malloc_filemap    : 326 kB
libxl: debug: libxl_dom.c:988:libxl__load_hvm_firmware_module: Loading BIOS: /usr/local/share/seabios/bios.bin
...
```

如果详细输出仍无法帮助诊断问题，还可以查看 **/var/log/xen** 中的 QEMU 和 Xen™ 工具栈日志。请注意，日志文件名会附加上域的名称，因此，如果域名为 `freebsd`，你应该会找到 **/var/log/xen/xl-freebsd.log** 和可能的 **/var/log/xen/qemu-dm-freebsd.log**。这两个日志文件中可能包含有用的调试信息。如果这些都没有帮助解决问题，请将你遇到的问题描述和尽可能多的信息发送到 <freebsd-xen@FreeBSD.org> 和 <xen-devel@lists.xenproject.org> 以获得帮助。
