# 34.10.使用 PXE 进行无盘操作

Intel® 预启动执行环境（PXE）能通过网络启动操作系统。例如，FreeBSD 系统可以通过网络启动，并在没有本地磁盘的情况下运行，使用从 NFS 服务器挂载的文件系统。PXE 支持通常在 BIOS 中提供。要在机器启动时使用 PXE，请在 BIOS 设置中选择“从网络启动”选项，或在系统初始化过程中按下功能键。

为了提供操作系统通过网络启动所需的文件，PXE 设置还需要正确配置的 DHCP、TFTP 和 NFS 服务器，其中：

* 初始参数，如 IP 地址、可执行启动文件名和位置、服务器名称以及根路径，从 DHCP 服务器获取。
* 操作系统加载器文件通过 TFTP 启动。
* 文件系统通过 NFS 加载。

当计算机通过 PXE 启动时，它会通过 DHCP 获取有关从哪里获取初始启动加载程序文件的信息。在主机计算机接收到这些信息后，它通过 TFTP 下载启动加载程序并执行它。在 FreeBSD 中，启动加载程序文件是 **/boot/pxeboot**。当 **/boot/pxeboot** 执行后，FreeBSD 内核将被加载，并且 FreeBSD 的其余启动过程将继续，如 [FreeBSD 启动过程](https://docs.freebsd.org/en/books/handbook/boot/#boot) 中所述。

> **注意**
>
> 对于基于 UEFI 的 PXE 启动，实际使用的启动加载程序文件是 **/boot/loader.efi**。请参阅下文的 [调试 PXE 问题](https://docs.freebsd.org/en/books/handbook/advanced-networking/#_debugging_pxe_problems) 部分，了解如何使用 **/boot/loader.efi**。

本节介绍如何在 FreeBSD 系统上配置这些服务，以便其他系统可以通过 PXE 启动到 FreeBSD。有关更多信息，请参考 [diskless(8)](https://man.freebsd.org/cgi/man.cgi?query=diskless\&sektion=8\&format=html)。

> **当心**
>
> 如前所述，提供这些服务的系统是不安全的。它应该位于网络的受保护区域，并且不应受到其他主机的信任。

## 34.10.1. 配置 PXE 环境

本节中的步骤配置了内置的 NFS 和 TFTP 服务器。下一节将演示如何安装和配置 DHCP 服务器。在此示例中，将包含 PXE 用户使用的文件的目录为 **/b/tftpboot/FreeBSD/install**。确保该目录存在，并且在 **/etc/inetd.conf** 和 **/usr/local/etc/dhcpd.conf** 中设置相同的目录名称非常重要。

> **注意**
>
> 以下命令示例假设使用 [sh(1)](https://man.freebsd.org/cgi/man.cgi?query=sh\&sektion=1\&format=html) shell。使用 [csh(1)](https://man.freebsd.org/cgi/man.cgi?query=csh\&sektion=1\&format=html) 和 [tcsh(1)](https://man.freebsd.org/cgi/man.cgi?query=tcsh\&sektion=1\&format=html) 的用户需要启动一个 [sh(1)](https://man.freebsd.org/cgi/man.cgi?query=sh\&sektion=1\&format=html) shell 或将命令适应 [csh(1)](https://man.freebsd.org/cgi/man.cgi?query=csh\&sektion=1\&format=html) 语法。

1. 创建根目录，该目录将包含一个可以通过 NFS 挂载的 FreeBSD 安装：

   ```sh
   # export NFSROOTDIR=/b/tftpboot/FreeBSD/install
   # mkdir -p ${NFSROOTDIR}
   ```
2. 通过向 **/etc/rc.conf** 添加以下行来启用 NFS 服务器：

   ```sh
   nfs_server_enable="YES"
   ```
3. 通过向 **/etc/exports** 添加以下内容来通过 NFS 导出无盘根目录：

   ```sh
   /b -ro -alldirs -maproot=root
   ```
4. 启动 NFS 服务器：

   ```sh
   # service nfsd start
   ```
5. 通过向 **/etc/rc.conf** 添加以下行来启用 [inetd(8)](https://man.freebsd.org/cgi/man.cgi?query=inetd\&sektion=8\&format=html)：

   ```sh
   inetd_enable="YES"
   ```
6. 取消注释 **/etc/inetd.conf** 中的以下行，确保它前面没有 `#` 符号：

   ```sh
   tftp dgram udp wait root /usr/libexec/tftpd tftpd blocksize 1468 -l -s /b/tftpboot
   ```

> **注意**
>
> 指定的 tftp 块大小（例如 1468 字节）替换了默认的 512 字节大小。有些 PXE 版本需要使用 TCP 版本的 TFTP。在这种情况下，取消注释第二个 `tftp` 行，其中包含 `stream tcp`。

7. 启动 [inetd(8)](https://man.freebsd.org/cgi/man.cgi?query=inetd\&sektion=8\&format=html)：

   ```sh
   # service inetd start
   ```
8. 将基本系统安装到 **${NFSROOTDIR}** 中，可以通过解压官方归档文件或重新构建 FreeBSD 内核和用户空间来完成（有关更详细的说明，请参考 [“从源代码更新 FreeBSD”](https://docs.freebsd.org/en/books/handbook/cutting-edge/#makeworld)，但在运行 `make installkernel` 和 `make installworld` 命令时不要忘记添加 `DESTDIR=${NFSROOTDIR}`）。
9. 测试 TFTP 服务器是否正常工作，并能通过 PXE 获取启动加载程序：

   ```sh
   # tftp localhost
   tftp> get FreeBSD/install/boot/pxeboot
   Received 264951 bytes in 0.1 seconds
   ```
10. 编辑 **${NFSROOTDIR}/etc/fstab**，并创建一个条目以通过 NFS 挂载根文件系统：

```sh
# Device                                         Mountpoint    FSType   Options  Dump Pass
myhost.example.com:/b/tftpboot/FreeBSD/install       /         nfs      ro        0    0
```

将 *myhost.example.com* 替换为 NFS 服务器的主机名或 IP 地址。在此示例中，根文件系统以只读方式挂载，以防止 NFS 客户端可能删除根文件系统的内容。

11. 为 PXE 启动的客户端机器设置根密码：

```sh
# chroot ${NFSROOTDIR}
# passwd
```

12. 如果需要，通过编辑 **${NFSROOTDIR}/etc/ssh/sshd\_config** 文件并启用 `PermitRootLogin`，为 PXE 启动的客户端机器启用 [ssh(1)](https://man.freebsd.org/cgi/man.cgi?query=ssh\&sektion=1\&format=html) 根登录。该选项在 [sshd\_config(5)](https://man.freebsd.org/cgi/man.cgi?query=sshd_config\&sektion=5\&format=html) 中有详细说明。
13. 在 **${NFSROOTDIR}** 中执行任何其他所需的自定义操作。这些自定义操作可能包括安装软件包或使用 [vipw(8)](https://man.freebsd.org/cgi/man.cgi?query=vipw\&sektion=8\&format=html) 编辑密码文件。

当从 NFS 根卷启动时，**/etc/rc** 检测到 NFS 启动并运行 **/etc/rc.initdiskless**。在这种情况下，**/etc** 和 **/var** 需要是内存支持的文件系统，以便这些目录可以写入，而 NFS 根目录保持只读：

```sh
# chroot ${NFSROOTDIR}
# mkdir -p conf/base
# tar -c -v -f conf/base/etc.cpio.gz --format cpio --gzip etc
# tar -c -v -f conf/base/var.cpio.gz --format cpio --gzip var
```

当系统启动时，将创建并挂载 **/etc** 和 **/var** 的内存文件系统，并将 **cpio.gz** 文件的内容复制到其中。默认情况下，这些文件系统的最大容量为 5 兆字节。如果归档文件无法容纳，通常是 **/var** 存储了二进制包，则可以通过在 **${NFSROOTDIR}/conf/base/etc/md\_size** 和 **${NFSROOTDIR}/conf/base/var/md\_size** 文件中输入所需的 512 字节扇区数量（例如，5 兆字节是 10240 扇区）来请求更大的大小。

## 34.10.2. 配置 DHCP 服务器

DHCP 服务器不必与 TFTP 和 NFS 服务器在同一台机器上，但它需要在网络中可访问。

DHCP 并不是 FreeBSD 基本系统的一部分，但可以通过 [net/isc-dhcp44-server](https://cgit.freebsd.org/ports/tree/net/isc-dhcp44-server/) Port 或包进行安装。

安装完成后，编辑配置文件 **/usr/local/etc/dhcpd.conf**。配置 `next-server`、`filename` 和 `root-path` 设置，如下所示：

```sh
subnet 192.168.0.0 netmask 255.255.255.0 {
   range 192.168.0.2 192.168.0.3 ;
   option subnet-mask 255.255.255.0 ;
   option routers 192.168.0.1 ;
   option broadcast-address 192.168.0.255 ;
   option domain-name-servers 192.168.35.35, 192.168.35.36 ;
   option domain-name "example.com";

   # TFTP 服务器的 IP 地址
   next-server 192.168.0.1 ;

   # 通过 tftp 获取的启动加载程序路径
   filename "FreeBSD/install/boot/pxeboot" ;

   # pxeboot 启动加载程序将尝试通过 NFS 挂载该目录作为根文件系统
   option root-path "192.168.0.1:/b/tftpboot/FreeBSD/install/" ;

}
```

`next-server` 指令用于指定 TFTP 服务器的 IP 地址。

`filename` 指令定义了 **/boot/pxeboot** 的路径。使用相对文件名，这意味着路径中不包括 **/b/tftpboot**。

`root-path` 选项定义了 NFS 根文件系统的路径。

编辑完成后，通过向 **/etc/rc.conf** 添加以下行来启用 DHCP 启动：

```sh
dhcpd_enable="YES"
```

然后启动 DHCP 服务：

```sh
# service isc-dhcpd start
```

## 34.10.3. 调试 PXE 问题

配置并启动所有服务后，PXE 客户端应该能够通过网络自动加载 FreeBSD。如果某个特定客户端无法连接，在该客户端机器启动时，进入 BIOS 配置菜单并确认其设置为从网络启动。

本节描述了一些调试提示，用于在没有客户端能够 PXE 启动时，隔离配置问题的源头。

1. 使用 [net/wireshark](https://cgit.freebsd.org/ports/tree/net/wireshark/) 软件包和 Ports 来调试 PXE 启动过程中的网络流量，过程如下面的图示所示。![pxe nfs](https://docs.freebsd.org/images/books/handbook/advanced-networking/pxe-nfs.png)

   图 1. 使用 NFS 根挂载的 PXE 启动过程

   1. 客户端广播 DHCPDISCOVER 消息。
   2. DHCP 服务器响应，返回 IP 地址、next-server、filename 和 root-path 值。
   3. 客户端向 next-server 发送 TFTP 请求，请求获取 filename。
   4. TFTP 服务器响应并将 filename 发送给客户端。
   5. 客户端执行 filename，即 pxeboot(8)，然后加载内核。当内核执行时，指定的 root-path 所述的根文件系统通过 NFS 挂载。
2. 在 TFTP 服务器上，查看 **/var/log/xferlog**，确保 **pxeboot** 是从正确的位置获取的。测试此示例配置：

   ```sh
   # tftp 192.168.0.1
   tftp> get FreeBSD/install/boot/pxeboot
   Received 264951 bytes in 0.1 seconds
   ```

   [tftpd(8)](https://man.freebsd.org/cgi/man.cgi?query=tftpd\&sektion=8\&format=html) 和 [tftp(1)](https://man.freebsd.org/cgi/man.cgi?query=tftp\&sektion=1\&format=html) 的 `BUGS` 部分记录了一些 TFTP 的局限性。
3. 确保根文件系统可以通过 NFS 挂载。测试此示例配置：

   ```sh
   # mount -t nfs 192.168.0.1:/b/tftpboot/FreeBSD/install /mnt
   ```
4. 对于基于 UEFI 的 PXE 启动，使用 **boot/loader.efi** 文件替换 **boot/pxeboot** 文件：

   ```sh
   # chroot ${NFSROOTDIR}
   # mv boot/pxeboot boot/pxeboot.original
   # cp boot/loader.efi boot/pxeboot
   ```


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://book.bsdcn.org/hanbook/di-34-zhang-gao-ji-wang-luo/34.10.-shi-yong-pxe-jin-hang-wu-pan-cao-zuo.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
