> 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.10.-guan-li-jail.md).

# 17.10.管理 jail

jail 创建完成后，可执行许多操作，如启动、重启或删除 jail、在其中安装软件等。本节将介绍可从主机对 jail 执行的不同操作。

## 17.10.1. 列出正在运行的 jail

要列出主机系统上正在运行的 jail，可使用 [jls(8)](https://man.freebsd.org/cgi/man.cgi?query=jls\&sektion=8\&format=html) 命令：

```sh
# jls
```

输出应该类似于以下内容：

```sh
   JID  IP Address      Hostname                      Path
     1  192.168.1.151  classic                       /usr/local/jails/containers/classic
```

[jls(8)](https://man.freebsd.org/cgi/man.cgi?query=jls\&sektion=8\&format=html) 支持 `--libxo` 参数，能通过 [libxo(3)](https://man.freebsd.org/cgi/man.cgi?query=libxo\&sektion=3\&format=html) 库以其他格式显示输出，例如 `JSON`、`HTML` 等。

例如，执行以下命令以获取 `JSON` 输出：

```sh
# jls --libxo=json
```

输出应该类似于如下内容：

```json
{"__version": "2", "jail-information": {"jail": [{"jid":1,"ipv4":"192.168.1.151","hostname":"classic","path":"/usr/local/jails/containers/classic"}]}}
```

默认列表只是 [jls(8)](https://man.freebsd.org/cgi/man.cgi?query=jls\&sektion=8\&format=html) 能报告内容的一小部分。加上 `-v` 标志可获取每个 jail 的详细多行摘要，包括其状态和分配的 cpuset ID：

```sh
# jls -v
```

使用 `-h` 可在所请求参数的值之上打印带字段名的标题行：

```sh
# jls -h jid name path
```

要查询单个 jail 的特定参数，用 `-j` 指定其名称并列出要打印的参数：

```sh
# jls -j classic path host.hostname ip4.addr
```

输出应该类似于以下内容：

```sh
/usr/local/jails/containers/classic classic 192.168.1.151
```

加上 `-d` 可包含正在关闭的 jail，否则它们在 dying 状态下会被隐藏：

```sh
# jls -d
```

## 17.10.2. 启动、重启和停止 jail

[service(8)](https://man.freebsd.org/cgi/man.cgi?query=service\&sektion=8\&format=html) 用于在主机上启动、重启或停止 jail。

例如，要启动 jail，运行以下命令：

```sh
# service jail start jailname
```

将 `start` 参数更改为 `restart` 或 `stop` 可对 jail 执行其他操作。

停止 jail 不止是杀掉其中的进程。当 jail 停止时，[jail(8)](https://man.freebsd.org/cgi/man.cgi?query=jail\&sektion=8\&format=html) 会先在 jail 内运行其 `exec.stop` 参数中的命令——通常是 `/bin/sh /etc/rc.shutdown`——以便其服务干净地关闭。然后向 jail 中仍在运行的进程发送 `SIGTERM`，并等待最多 `stop.timeout` 秒（默认为 10 秒）让它们退出。超时之后，jail 会被移除，并用 `SIGKILL` 杀掉剩余的一切。如果 `stop.timeout` 设置为零，则不会发送 `SIGTERM`，jail 会立即被移除。这正是运行慢停止守护进程的 jail 在停止时看似挂起十秒的原因；对于运行数据库或其他需要更长时间才能优雅关闭的服务的 jail，可在 **/etc/jail.conf** 中调高 `stop.timeout`。

## 17.10.3. 销毁 jail

销毁 jail 不是像使用 [service(8)](https://man.freebsd.org/cgi/man.cgi?query=service\&sektion=8\&format=html) 停止 jail 并删除 jail 目录和 **/etc/jail.conf** 条目那么简单。

FreeBSD 非常重视系统安全。因此，某些文件即使是 root 用户也不能删除。这种功能称为文件标志（File Flags）。对于文件存储在 UFS 文件系统上的 jail，必须先清除这些标志才能删除 jail 目录。

第一步是停止所需的 jail，执行以下命令：

```sh
# service jail stop classic
```

第二步是使用 [chflags(1)](https://man.freebsd.org/cgi/man.cgi?query=chflags\&sektion=1\&format=html) 移除这些标志，执行以下命令，其中 `classic` 是要删除的 jail 名称：

```sh
# chflags -R 0 /usr/local/jails/containers/classic
```

第三步是删除包含 jail 的目录：

```sh
# rm -rf /usr/local/jails/containers/classic
```

以上两步适用于存储在 UFS 文件系统上的 jail。如果 jail 由其专用的 ZFS 数据集支撑——如 [设置 jail 目录结构](/di-17-zhang-jail-yu-rong-qi/17.3.-zhu-ji-pei-zhi-jail.md) 中所创建——则改为销毁数据集来移除。数据集销毁不受文件标志阻挡，因此此时不需要 [chflags(1)](https://man.freebsd.org/cgi/man.cgi?query=chflags\&sektion=1\&format=html) 这一步：

```sh
# service jail stop classic
# zfs destroy zroot/jails/containers/classic
```

加上 `-r` 可同时销毁数据集持有的所有快照。

> **注意**
>
> 通过克隆模板快照创建的 ZFS 瘦 jail（参见 [瘦 jail](/di-17-zhang-jail-yu-rong-qi/17.2.-jail-de-lei-xing.md)）依赖于该快照。只要还有克隆存在，[zfs(8)](https://man.freebsd.org/cgi/man.cgi?query=zfs\&sektion=8\&format=html) 就会拒绝销毁模板快照，并打印依赖它的克隆列表。要退役一个模板，请先销毁从它克隆出的每个 jail，或对某个克隆执行 `zfs promote` 使其不再依赖模板。

最后，无论哪种情况，都需删除 **/etc/jail.conf** 或 **/etc/jail.conf.d** 中的 jail 条目。对于 NullFS 瘦 jail，还要删除其 **.fstab** 文件和现已为空的挂载点目录。

## 17.10.4. 在 jail 中处理软件包

[pkg(8)](https://man.freebsd.org/cgi/man.cgi?query=pkg\&sektion=8\&format=html) 工具支持 `-j` 参数，用于处理安装在 jail 中的软件包。

例如，要在 jail 中安装 [www/nginx-lite](https://cgit.freebsd.org/ports/tree/www/nginx-lite/)，可**从主机**执行以下命令：

```sh
# pkg -j classic install nginx-lite
```

有关在 FreeBSD 中使用软件包的更多信息，请参见 [安装应用程序：软件包和 Ports](/di-4-zhang-an-zhuang-ying-yong-cheng-xu-ruan-jian-bao-he-ports/4.1.-gai-shu.md)。

## 17.10.5. 从主机管理 jail 中的服务

jail 中的服务可从主机启用和控制，无需先用 [jexec(8)](https://man.freebsd.org/cgi/man.cgi?query=jexec\&sektion=8\&format=html) 进入 jail。[service(8)](https://man.freebsd.org/cgi/man.cgi?query=service\&sektion=8\&format=html) 和 [sysrc(8)](https://man.freebsd.org/cgi/man.cgi?query=sysrc\&sektion=8\&format=html) 都接受 `-j` 参数，通过 ID 或名称指定目标 jail，并作用于该 jail 的 [rc(8)](https://man.freebsd.org/cgi/man.cgi?query=rc\&sektion=8\&format=html) 配置。

延续上面的软件包示例，直接从主机在 `classic` jail 中启用并启动 nginx：

```sh
# sysrc -j classic nginx_enable=YES
# service -j classic nginx start
```

这些命令的效果与通过 [jexec(8)](https://man.freebsd.org/cgi/man.cgi?query=jexec\&sektion=8\&format=html) 运行 [sysrc(8)](https://man.freebsd.org/cgi/man.cgi?query=sysrc\&sektion=8\&format=html) 和 [service(8)](https://man.freebsd.org/cgi/man.cgi?query=service\&sektion=8\&format=html) 相同，但它们让主机端的管理和脚本与非 jail 系统保持一致。

## 17.10.6. 访问 jail

虽然前面提到最好从主机系统管理 jail，但可使用 [jexec(8)](https://man.freebsd.org/cgi/man.cgi?query=jexec\&sektion=8\&format=html) 进入 jail。

可从主机系统执行 [jexec(8)](https://man.freebsd.org/cgi/man.cgi?query=jexec\&sektion=8\&format=html) 命令来进入 jail：

```sh
# jexec -u root jailname
```

`-u` 标志选择以哪个用户身份运行，并针对*主机*的密码数据库解析名称。若要针对 *jail 自己的*密码数据库解析用户，请使用 `-U`。当未给定命令时（如这里），[jexec(8)](https://man.freebsd.org/cgi/man.cgi?query=jexec\&sektion=8\&format=html) 会启动该用户在 jail 内的登录 shell。加上 `-l` 可在干净的登录环境中运行，仅保留 `HOME`、`SHELL`、`TERM` 和 `USER`，将 `PATH` 重置为 **/bin:/usr/bin**，行为如同全新登录；`-d` 设置工作目录，默认为 jail 根目录。

当进入 jail 时，系统将显示在 [motd(5)](https://man.freebsd.org/cgi/man.cgi?query=motd\&sektion=5\&format=html) 中配置的消息。

## 17.10.7. 在 jail 中执行命令

要从主机系统在 jail 中执行命令，可使用 [jexec(8)](https://man.freebsd.org/cgi/man.cgi?query=jexec\&sektion=8\&format=html)。

例如，要停止在 jail 中运行的服务，可执行以下命令：

```sh
# jexec -l jailname service nginx stop
```

## 17.10.8. 从主机查看 jail 中的进程和套接字

从主机上，每个 jail 的进程和套接字都是可见的，并可按 jail 过滤。

[ps(1)](https://man.freebsd.org/cgi/man.cgi?query=ps\&sektion=1\&format=html) 接受 `-J` 将输出限制为单个 jail，按 JID 或名称指定：

```sh
# ps -J classic
```

输出应该类似于以下内容，其中 `STAT` 列中的 `J` 标记表示进程在 jail 中运行：

```sh
 PID TT  STAT    TIME COMMAND
2841  -  IsJ  0:00.02 /usr/sbin/syslogd -ss
2903  -  IsJ  0:00.00 nginx: master process /usr/local/sbin/nginx
```

使用 `ps -J 0` 可仅显示主机自己的进程。还可在 `-o` 输出格式中加入 `jail` 和 `jid` 关键字，以显示每个进程所属的 jail。例如，列出主机上的每个进程及其所属 jail：

```sh
# ps ax -o pid,jail,command
```

[top(1)](https://man.freebsd.org/cgi/man.cgi?query=top\&sektion=1\&format=html) 接受 `-J` 将其实时显示过滤为单个 jail，这会隐含启用 `-j` 并添加 `JID` 列：

```sh
# top -J classic
```

[sockstat(1)](https://man.freebsd.org/cgi/man.cgi?query=sockstat\&sektion=1\&format=html) 用 `-j` 列出属于某个 jail 的套接字，这是检查 jail 中的守护进程在启动服务后实际绑定了哪些端口的自然方式：

```sh
# sockstat -4 -6 -l -j classic
```

从 jail 内部情况正好相反：jail 中的进程只能看到自己 jail 的进程和套接字，永远看不到主机或其他 jail 的。

## 17.10.9. 使用 jail 工具直接管理 jail

上面所示的 [service(8)](https://man.freebsd.org/cgi/man.cgi?query=service\&sektion=8\&format=html) 包装器是开机时运行 jail 的常规方式，但 [jail(8)](https://man.freebsd.org/cgi/man.cgi?query=jail\&sektion=8\&format=html) 也可直接创建、修改和移除 jail，这对于脚本编写以及在不依赖 rc 框架的情况下测试配置很有用。

按名称创建已配置的 jail：

```sh
# jail -c classic
```

移除正在运行的 jail；这同时会移除它创建的任何子 jail 并杀掉其中的进程：

```sh
# jail -r classic
```

`-m` 标志可在不重启的情况下修改正在运行的 jail 的参数。例如，向正在运行的 jail 添加第二个 IPv4 地址：

```sh
# jail -m name=classic ip4.addr+=192.168.250.71
```

[jail(8)](https://man.freebsd.org/cgi/man.cgi?query=jail\&sektion=8\&format=html) 指出“某些参数不能在正在运行的 jail 上更改”；实际中这包括 `path` 和 `vnet`，它们要求重启 jail。

最后，`-e` 可展示所有已配置的非通配符 jail 及其参数，而不会创建、修改或移除任何内容，便于验证配置文件。它接受一个用于分隔参数的字符串：

```sh
# jail -e ,
```

## 17.10.10. 备份和迁移 jail

jail 在很大程度上是自包含的，因此备份或迁移到另一台主机主要就是复制其文件和配置。

先停止 jail 以获得一致的副本；基于 ZFS 的 jail 可在运行时直接快照。对于位于其专用 ZFS 数据集上的 jail，可递归快照并流式传输到目标主机：

```sh
# service jail stop classic
# zfs snapshot -r zroot/jails/containers/classic@migrate
# zfs send -R zroot/jails/containers/classic@migrate | ssh newhost zfs receive zroot/jails/containers/classic
```

[zfs-send(8)](https://man.freebsd.org/cgi/man.cgi?query=zfs-send\&sektion=8\&format=html) 和 [zfs-receive(8)](https://man.freebsd.org/cgi/man.cgi?query=zfs-receive\&sektion=8\&format=html) 的完整机制（包括增量传输）在 [复制](/di-23-zhang-z-wen-jian-xi-tong-zfs/23.4.-zfs-guan-li.md) 中说明。

对于位于 UFS 文件系统上的 jail，使用 [tar(1)](https://man.freebsd.org/cgi/man.cgi?query=tar\&sektion=1\&format=html) 加 `-p` 以保留所有权、权限和文件标志：

```sh
# tar -cpf classic.tar -C /usr/local/jails/containers classic
```

在目标主机上以 `root` 身份用 `tar -xpf` 解压归档，以保留文件标志。当 jail 占用其专用的 UFS 文件系统时，[dump(8)](https://man.freebsd.org/cgi/man.cgi?query=dump\&sektion=8\&format=html) 和 [restore(8)](https://man.freebsd.org/cgi/man.cgi?query=restore\&sektion=8\&format=html) 是替代方案；它们作用于整个文件系统，因此不适用于本章使用的布局——所有 jail 都是同一共享文件系统内的目录。

最后，将 jail 在 **/etc/jail.conf** 中的块——或它在 **/etc/jail.conf.d** 中的文件——复制到新主机，调整任何主机相关的参数（如 `interface`、`ip4.addr` 或 VNET jail 使用的网桥），然后在新主机上启动 jail。

> **警告**
>
> 目标主机运行的 FreeBSD 发行版本不能比 jail 的用户空间旧，遵循与 jail 永远不能运行比宿主更新版本相同的规则。NullFS 瘦 jail 不是自包含的：其模板和骨架也必须存在于目标主机上，因此需一并迁移，或先将 jail 转换为厚拷贝。**/etc/rctl.conf** 中保存的任何 [rctl(8)](https://man.freebsd.org/cgi/man.cgi?query=rctl\&sektion=8\&format=html) 规则必须单独带过去。
