# 19.2 Jail 系统更新

## 多 Jail 管理与分层架构

管理多个 Jail 时，逐个执行更新操作，既浪费存储空间，也不便管理。建立统一模板后，所有 Jail 可共用同一个基础环境，各自拥有独立的可写空间且互不干扰。

本节将建立如下目录结构：

* **/jail/mroot** 是模板目录，是所有 Jail 共用的只读部分，在本例中将被挂载到 **/jail/www**。
* **/jail/skel** 是框架目录，用于创建 Jail，本身不直接供任何 Jail 使用。
* **/jail/www** 是 Jail `www` 运行的根目录，也是只读模板的挂载点，本身是个空目录。
* **/jail/www/s** 是 Jail `www` 的可写部分的挂载点，也是个空目录。
* **/jail/files/www** 是 Jail `www` 的可写部分的实际存放位置，将被挂载到 **/jail/www/s**。

相关文件结构图：

```sh
/jail/
├── mroot/           # 只读模板目录（所有 Jail 共用）
│   ├── usr/
│   │   ├── ports/   # Ports 树
│   │   └── src/     # 系统源代码
│   └── s/           # 符号链接目标目录
├── skel/            # 框架目录（创建 Jail 的模板）
│   ├── home/
│   ├── usr-X11R6/
│   ├── distfiles/
│   ├── portbuild/
│   ├── etc/
│   ├── usr-local/
│   ├── tmp/
│   ├── var/
│   └── root/
├── www/             # Jail www 的根目录（空目录，挂载点）
│   └── s/           # Jail www 的可写部分挂载点（空目录）
├── files/
│   └── www/         # Jail www 的可写部分实际存放位置
└── www.fstab        # Jail www 的 fstab 配置文件
```

需要创建多个 Jail 时，只需重复创建数据目录和项目目录，所有 Jail 均可共用 **/jail/mroot**。

## 安装 cpdup 工具

`cpdup` 是 FreeBSD 系统中用于高效复制目录结构的专用工具，可精确复制文件系统的所有属性，包括权限、时间戳和特殊文件。

使用 pkg 安装 cpdup：

```sh
# pkg install cpdup
```

或者使用 Ports 安装：

```sh
# cd /usr/ports/sysutils/cpdup/
# make install clean
```

安装完成后，可运行 `cpdup` 验证工具是否正确安装。

## 创建框架目录结构

创建所需 Jail 路径并将基本系统文件放入目录，并将 Ports 树和系统源代码放入模板。注意源代码的版本应与 **/jail/mroot** 对应的基本系统（basejail）版本一致。FreeBSD 源代码可参照相关章节获取：

创建 Jail 根目录：

```sh
# mkdir -p /jail/mroot
```

克隆 FreeBSD Ports 仓库到 Jail 内：

```sh
# git clone --filter=tree:0 https://mirrors.ustc.edu.cn/freebsd-ports/ports.git /jail/mroot/usr/ports
```

复制宿主机源代码到 Jail 内（需版本一致）：

```sh
# cpdup /usr/src /jail/mroot/usr/src
```

建立符号链接，将可写部分映射到实际存储位置：

```sh
# cd /jail/mroot  # 进入模板目录
# mkdir s # 创建用于符号链接的目标目录
# 注意：在创建符号链接之前，确保相关目录已被移动到骨架目录，或使用 -f 选项强制创建
# ln -sf s/etc etc  # 创建或替换 /etc 的符号链接
# ln -sf s/home home  # 创建或替换 /home 的符号链接
# ln -sf s/root root  # 创建或替换 /root 的符号链接
# ln -sf ../s/usr-local usr/local  # 创建或替换 /usr/local 的符号链接
# ln -sf ../s/usr-X11R6 usr/X11R6  # 创建或替换 /usr/X11R6 的符号链接
# ln -sf ../../s/distfiles usr/ports/distfiles  # 创建或替换 /usr/ports/distfiles 的符号链接
# ln -sf s/tmp tmp  # 创建或替换 /tmp 的符号链接
# ln -sf s/var var  # 创建或替换 /var 的符号链接
```

## 创建骨架目录

创建骨架目录及其子目录，然后将可写目录移动到骨架目录：

```sh
# mkdir -p /jail/skel  # 创建骨架目录
# mkdir -p /jail/skel/home /jail/skel/usr-X11R6 /jail/skel/distfiles /jail/skel/portbuild  # 创建子目录
# 将可写目录移动到骨架目录
# mv /jail/mroot/etc /jail/skel  # 移动 /etc
# mv /jail/mroot/usr/local /jail/skel/usr-local  # 移动 /usr/local
# mv /jail/mroot/tmp /jail/skel  # 移动 /tmp
# mv /jail/mroot/var /jail/skel  # 移动 /var
# mv /jail/mroot/root /jail/skel  # 移动 /root
```

使用 `etcupdate` 同步系统配置文件。`etcupdate` 是 FreeBSD 系统用于管理系统配置文件更新的专用工具，能够在系统升级过程中自动合并本地修改与系统默认配置，避免配置文件丢失或冲突。`-s` 指定源代码树路径，`-D` 指定目标根目录，`-d` 指定存储合并历史记录的数据库目录。

