> For the complete documentation index, see [llms.txt](https://freebsd-journal-cn.bsdcn.org/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://freebsd-journal-cn.bsdcn.org/20220102-ruan-jian-yu-xi-tong-guan-li/cbsd.md).

# CBSD：第一部分——生产环境

* 原文：[CBSD: Part 1-Production](https://freebsdfoundation.org/wp-content/uploads/2022/03/CBSD-Part-1-Production.pdf)
* 作者：**OLEG GINZBURG**

2012 年，我作为 Nevosoft 的 IT 系统管理员工作，这是一家小型游戏开发公司，所有的服务器基础设施都基于 FreeBSD 操作系统开发。当时，没人听说过 Kubernetes 和 Docker，但得益于 FreeBSD Jail，公司的服务器通过将所有组件分开，每个服务都使用独立的 Jail 容器，从而受益。如今，FreeBSD 拥有十几个（甚至更多）容器编排程序，尽管在 2012 年时，选择并不广泛。那时有 ezjail，但它是为独立服务器提供的解决方案。我们的安装环境有三十到四十台物理服务器，每台服务器上运行着十到二十个容器。除了基本的创建操作外，删除、启动和克隆容器，公司的系统管理员还需要更高级的功能，如远程服务器上的容器管理、容器从一台服务器迁移到另一台服务器的能力，以及将容器保存为可移植镜像的功能。这些结果是通过创建简单的 shell 脚本实现的。然而，2013 年，出于应用专有软件的需要，导致公司开始迁移到 Linux。因为到那时，所有创建的 shell 脚本在功能上已经与 ezjail 平起平坐，某些情况下还增加了独特的功能（例如，最初有 TUI—基于文本的用户界面），于是决定将这些脚本集合合并，并以相同的标题发布到 FreeBSD Ports Tree 中。这标志着 CBSD 项目的开始。

## 与其他管理系统的比较

如今，FreeBSD 至少支持三十种用于管理容器和虚拟机的工具。在 FreeBSD 平台上有 bhyve，从这里开始，列表逐渐变长。2022 年是 CBSD 项目十周年——它持续更新并继续扩展。到目前为止，它是 FreeBSD 平台上最早的虚拟环境管理系统之一。虚拟环境不仅意味着基于 Jail 的容器化，还支持基于 bhyve、XEN 和 QEMU / NVMM 虚拟机监控器的虚拟机。

该项目的开发基于以下概念和理念：

* 既关注单一模式的安装，也关注由多个主机构成的环境；
* 可以与其他解决方案集成；
* 对变化保持开放，并从大局出发思考：越大的构想，潜力越大；而较小的想法通常不容易扩展；
* 保持简洁和灵活。

> “简单就是终极的复杂。”——列奥纳多·达芬奇

围绕 CBSD 已经出现了广泛的工具包：Web 界面、用于将任务传递给分布式 CBSD 节点的消息代理服务、API 服务以及用于与其交互的瘦客户端。

最后，CBSD 是原生的 FreeBSD 产品，而不是 Linux 解决方案的移植尝试。

了解更多关于项目目标的信息：<https://www.bsdstore.ru/en/cbsd_goals_ssi.html>

了解更多关于项目开发的信息：<https://www.bsdstore.ru/en/cbsd_history_ssi.html>

## 子项目

CBSD 不仅是面向最终用户的产品，也是参与构建复杂解决方案的元素，通过委派创建和管理 CBSD 虚拟环境的功能，能够节省大量工时。因此有多个独立的项目和发行版，用于展示 CBSD 工作如何被重用并了解相关实践：

* Reggae（由 Goran Mekic 开发）使用 CBSD 自动化 DevOps 任务；
* 发行包 <https://k8s-bhyve.convectix.com/>（k8s-bhyve）展示了在 bhyve 虚拟机监控器上快速（几秒到 1 分钟内）启动 Kubernetes 集群的能力；
* 发行包 <https://clonos.convectix.com/> 能够在 CBSD 上为 bhyve 容器和虚拟机构建 Web/UI 界面；
* 发行包 <https://myb.convectix.com/>（MyBee）使用户能够通过 CBSD 操作云镜像，而无需使用 UI 和 CLI：可以通过向 CBSD API 发送 HTTP 请求（例如，使用 curl 工具）或使用 nubectl 瘦客户端（<https://github.com/bitcoin-software/nubectl>）来获取虚拟机。

## CBSD 和 Jail 容器：实际应用

下面更好地了解可用的 CBSD jail 操作方法。网站上有关于初始 CBSD 安装和自定义过程的描述，假设你已经拥有运行时版本。设置容器的方式有多种：

**选项 1**：命令行对话格式。这种方法不需要学习各种可能的 jail 参数，因为脚本会自动调取它们：

cbsd jconstruct

![jconstruct 命令行对话格式截图](/files/C3s7onW8CYMbu3Pe8lcw)

**选项 2**：通过 TUI 的对话格式。这种方式也适合初学者，因为与第一种选项一样，不需要了解可能的参数和命令：

cbsd jconstruct-tui

![jconstruct-tui 对话格式截图](/files/6kWiIN4IGbGwu3jyY6ip)

这两种对话框的输出是生成文本配置文件，其中包含 jcreate 脚本的参数集合，你可以直接从 TUI 界面或命令行调用该脚本。

**选项 3**：使用配置文件或命令行参数。

与之前的方法相比，这种方法较为复杂，因为它需要了解可配置参数的名称，但适合环境开发的自动化。你可以通过以下方式合成配置文件模板：

```sh
jname="myjail1"
ver=13.0
baserw=0
pkglist="misc/mc shells/bash"
astart=0
ip4_addr="DHCP"
...
```

启动：`cbsd jcreate jconf=<文件路径>`

此外，也可以直接通过命令行参数指定选项，而无需使用配置文件：

```sh
cbsd jcreate jname=myjail1 ver=13.0 baserw=0 pkglist="misc/mc
shells/bash" astart=0 ip4_addr="DHCP"
```

配置文件和命令行参数可以结合使用：

```sh
cbsd jcreate jconf=<文件路径> ip4_addr="10.0.0.2" runasap=1
```

**选项 4：类似** [**Vagrant**](https://www.vagrantup.com/) **的风格：CBSDfile**

这是一种描述 CBSD 虚拟环境的标记方式，允许同时描述容器设置并在创建时执行自定义操作（例如，将文件复制到容器文件系统并配置服务）。尽管在一个 CBSD 文件中可以描述无限数量的环境，并通过调用 `cbsd up` 和 `cbsd destroy` 来创建和删除它们，但这种标记方式对于某些目录结构来说更为用户友好，通常每个目录仅描述一个环境，例如：<https://github.com/cbsd/cbsdfile-recipes/tree/master/jail>

此外，通过 CBSD 文件格式创建的环境的独特能力与本地环境以及通过 [CBSD API](https://www.bsdstore.ru/en/cbsd_api_ssi.html) 的环境无关。

## Jail 模板

我们不会详细讨论典型的容器工作操作，因为这些内容已经在 [网站](https://www.bsdstore.ru/en/docs.html) 上描述。我们将介绍一些 CBSD 项目的创新，旨在简化 jail 中的操作——特别是与容器模板相关的工作。模板是容器描述和配置的方法。容器可以导出为可移植镜像。容器可以包含标准运行时环境，但在大多数情况下，它们用于服务/应用程序的隔离和通过镜像分发。这种方式有其优缺点。容器方法的一些优点包括服务部署的速度、不会影响基本系统环境，以及能够固定依赖项版本，确保应用程序正常运行。

谈到这种方法的缺点，存在安全性问题，特别是当使用没有自包含构建脚本（模板）的容器或镜像时。这使得对容器中的软件进行更新变得困难（例如，修复 [0-day](https://en.wikipedia.org/wiki/Zero-day_\(computing\)) 漏洞），并且容易出现后门，这些后门可能是镜像收集者故意或无意留下的。考虑到安装某些软件的复杂性，通常会发现容器中配置的服务是手动放置的，或者丢失了组装说明。

另一个缺点是容器服务配置的复杂性。有些镜像可以通过环境变量配置有限数量的参数，尽管这并不总是足够的。例如动态配置，当需要为 WEB 服务器添加和配置多个虚拟主机（vhost）并在数据库管理系统（DBMS）中设置多个数据库时。对于这样的任务，有专门的软件类别，称为配置管理。该类别中最知名的产品包括 [Ansible](https://www.ansible.com/)、[Chef](https://www.chef.io/)、[Puppet](https://puppet.com/)、[Rex](https://www.rexify.org/) 和 [SaltStack](https://saltproject.io/)。然而，通常它们都针对经典环境进行了优化。CBSD 可以结合配置管理器的全部功能和基于容器的方法，从而获得带有管理服务的容器。CBSD 中使用的模板有两种类型：

* 静态（经典）模板，主要仅包含安装软件。例如 CBSD 的 sambashare 容器模板。

类似的模板可以在其他项目中找到：例如，Fockerfile 示例中的 [nginx](https://github.com/sadaszewski/focker/tree/8be1fcaf601cca6afd703f96b1f90946a9831ff3/example/gateway/nginx-http) 模板（[focker](https://github.com/sadaszewski/focker) 项目）：

```sh
base: freebsd-latest
steps:
 - run:
 - ASSUME_ALWAYS_YES=yes IGNORE_OSVERSION=yes pkg install nginx
```

或者，这是 [BastilleBSD](https://bastillebsd.org/) 项目中 [RabbitMQ](https://github.com/BastilleBSD-Templates/rabbitmq/tree/e1a1275090c3d47b38402ab73941afb3c33cf3a6) 模板的示例，其中 CMD 文件的内容是：

```sh
service rabbitmq restart
```

**PKG** 文件内容：

```sh
rabbitmq
python27
```

这些模板的实用性有限，因为它们只是以另一种形式编写了两行 shell 命令：

```sh
pkg install -y rabbitmq python27
service rabbitmq restart
```

还有更复杂的模板，涵盖了服务配置文件的复制，并且在某些情况下，配置过程还会使用复杂的 sed/awk 结构进行处理。

作为一般原则：

a) 这是不可逆操作，为一次性操作而设计：无法回滚或更改配置，除非重新创建容器；

b) 这种模板对其维护要求很高：即使容器中的服务有小更新，源配置文件也可能会发生实质性变化；

c) 无法保证服务会正常运行，或者在配置错误的情况下无法回滚到以前的运行时版本；

d) 在 sed/awk 操作和切换到使用配置管理程序之间，难以找到合适的界限。

这是问题的解决方案，也是 CBSD 项目的主要信息：不要重复造轮子，而是在可能的情况下复用他人的成果。许多开发者维护着配置管理模块，因此我们建议为了节省时间，复用他们的成果来委派配置管理，并使用原生的工具来处理配置。为了实现这一目标，CBSD 从 2016 年起引入了 forms 脚本。CBSD 的 [Forms](https://www.bsdstore.ru/en/13.0.x/wf_imghelper_ssi.html) 功能用于获取和保存用户参数，以适合配置模块的 [YAML](https://en.wikipedia.org/wiki/YAML) 格式——一种特殊的中间件。CBSD 选择 Puppet 作为配置管理系统，但也可以使用其他任何框架。

## 为使用模板准备 CBSD

为了更好地理解，下面创建 jail1 容器，并将 [Redis](https://redis.io/) 服务模板应用到该容器。

目前，预计 CBSD 已正确安装和配置：你可以启动容器，并且 `cbsd dhcpd` 命令会为客户端分配能够访问互联网的地址（例如通过 NAT）。这是容器中 pkg 操作和包安装的必要条件。

1. 必须安装 Git 来从公共仓库 <https://github.com/cbsd> 安装 CBSD 插件。如果尚未安装，安装 git（约 220 MB）或其轻量版 git-lite（约 40 MB）：

```sh
pkg install -y git-lite
```

2. 从 CBSD 基础分发版中包含的配置文件创建系统容器 cbsdpuppet1。这一步需要在每个新的 CBSD 主机上执行一次：

```sh
cbsd jcreate jname=cbsdpuppet1 jprofile=cbsdpuppet
```

cbsdpuppet1 容器不应运行，因为它只承担一项功能——它包含了 CBSD 用于配置容器的 [puppet7](https://www.freshports.org/sysutils/puppet7/) 安装包。

3. 安装 CBSD puppet 模块——这是可选功能，不包含在基本分发版中。该模块包含了经 CBSD 项目验证的 puppet 模块。这一步需要在每个新的 CBSD 主机上执行一次：

```sh
cbsd module mode=install puppet
```

4. 安装 Redis 服务配置模块。这一步需要在每个新的 CBSD 主机上执行一次：

```sh
cbsd module mode=install forms-redis
```

5. 创建容器，名称任意，在其中获取 Redis 服务，例如：jail1：

```sh
cbsd jcreate jname=jail1 runasap=1
```

（注意：参数 `runasap=1` 意味着容器将立即启动）

6. 最后一步：将 Redis 服务模板应用到 jail1 容器：

```sh
cbsd forms module=redis jname=jail1
```

将出现熟悉的 TUI 界面，显示 Redis 模块的参数，你可以根据需要进行配置：

![Redis 模块参数 TUI 界面](/files/UJGmjDJjiFmZDk62mBU3)

保留默认参数，并通过 \[COMMIT] 操作选择它们。脚本运行完成可能需要一段时间（取决于互联网连接速度，因为模块从官方仓库 pkg.FreeBSD.org 安装 Redis 服务器），因此你需要再次确认容器中的服务已安装并正在运行：

```sh
~ # cbsd jexec jname=jail1 sockstat -4l
USER COMMAND PID FD PROTO LOCAL ADDRESS FOREIGN ADDRESS
redis redis-serv 8910 7 tcp4 172.16.0.13:6379 *:*
```

再次调用 cbsd forms，你可以随时重新配置服务。此外，forms 会保留以前的值，并且在初始化时始终输出当前数据。例如，更改以下参数：

* 将端口设置为 7777；
* 设置连接到 Redis 服务器的密码；
* 将 maxmemory 参数设置为 4g；
* 将 maxmemory\_policy 参数设置为 noeviction。

应用新参数后，检查状态：

```sh
~ # cbsd jexec jname=jail1 sockstat -4l
USER COMMAND PID FD PROTO LOCAL ADDRESS FOREIGN ADDRESS
redis redis-serv 12587 7 tcp4 172.16.0.13:7777 *:*
~ # cbsd jexec jname=jail1 grep '^maxmemory' /usr/local/etc/redis.conf
maxmemory 4g
maxmemory-policy noeviction
```

大多数 Puppet 模块会验证数据、配置文件和服务状态，以防止不正确的输入。因此，因无效参数输入导致服务不可用的可能性，比静态模板要小得多。

现在已了解 TUI 界面，下面转向自动化。cbsd forms 允许通过环境变量接受模板的参数，从而省去交互式对话。为了查看某个特定模板接受哪些变量，可以使用参数 `vars` 并指明所需模块：

```sh
~ # cbsd forms module=redis vars
H_BIND H_PORT H_REQUIREPASS H_MAXMEMORY H_MAXMEMORY_POLICY
H_TCP_KEEPALIVE H_LOG_LEVEL H_SYSLOG_ENABLED H_TIMEOUT H_SLAVE_PRIORITY
H_SLAVEOF
```

Forms 会在常规参数的名称前添加 `H_` 前缀，以降低与系统全局变量冲突的可能性。下面第三次重新配置 Redis 端口（将其设置为 9999），但不使用交互模式（`inter=0`）：

```sh
env H_PORT=9999 cbsd forms module=redis jname=jail1 inter=0
```

请注意应用模板时输出的值：

![CBSD 变量输出信息](/files/hfMo6tH2EKIMGOLK7D7z)

这是模板工作结果导出的 CBSD 变量的输出信息。参数化格式可以通过掩码设置，掩码由配置文件 `forms_export_vars.conf` 在目录 `~cbsd/etc` 中定义。

查看其默认值：

<https://github.com/cbsd/cbsd/blob/v13.0.18/etc/defaults/forms_export_vars.conf>

这些值可以自动导出到文件或各种服务发现服务，如 [Consul](https://www.consul.io/)。在这种情况下，你的集群会自动获取这些值，可以用于构建 SOA（面向服务的架构），但这是另一个话题。

除了 Redis，[这里](https://github.com/cbsd) 还有其他服务配置模板：命名为 `modules-forms-XXXX` 的仓库。例如，尝试使用以下模板：

• modules-forms-memcached

• modules-forms-mysql

• modules-forms-grafana

• modules-forms-rabbitmq

• modules-forms-postgresql

• modules-forms-elasticsearch

注意：尽管传统上 1 个服务对应 1 个容器，但对于 CBSD forms，你可以将多个模板应用于同一个环境，从而一次性获得所有服务。

## 它是如何工作的

Puppet 模块（或任何其他类似系统）包含了在 99% 的情况下采用值参数形式的参数，这些参数通常采用 YAML 格式参数化。为了方便用户，CBSD 提供 TUI 界面来处理基本表单，在这个界面中，与 HTML 表单类似，可以存在以下元素：

• 单选按钮（布尔类型的值为 true/false、yes/no）；

• 复选框元素；

• 已知特定值的下拉菜单；

• 用于用户输入的自定义输入框；

为了将参数保持在通用形式中，CBSD forms 使用 [SQLite3](https://www.sqlite.org/index.html) 数据库——一种描述参数输入的表单，存储输入的值。记录（写入）格式是通用的，既适用于 TUI 对话框的自动生成（CBSD forms），也适用于 WEB/HTML 表单（[ClonOS](https://clonos.convectix.com/)），如下例所示。下面使用 CBSD forms 所需的格式创建 SQLite3 数据库：

```sh
sqlite3 /tmp/myforms.sqlite <<EOF
BEGIN TRANSACTION;
CREATE TABLE forms (  idx INTEGER PRIMARY KEY AUTOINCREMENT, mytable
VARCHAR(255) DEFAULT '', group_id INTEGER DEFAULT 1, order_id INTEGER
DEFAULT 1, param TEXT DEFAULT NULL, desc TEXT DEFAULT NULL, def TEXT
DEFAULT NULL, cur TEXT DEFAULT NULL, new TEXT DEFAULT NULL, mandatory
INTEGER DEFAULT 0, attr TEXT DEFAULT NULL, xattr TEXT DEFAULT NULL, type
VARCHAR(255) DEFAULT 'inputbox', link VARCHAR(255) DEFAULT '', groupname
VARCHAR(128) DEFAULT ''  );
INSERT INTO forms (
mytable,group_id,order_id,param,desc,def,cur,new,mandatory,attr,type,link,
groupname ) VALUES ( "forms", 1,0,"-","BSDMag poll: favorite BSD stuff
on",'-','','',1, "maxlen=128", "delimer", "", "" );
INSERT INTO forms (
mytable,group_id,order_id,param,desc,def,cur,new,mandatory,attr,type,link,
groupname ) VALUES ( "forms", 1,1,"FreeBSD","enter favorite FreeBSD tools,
e.g: sysrc",'bsdinstall','',  '',1, "maxlen=60", "inputbox", "", "" );
INSERT INTO forms (
mytable,group_id,order_id,param,desc,def,cur,new,mandatory,attr,type,link,
groupname ) VALUES ( "forms", 1,2,"DragonFlyBSD","enter favorite DFLY
tools. e.g: checkpoint",'checkpoint','',  '',1, "maxlen=60", "inputbox",
 "", "" );
CREATE TABLE system (  helpername TEXT DEFAULT NULL, helperdesc TEXT
DEFAULT NULL, version INTEGER DEFAULT 0, packages INTEGER DEFAULT 0,
have_restart INTEGER DEFAULT 0, longdesc TEXT DEFAULT NULL, title TEXT
DEFAULT ''  );
INSERT INTO system VALUES('bsdmag',NULL,201607,'','','','');
COMMIT;
EOF
```

![SQLite3 数据库表单截图](/files/yZhxEx93fAMezNcHUCbZ)

下面详细说明这些行中的一些内容。其中最重要的表格列是：

* `param` – 直接的参数名称，表示要获取的值；
* `desc` – 相关的描述（如果适用）；
* `def` – 默认值；
* `new` – 用户输入的新值；
* `type` – 字段类型：inputbox、delimiter、password、group\_add、group\_del、radio、checkbox。

接下来的两行可以添加两个参数，我们希望获取的值是：FreeBSD 和 DragonFlyBSD，每个都有自己的默认值，并且都使用 inputbox 字段格式，因此用户可以自由地输入任意字符串值。

在这个表单上运行 cbsd forms 脚本：

```sh
cbsd forms formfile=/tmp/myforms.sqlite
```

输出将是一个动态构建的表单，用于处理参数：

![动态构建的参数表单](/files/PTCdBm9Z02so4leVROeE)

Redis 服务的表单结构更为复杂，因为它包含布尔类型和下拉菜单类型的字段元素。下面看一下表格：

![Redis 服务表单结构](/files/3tCnl4bt0cBH8jD8l9zC)

以表格 [memory\_policy\_select](http://clonos.bsdstore.ru:81/phpliteadmin.php?database=%2Fusr%2Fjails%2Fvar%2Fdb%2Fredis.sqlite\&table=memory_policy_select\&fulltexts=0\&numRows=30\&action=row_view) 为例，这是 memory\_policy 参数的选择类型变体表：

![memory\_policy 选择类型变体表](/files/E8GGKkM8RIS0anAldUJ3)

在 WEB 界面中，自动生成的表单（截图来自 ClonOS）如下所示：

![ClonOS WEB 界面表单截图](/files/tJnni3CbWdovx5n53RHE)

其中，maxmemory 策略参数位于特定的下拉菜单元素中。

![maxmemory 策略下拉菜单](/files/Tg8yXIYd1dhUU0QHyF5p)

TUI 界面提供了类似的选择：

![TUI 界面选择截图](/files/Gd1cjWS6o9UmmgpM1b9Q)

> **注意**
>
> 所有 CBSD TUI 对话框都可以通过类似的基于 SQL 的表单来描述，这些表单目前仅用于模板操作。

在获取并处理参数后，cbsd forms 之后，cbsd puppet 模块介入操作。接下来发生的事情如下：

* 使用 nullfs 挂载 overlay 文件系统，将 cbsdpuppet1 系统容器中的 puppet7 包文件挂载到目标容器（jail1）的 **/tmp/XXX** 目录；
* 将包含模块参数的 YAML 文件渲染到目标容器（jail1）；
* 获取 puppet apply 指令，应用于可配置容器（jail1），以便从 **/tmp/XXX** 目录应用 puppet 清单。

例如，以下是应用参数时 nullfs 挂载点的样子：

![nullfs 挂载点截图](/files/6ryhn5R2mFHm0vlWWMLA)

服务配置完成后，临时 nullfs 文件系统会卸载，因为它们已经不再需要。因此，查看上述示例中 jail1 容器内已安装的软件，将不会看到 puppet7 包或它依赖的文件。

```sh
~ # cbsd jexec jname=jail1 pkg info
pkg-1.17.4 Package manager
redis-6.2.6 Persistent key-value database
```

换句话说，使用 cbsdpuppet1 容器后，你无需在每个容器中安装 puppet7 和必要的依赖项——配置应用不依赖于目标容器中是否存在任何系统软件。

## 尾声

cbsd forms 是 CBSD 框架在处理基于 jail 的容器时最强大的功能之一，它在 CBSD 和配置管理器之间架起了桥梁。因此，CBSD 项目不再依赖 sed/awk 脚本支持和静态模板，而是为适当的 Puppet 模块贡献 FreeBSD 支持，即使该模块已经不再被维护：如果某个模块能够在 FreeBSD 环境中运行，你可以通过 cbsd forms 透明使用它。这对特定的 CBSD 用户和所有 FreeBSD 上的 Puppet 用户都有好处。如果你使用其他配置系统，可以通过 cbsd forms 脚本调用其他系统。

CBSD 项目支持基于现有模板的即用型镜像的变体和分发，你可以通过 CBSD 仓库使用 `cbsd repo` 和 `cbsd images`。请参阅内联文档和示例：

```sh
~ # cbsd repo --help
~ # cbsd images --help
```

在系列文章的下一篇中，将探讨 CBSD 的虚拟机管理功能。

***

**OLEG GINZBURG** 生活在俄罗斯，担任 [X5 Retail group](https://x5.ru/en/) 的 DevOps 工程师。他自 ZX Spectrum（**译者注：英国公司 Sinclair Research 于 1982 年发布的个人电脑。**）以来便热爱计算机科学，是 Unix 和 FreeBSD 爱好者。他是 FreeBSD 的推广者，并参与多个项目：CBSD、[ClonOS](https://clonos.convectix.com/)、[MyB](https://myb.convectix.com/)、[K8Sbhyve](https://k8s-bhyve.convectix.com/) 和 [AdvanceBSD group](https://www.reddit.com/r/AdvanceBSD/)。


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## 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, and the optional `goal` query parameter:

```
GET https://freebsd-journal-cn.bsdcn.org/20220102-ruan-jian-yu-xi-tong-guan-li/cbsd.md?ask=<question>&goal=<endgoal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is optional and describes the broader end goal you are ultimately trying to accomplish on behalf of the user. GitBook uses it to tailor the answer towards what is most useful for that goal.

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.
