# 新型 ZFS 启动环境工具

* [New ZFS Boot Environments Tool](https://vermaden.wordpress.com/2018/08/24/new-zfs-boot-environments-tool/)
* 作者：𝚟𝚎𝚛𝚖𝚊𝚍𝚎𝚗
* 2018/08/24

> **注意**
>
> 注：15.0-RELEASE 中仅存在命令 **bectl**。

大约一个月前，我有幸在 [**PBUG**](https://vermaden.wordpress.com/2018/07/30/zfs-boot-environments-at-pbug/) 上做了关于 **ZFS 启动环境** 的演讲。在演讲的最后，我提到了在 FreeBSD 上管理 **ZFS 启动环境** 的工具发展历史。

![zfs-boot-environments-history.png](https://vermaden.wordpress.com/wp-content/uploads/2018/08/zfs-boot-environments-history.png?w=960)

Pawel Jakub Dawidek——他也参加了 PBUG #3 会议——建议我应该尝试将 **beadm** 纳入 FreeBSD 基本系统。我也从很多 **beadm** 用户那里听到过这个想法，他们反复询问为什么 FreeBSD 基本系统没有内置 **beadm**。因此，在 PBUG #3 结束后，我正是做了这件事。我创建了新的 PR——[Bug 230323 – Idea/Feature Request – include beadm in the base](https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=230323)——令我（惊喜地）发现，他们将新工具 **bectl** 内置到了 FreeBSD 基本系统！现在，我们的 **ZFS 启动环境** 工具家族中新增了一员——工具 **bectl**。

当然，我仍会维护和更新工具 **beadm**，它仍可通过 FreeBSD Ports 中的 [sysutils/beadm](https://freshports.org/sysutils/beadm) 获取。因为该工具是用 POSIX **/bin/sh** 编写的，所以调试快速且易于修改。简单来说（TLDR）：**bectl** 是用 C 语言实现的 **beadm**，它已引入到了 FreeBSD 基本系统，这意味着它将成为 FreeBSD 12.0-RELEASE 的一部分。目前 **bectl** 已经可以在 12.0-ALPHA2 镜像中使用。

## 对比

新的 **bectl** 工具还处于非常早期的阶段，目前（尚未）能完全替代工具 **beadm**。下面是 **bectl** 和 **beadm** 工具使用信息的简单对比。

```sh
root@fbsd12:~ # beadm
usage:
  beadm activate 
  beadm create [-e nonActiveBe | -e beName@snapshot] 
  beadm create 
  beadm destroy [-F] 
  beadm list [-a] [-s] [-D] [-H]
  beadm rename  
  beadm mount  [mountpoint]
  beadm { umount | unmount } [-f] 
  beadm version
```

……以及新的 **bectl** 工具。

```sh
root@fbsd12:~ # bectl
missing command
usage:  bectl ( -h | -? | subcommand [args...] )
        bectl activate [-t] beName
        bectl create [-e nonActiveBe | -e beName@snapshot] beName
        bectl create beName@snapshot
        bectl destroy [-F] beName | beName@snapshot⟩
        bectl export sourceBe
        bectl import targetBe
        bectl jail [ -o key=value | -u key ]... bootenv
        bectl list [-a] [-D] [-H] [-s]
        bectl mount beName [mountpoint]
        bectl rename origBeName newBeName
        bectl { ujail | unjail } ⟨jailID | jailName | bootenv)
        bectl { umount | unmount } [-f] beName
```

例如，**bectl** 目前无法重命名正在使用/已挂载的启动环境，而 **beadm** 可以。

```sh
root@fbsd12:~ # bectl rename safe new
boot environment is already mounted
failed to rename bootenv safe to new
```

可以通过命令 **zfs rename -u ...** 重命名挂载到 **/** 的 ZFS 数据集（这正是 **beadm** 在底层所做的操作），作为 **bectl** 工具的替代方案。

```sh
root@fbsd12:~ # bectl list
BE      Active Mountpoint Space Created
safe    NR     /          188K  2018-08-18 02:32
default -      -          427M  2018-08-18 02:26

root@fbsd12:~ # zfs list | grep safe
zroot/ROOT/safe      108K  6.85G   427M  /

root@fbsd12:~ # zfs rename -u zroot/ROOT/safe zroot/ROOT/new
```

随后它会像平常一样以新名字显示在 **bectl** 下，如下所示：

```sh
root@fbsd12:~ # bectl list
BE      Active Mountpoint Space Created
new     NR     /          188K  2018-08-18 02:32
default -      -          427M  2018-08-18 02:26
```

**bectl** 相较于 **beadm** 的一个很棒的新功能是可以在指定的启动环境中动态创建 FreeBSD Jail。

下面展示 **bectl** 创建 FreeBSD Jail 的实际操作。

```sh
root@fbsd12:~ # bectl list
BE      Active Mountpoint Space Created
new     NR     /          188K  2018-08-18 02:32
default -      -          427M  2018-08-18 02:26

root@fbsd12:~ # bectl jail default
# pwd
/
# ls /
.cshrc          bin             entropy         libexec         net             root            usr
.profile        boot            etc             media           proc            sbin            var
COPYRIGHT       dev             lib             mnt             rescue          tmp             zroot
# exit
root@fbsd12:~ # jls
   JID  IP Address      Hostname                      Path
     1                  default                       /tmp/be_mount.OnRc

root@fbsd12:~ # mount | grep default
zroot/ROOT/default on /tmp/be_mount.OnRc (zfs, local, noatime, nfsv4acls)

root@fbsd12:~ # bectl unjail default

root@fbsd12:~ # jls
   JID  IP Address      Hostname                      Path
```

如果你从 **beadm** 迁移到 **bectl**，你也需要更加小心，因为 **bectl** 不会让你进行确认 :🙂:

例如，**beadm** 工具会在销毁指定的启动环境前询问你是否确认。而 **bectl** 工具则会直接删除，不会在屏幕上出现任何提示。

```sh
root@fbsd12:~ # bectl list
BE      Active Mountpoint Space Created
new     NR     /          188K  2018-08-18 02:32
default -      -          427M  2018-08-18 02:26

root@fbsd12:~ # beadm destroy safe
Are you sure you want to destroy 'safe'?
This action cannot be undone (y/[n]): n

root@fbsd12:~ # bectl destroy safe

root@fbsd12:~ # bectl list
BE      Active Mountpoint Space Created
new     NR     /          188K  2018-08-18 02:32
```

**bectl** 缺少的功能之一是 *Ansible* 插件支持。**beadm** 支持 *Ansible* 插件，因此如果你希望使用该配置管理工具，那么使用 **bectl** 时就需要退回到 **原生** *Ansible* 模块 :🙂:

好消息是 **beadm** 和 **bectl** 可以在同一台主机上共存，所以你不必二选一。你仍然可以用 **beadm** 工具处理日常任务（或用于 *Ansible* 模块），而用 **bectl** 来处理 **jail**/**unjail** 等选项。

我认为随着时间推移，**bectl** 会增加所需功能，而将这样的工具纳入 FreeBSD 基本系统无疑是个受欢迎的补充。

## 更新 1

[New ZFS Boot Environments Tool](https://vermaden.wordpress.com/2018/08/24/new-zfs-boot-environments-tool/) 文章被收录在 [BSD Now 262 – OpenBSD Surfacing](https://www.jupiterbroadcasting.com/127006/openbsd-surfacing-bsd-now-262/) 集中。

感谢分享！

## 更新 2

最后，我有时间在更新的 FreeBSD-12.0-ALPHA6 版本中再次测试新的 **bectl** 命令，看是否有改进。

现在在无参数调用时，**bectl** 不会显示缺少命令的提示。

可以使用 **bectl** 命令重命名当前正在使用的启动环境。

我注意到的最后一点是，**bectl jail** 命令在退出后不会保持 Jail 处于启用/运行状态，虽然只是外观上的变化，但很重要。

最后，最简单的迁移路径是创建一个简单别名：

```sh
# alias beadm=bectl
```

… 或针对 (T)CSH shell：

```sh
# alias beadm bectl
```


---

# 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://translated-articles.bsdcn.org/2025-nian/new-zfs-boot-environments-tool.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.