```sh
# etcupdate -s /jail/mroot/usr/src -d /jail/skel/var/db/etcupdate -D /jail/skel
```

设置 Ports 构建工作目录前缀：设置 `WRKDIRPREFIX` 变量，即可将 Ports 构建过程中的临时工作目录统一指向指定路径，便于集中管理和空间回收。

```sh
# echo "WRKDIRPREFIX?=/s/portbuild" >> /jail/skel/etc/make.conf
```

## 创建数据目录

复制一份框架目录作为数据目录：

```sh
# cpdup /jail/skel /jail/files/www
```

## 创建项目目录

创建项目目录及其子目录：

```sh
# mkdir /jail/www /jail/www/s
```

## 创建 fstab 文件

编辑 **/jail/[www.fstab](http://www.fstab)** 文件。nullfs 是 FreeBSD 提供的一种特殊文件系统，可在同一主机的不同位置重复挂载同一文件系统，支持 ro（只读）和 rw（读写）等挂载选项。若需手动挂载而非通过 fstab，也可使用 `mount_nullfs` 命令直接操作：

```ini
# 将公共的只读系统挂载到项目目录
/jail/mroot /jail/www nullfs ro 0 0
# 将项目数据目录挂载到项目目录
/jail/files/www /jail/www/s nullfs rw 0 0
```

## `jail.conf` 文件配置示例

以下为 jail.conf 配置文件示例：

```ini
# 全局配置部分
exec.start = "/bin/sh /etc/rc";  # 启动命令
exec.stop = "/bin/sh /etc/rc.shutdown jail";  # 停止命令
exec.clean;  # 清理执行环境
mount.devfs;  # 挂载 devfs
allow.raw_sockets = 1;  # 允许原始套接字
allow.sysvipc = 1;  # 允许 System V IPC

# 主机名可使用变量替代
hostname = "${name}.domain.local";

# Jail 的路径，可使用变量替代
path = "/jail/$name";

# IPv4 地址
ip4.addr = 192.168.1.$ip;

# fstab 文件位置
mount.fstab = "/jail/$name.fstab";

www {    # Jail 名称为 www
    $ip = 2; # 占位变量，此处是指定 IP 为 192.168.1.2
    # 如不使用 fstab，可覆盖全局配置
    # mount.fstab = "";
}
```

布尔参数（如 `allow.raw_sockets`、`mount.devfs`）除赋值 `"true"`/`"false"` 外，还可使用无值快捷写法（如 `allow.raw_sockets` 等价于 `allow.raw_sockets = "true"`），或以 `no` 前缀表示否定（如 `allow.nomount` 等价于 `allow.mount = "false"`）。

## 参考文献

* FreeBSD Project. jail -- manage system jails\[EB/OL]. \[2026-04-14]. <https://man.freebsd.org/cgi/man.cgi?query=jail&sektion=8>. Jail 管理工具手册页，涵盖创建、修改与删除 Jail 的参数与操作。
* FreeBSD Project. jail.conf -- configuration file for jail(8)\[EB/OL]. \[2026-04-14]. <https://man.freebsd.org/cgi/man.cgi?query=jail.conf&sektion=5>. Jail 配置文件格式手册页，定义参数语法与伪参数。
* FreeBSD Project. nullfs -- null file system\[EB/OL]. \[2026-04-14]. <https://man.freebsd.org/cgi/man.cgi?query=nullfs&sektion=4>. nullfs 文件系统手册页，描述只读与读写挂载选项。
* FreeBSD Project. mount\_nullfs -- mount a null layer\[EB/OL]. \[2026-04-14]. <https://man.freebsd.org/cgi/man.cgi?query=mount_nullfs&sektion=8>. nullfs 挂载命令手册页。
* FreeBSD Project. cpdup -- mirror directories\[EB/OL]. \[2026-04-14]. <https://man.freebsd.org/cgi/man.cgi?query=cpdup&sektion=1>. 目录镜像复制工具手册页，用于 Jail 文件系统复制。
* FreeBSD Project. devfs -- device file system\[EB/OL]. \[2026-04-14]. <https://man.freebsd.org/cgi/man.cgi?query=devfs&sektion=4>. 设备文件系统手册页，描述 devfs 挂载与规则集管理。
* FreeBSD Project. devfs -- device file system manager\[EB/OL]. \[2026-04-14]. <https://man.freebsd.org/cgi/man.cgi?query=devfs&sektion=8>. devfs 管理工具手册页，描述规则集配置与设备访问控制。
* FreeBSD Project. etcupdate -- manage updates to system files not updated by installworld\[EB/OL]. \[2026-04-14]. <https://man.freebsd.org/cgi/man.cgi?query=etcupdate&sektion=8>. 系统配置文件更新管理工具手册页。
* FreeBSD Project. Jails\[EB/OL]. FreeBSD Wiki, \[2026-04-14]. <https://wiki.freebsd.org/Jails>. FreeBSD Wiki 中关于 Jail 的配置与管理信息。


---

# 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/di-19-zhang-jail-rong-qi-guan-li/di-19.2-jie-jail-xi-tong-geng-xin.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.
