> 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.12.-geng-xin-jail.md).

# 17.12.升级 jail

升级 FreeBSD jail 可确保这些隔离环境保持安全、最新，并与 FreeBSD 生态系统中可用的最新功能和改进保持一致。

正确的工具取决于 jail 用户空间的安装方式。从发行集安装的 jail（创建章节中解压的 **base.txz**，或由 [bsdinstall(8)](https://man.freebsd.org/cgi/man.cgi?query=bsdinstall\&sektion=8\&format=html) 生成的发行集）在 FreeBSD 14.x 和 15.x 上都通过 `-j` 标志从主机上用 [freebsd-update(8)](https://man.freebsd.org/cgi/man.cgi?query=freebsd-update\&sektion=8\&format=html) 更新。从 FreeBSD 15.0 起可用的、从基础软件包安装的 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) 不适用于它们。从源码构建的 jail 用源码流程（`make installworld` 和 `make distribution`）更新。绝不要在同一 jail 上混用这些机制。

> **注意**
>
> 对发行集的支持计划在 FreeBSD 16 中移除，但将与 [freebsd-update(8)](https://man.freebsd.org/cgi/man.cgi?query=freebsd-update\&sektion=8\&format=html) 一起在 FreeBSD 15 稳定分支的生命周期内继续存在。基础软件包在 FreeBSD 15.0 中作为技术预览提供，并有望在未来的发行版本中成为标准方法，因此 pkgbase 是面向未来的路径。

> **技巧**
>
> 在 OpenZFS 上，升级前对 jail 的数据集进行快照，这样失败的升级可立即撤销：

```sh
# zfs snapshot zroot/jails/containers/classic@preupgrade
```

> 如果升级失败，停止 jail 并运行 `zfs rollback zroot/jails/containers/classic@preupgrade` 以恢复之前的状态。这要求 jail 是其独立的数据集：OpenZFS 瘦 jail 克隆已经是了，但用 [mkdir(1)](https://man.freebsd.org/cgi/man.cgi?query=mkdir\&sektion=1\&format=html) 创建的传统 jail 不是，因此请改用 `zfs create` 创建。对于 NullFS 瘦 jail，改为对模板数据集快照。[zfs-rollback(8)](https://man.freebsd.org/cgi/man.cgi?query=zfs-rollback\&sektion=8\&format=html) 仅回滚到最近的快照，除非加上 `-r` 以销毁更新的快照。

## 17.12.1. 升级传统 jail 或使用 OpenZFS 快照的瘦 jail

jail **必须从主机**操作系统更新。FreeBSD 的默认行为是不能在 jail 中使用 [chflags(1)](https://man.freebsd.org/cgi/man.cgi?query=chflags\&sektion=1\&format=html)，这会阻止某些文件的更新，因此从 jail 内部更新将失败。

要将 jail 更新为正在运行的 FreeBSD 版本的最新补丁版本，请在主机上执行以下命令：

```sh
# freebsd-update -j classic fetch install
# service jail restart classic
```

要将 jail 升级到新的主要或次要版本，首先按照 [执行主要和次要版本升级](/di-27-zhang-freebsd-geng-xin-yu-sheng-ji/27.2.-geng-xin-freebsd.md) 的说明升级主机系统。主机系统升级并重新启动后，便可升级 jail。

> **技巧**
>
> 在版本之间升级时，创建新 jail 比完全升级更容易。

例如，要将 `classic` jail 升级到 14.3-RELEASE，请在主机上执行以下命令：

```sh
# freebsd-update -j classic -r 14.3-RELEASE upgrade
# freebsd-update -j classic install
# service jail restart classic
# freebsd-update -j classic install
# service jail restart classic
```

> **注意**
>
> 必须执行两次 `install` 步骤。jail 没有自己的内核（它运行宿主的内核），因此这里不会升级任何内核。第一次 `install` 将新的用户空间写入 jail；jail 在更新的文件上重启后，第二次 `install` 完成替换那些在之前版本仍在使用时无法更改的组件。

然后，如果是主要版本升级，则需要重新安装所有已安装的软件包并重新启动 jail。这是因为在主要版本之间升级时，ABI 版本会发生变化。

从主机执行以下命令：

```sh
# pkg -j classic upgrade -f
# service jail restart classic
```

## 17.12.2. 升级 OpenZFS 瘦 jail

[使用 OpenZFS 快照创建瘦 jail](/di-17-zhang-jail-yu-rong-qi/17.5.-shou-jail.md) 中创建的瘦 jail 是模板快照的 OpenZFS 克隆，但克隆一旦存在就独立于该模板。刷新模板*不会*更新已经从它克隆出的 jail，而更新克隆也*不会*更新模板。因此有两种方式来保持这些 jail 最新。

第一种方式是用 [freebsd-update(8)](https://man.freebsd.org/cgi/man.cgi?query=freebsd-update\&sektion=8\&format=html) 就地更新每个克隆，与传统 jail 完全相同，如本节“升级传统 jail 或使用 OpenZFS 快照的瘦 jail”小节所述。对于少量 jail 这是最简单的方式，但每个被打过补丁的克隆都会偏离模板快照：更改的块按每个 jail 重复存储，从而侵蚀了空间节省；而主要版本升级会重写大量用户空间，使大部分共享丢失。

第二种方式通过刷新模板并重新克隆来保持共享。在主机上将模板更新到最新的补丁级别：

```sh
# freebsd-update -b /usr/local/jails/templates/14.3-RELEASE/ fetch install
```

> **注意**
>
> 带有 `-b` 的 [freebsd-update(8)](https://man.freebsd.org/cgi/man.cgi?query=freebsd-update\&sektion=8\&format=html) 从主机的 `uname -r` 读取正在运行的发行版本，而非从模板读取。如果模板运行的发行版本比宿主旧，请加上 `--currently-running <template-release>`，使其为模板的发行版本打补丁而非宿主的，如 [使用 NullFS 升级瘦 jail](/di-17-zhang-jail-yu-rong-qi/17.12.-geng-xin-jail.md) 中所示。

然后拍摄新的快照并从它克隆未来的 jail：

```sh
# zfs snapshot zroot/jails/templates/14.3-RELEASE@base-p2
# zfs clone zroot/jails/templates/14.3-RELEASE@base-p2 zroot/jails/containers/newjail
```

现有的克隆会保留创建时使用的基础，直到从新快照重新创建为止。对于主要或次要版本升级，请按照 [使用 OpenZFS 快照创建瘦 jail](/di-17-zhang-jail-yu-rong-qi/17.5.-shou-jail.md) 为新发行版本创建全新的模板数据集，而不是就地升级现有模板，并从其 `@base` 快照克隆新 jail。

> **技巧**
>
> 将每个 jail 的应用数据和配置保存在独立的数据集上，或保存在 jail 的本地存储中，这样从更新的模板快照重新创建 jail 的成本低廉，并优于逐个为每个克隆打补丁。

## 17.12.3. 使用 NullFS 升级瘦 jail

由于使用 NullFS 的瘦 jail 共享大部分系统目录，因此它们非常容易更新。只需要更新模板即可，这样就能同时更新多个 jail。

要将模板更新为正在运行的 FreeBSD 版本的最新补丁版本，请在主机上执行以下命令：

```sh
# freebsd-update -b /usr/local/jails/templates/14.3-RELEASE-base/ fetch install
# service jail restart
```

要将模板升级到新的主要或次要版本，首先按照 [执行主要和次要版本升级](/di-27-zhang-freebsd-geng-xin-yu-sheng-ji/27.2.-geng-xin-freebsd.md) 的说明升级主机系统。主机系统升级并重新启动后，便可升级模板。

因为主机先升级，带 `-b` 的 [freebsd-update(8)](https://man.freebsd.org/cgi/man.cgi?query=freebsd-update\&sektion=8\&format=html) 会检测到宿主的新发行版本而非模板的旧发行版本：`-b` 从主机的 `uname -r` 读取运行版本，与 `-j` 不同——后者检测 jail 自己的用户空间。传入 `--currently-running <template-release>`，使模板从其真实发行版本打补丁。以下示例将运行 14.2-RELEASE 的模板升级到 14.3-RELEASE；请替换为适用的发行版本：

```sh
# freebsd-update -b /usr/local/jails/templates/14.2-RELEASE-base/ --currently-running 14.2-RELEASE -r 14.3-RELEASE upgrade
# freebsd-update -b /usr/local/jails/templates/14.2-RELEASE-base/ install
# service jail restart
# freebsd-update -b /usr/local/jails/templates/14.2-RELEASE-base/ install
# service jail restart
```

## 17.12.4. 升级 pkgbase jail

从 FreeBSD 15.0 起可用的、从基础软件包安装的 jail，用 [pkg(8)](https://man.freebsd.org/cgi/man.cgi?query=pkg\&sektion=8\&format=html) 针对 `FreeBSD-base` 存储库升级，而非用 [freebsd-update(8)](https://man.freebsd.org/cgi/man.cgi?query=freebsd-update\&sektion=8\&format=html)。

`FreeBSD-base` 存储库在 **/etc/pkg/FreeBSD.conf** 中定义，但在 FreeBSD 15.0 上默认禁用，因此升级前请先启用它。

从主机上，用 `-r`（`--rootdir`）将 [pkg(8)](https://man.freebsd.org/cgi/man.cgi?query=pkg\&sektion=8\&format=html) 指向 jail 的根目录：

```sh
# pkg -r /usr/local/jails/containers/pkgjail update
# pkg -r /usr/local/jails/containers/pkgjail upgrade
# service jail restart pkgjail
```

同样的升级可从 jail 内部用 [jexec(8)](https://man.freebsd.org/cgi/man.cgi?query=jexec\&sektion=8\&format=html) 和普通的 `pkg upgrade` 执行：

```sh
# jexec -l pkgjail pkg upgrade
# service jail restart pkgjail
```

[pkg(8)](https://man.freebsd.org/cgi/man.cgi?query=pkg\&sektion=8\&format=html) 对 **/etc** 下带标记的配置文件执行三方合并。当合并无法自动完成时，它将新版本留作 **.pkgnew**；当它接管之前未打包的文件时，将旧副本保存为 **.pkgsave**。升级后请检查 **/etc**，并手动调和这些文件。不要在 pkgbase 系统上运行 [etcupdate(8)](https://man.freebsd.org/cgi/man.cgi?query=etcupdate\&sektion=8\&format=html)；[pkg(8)](https://man.freebsd.org/cgi/man.cgi?query=pkg\&sektion=8\&format=html) 自行处理配置合并。主机的 base package 工作流参见 [使用软件包更新 FreeBSD](/di-27-zhang-freebsd-geng-xin-yu-sheng-ji/27.7.-pkgbase.md)。
