> For the complete documentation index, see [llms.txt](https://handbook.bsdcn.org/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://handbook.bsdcn.org/di-17-zhang-jail-yu-rong-qi/17.4.-chuan-tong-jail-hou-jail.md).

# 17.4.传统 jail（厚 jail）

这些 jail 类似于真正的 FreeBSD 系统。它们差不多能像普通宿主系统一样管理，并可独立更新。

## 17.4.1. 创建传统 jail

原则上，jail 只需要主机名、根目录、IP 地址以及用户空间。

jail 的用户空间可从 FreeBSD 官方下载服务器获取。

执行以下命令下载用户空间：

```sh
# fetch https://download.freebsd.org/ftp/releases/amd64/amd64/15.1-RELEASE/base.txz -o /usr/local/jails/media/15.1-RELEASE-base.txz
```

下载完成后，需要将其内容解压到 jail 的目录中。

执行以下命令将用户空间解压到 jail 目录：

```sh
# mkdir -p /usr/local/jails/containers/classic
# tar -xf /usr/local/jails/media/15.1-RELEASE-base.txz -C /usr/local/jails/containers/classic --unlink
```

将用户空间解压到 jail 目录后，需要复制时区和 DNS 服务器的配置文件：

```sh
# cp /etc/resolv.conf /usr/local/jails/containers/classic/etc/resolv.conf
# cp /etc/localtime /usr/local/jails/containers/classic/etc/localtime
```

文件复制完成后，下一步是执行以下命令更新到最新的补丁级别：

```sh
# freebsd-update -b /usr/local/jails/containers/classic/ fetch install
```

最后一步是配置 jail。需要在 **/etc/jail.conf** 文件中，或者在 **jail.conf.d** 目录中，添加包含该 jail 参数的配置条目。

示例如下：

```sh
classic {
  # 启动与日志
  exec.start = "/bin/sh /etc/rc";
  exec.stop = "/bin/sh /etc/rc.shutdown";
  exec.consolelog = "/var/log/jail_console_${name}.log";

  # 权限设置
  allow.raw_sockets;
  exec.clean;
  mount.devfs;

  # 主机名与路径
  host.hostname = "${name}";
  path = "/usr/local/jails/containers/${name}";

  # 网络配置
  ip4.addr = 192.168.1.151;
  interface = em0;
}
```

执行以下命令启动该 jail：

```sh
# service jail start classic
```

关于如何管理 jail 的更多信息，参见 [管理 jail](/di-17-zhang-jail-yu-rong-qi/17.10.-guan-li-jail.md) 一节。

## 17.4.2. 获取 jail 用户空间

上面展示的 fetch-and-extract 流程是填充 jail 根目录的传统方式，但不是唯一方式。同一用户空间也可通过 [bsdinstall(8)](https://man.freebsd.org/cgi/man.cgi?query=bsdinstall\&sektion=8\&format=html)、基本系统软件包或本地构建的源码树来安装。

[bsdinstall(8)](https://man.freebsd.org/cgi/man.cgi?query=bsdinstall\&sektion=8\&format=html) 提供了专用的 `jail` 目标，可在指定目录下为 [jail(8)](https://man.freebsd.org/cgi/man.cgi?query=jail\&sektion=8\&format=html) 设置新用户空间。它会获取并解压分发集而不安装内核，跳过了正常安装中的磁盘分区和网络步骤，因此是手动 fetch 与 [tar(1)](https://man.freebsd.org/cgi/man.cgi?query=tar\&sektion=1\&format=html) 序列的一条命令式、可脚本化的替代方案：

```sh
# bsdinstall jail /usr/local/jails/containers/classic
```

`jail` 目标默认安装基本分发（**base.txz**）。在 FreeBSD 15.0 及更高版本上，[bsdinstall(8)](https://man.freebsd.org/cgi/man.cgi?query=bsdinstall\&sektion=8\&format=html) 还可通过其 `pkgbase` 目标从基本系统软件包填充根目录；下面将介绍基于 pkg 的方法。

从 FreeBSD 15.0 开始，基本系统也以软件包形式发布，因此可完全使用 [pkg(8)](https://man.freebsd.org/cgi/man.cgi?query=pkg\&sektion=8\&format=html) 从 `FreeBSD-base` 存储库安装 jail 用户空间。通过全局 `--rootdir` 选项将 [pkg(8)](https://man.freebsd.org/cgi/man.cgi?query=pkg\&sektion=8\&format=html) 指向 jail 的根目录，并安装基本集的 jail 变体：

```sh
# pkg --rootdir /usr/local/jails/containers/classic install -r FreeBSD-base FreeBSD-set-base-jail
```

若要使用最小用户空间，可改用 `FreeBSD-set-minimal-jail`。以这种方式安装的 jail 此后通过 [pkg(8)](https://man.freebsd.org/cgi/man.cgi?query=pkg\&sektion=8\&format=html) 而非 [freebsd-update(8)](https://man.freebsd.org/cgi/man.cgi?query=freebsd-update\&sektion=8\&format=html) 来修补和升级；参见 [升级 pkgbase jail](/di-17-zhang-jail-yu-rong-qi/17.12.-geng-xin-jail.md)。

> **注意**
>
> 基本系统软件包在 FreeBSD 15.0 中是技术预览。`FreeBSD-base` 存储库在 **/etc/pkg/FreeBSD.conf** 中定义但默认禁用，因此需显式使用 `install -r FreeBSD-base` 选择它。目标根目录及其 **var/cache/pkg** 和 **var/db/pkg** 子目录可能需要先创建。传统的 **base.txz** 分发集在 FreeBSD 15.0 中仍然发布，因此 fetch-and-extract 方法在该版本上仍然可用。

从源码树构建的用户空间可通过 `DESTDIR` 变量安装到 jail 的根目录，这是在 jail 中运行 `-STABLE` 或自定义构建用户空间的方式。构建 world 后，将其及剩余的分发文件安装到 jail 目录：

```sh
# cd /usr/src
# make buildworld
# make installworld DESTDIR=/usr/local/jails/containers/classic
# make distribution DESTDIR=/usr/local/jails/containers/classic
```

`make distribution` 会在 jail 的 **/etc** 下安装默认配置文件，仅在填充 jail 时运行一次；在更新期间重复执行会覆盖 jail 内部的配置。要从源码更新此类 jail，请遵循与宿主相同的流程：使用相同的 `DESTDIR` 运行 `installworld`，通过指向 jail 根目录的 `etcupdate -D` 合并配置文件，并使用相同的 `DESTDIR` 通过 `make delete-old delete-old-libs` 移除过时文件和库，而不是使用 [freebsd-update(8)](https://man.freebsd.org/cgi/man.cgi?query=freebsd-update\&sektion=8\&format=html)。完整的源码更新流程请参见 [从源码更新 FreeBSD](/di-27-zhang-freebsd-geng-xin-yu-sheng-ji/27.1.-gai-shu.md)。

## 17.4.3. 将 jail 克隆为模板

一旦厚 jail 完全配置好——安装了软件包、启用了服务、设置了本地配置——它就可以作为模板镜像：一种可几乎瞬间部署大量相同 jail 的模板。

在 OpenZFS 上，这是通过对已配置 jail 的数据集进行快照和克隆来完成的。这要求源 jail 位于自己的数据集上，因此应使用 `zfs create` 创建，而不是上面传统 jail 流程中使用的 `mkdir`：

```sh
# zfs create -p zroot/jails/containers/classic
```

在快照之前停止 jail 以保证镜像一致性，然后对数据集进行快照并为每个新 jail 克隆一次：

```sh
# service jail stop classic
# zfs snapshot zroot/jails/containers/classic@template
# zfs clone zroot/jails/containers/classic@template zroot/jails/containers/web1
```

在 UFS 宿主上，改为复制已配置 jail 的目录树：

```sh
# cp -a /usr/local/jails/containers/classic /usr/local/jails/containers/web1
```

每个克隆都需要在 **/etc/jail.conf** 或 **jail.conf.d** 中拥有自己的条目，包含唯一的名称、路径和 IP 地址。由于上面示例中的 `path` 和 `host.hostname` 参数都是通过 `${name}` 从块名派生的，因此只有块名和地址不同：

```sh
web1 {
  # 启动与日志
  exec.start = "/bin/sh /etc/rc";
  exec.stop = "/bin/sh /etc/rc.shutdown";
  exec.consolelog = "/var/log/jail_console_${name}.log";

  # 权限设置
  allow.raw_sockets;
  exec.clean;
  mount.devfs;

  # 主机名与路径
  host.hostname = "${name}";
  path = "/usr/local/jails/containers/${name}";

  # 网络配置
  ip4.addr = 192.168.1.152;
  interface = em0;
}
```

模板镜像克隆不同于瘦 jail（参见 [瘦 jail](/di-17-zhang-jail-yu-rong-qi/17.5.-shou-jail.md)）。瘦 jail 保持共享只读基本系统并在模板处一次性维护，而每个模板镜像克隆都是一份完整的、可独立写入的副本，按照 [升级 jail](/di-17-zhang-jail-yu-rong-qi/17.12.-geng-xin-jail.md) 中所述独立修补和升级。OpenZFS 克隆最初作为与快照共享块的省空间写时复制副本，并保持对原始快照的依赖，直到 `zfs promote` 切断该依赖。要获得完全独立的副本，或将 jail 复制到另一个存储池或宿主，请使用 `zfs send` 通过管道传递给 `zfs receive`，而不是使用 `zfs clone`。
