11/Documentation
Crew 命令
同一 URL 下汇聚多台机器的 Collie:invite、join、deputy、故障转移
crew 是指在同一个 lead 下运行 Collie 的多台机器,手机只需通过 lead 的单一 URL 即可访问每台机器的 herd。lead 是手机直接连接的机器,其他机器均为 member,而 deputy 是唯一获准接管的 member。
| 命令 | 作用 | |
|---|---|---|
collie crew invite | 生成一个单次使用、有效期 10 分钟的注册令牌(在 lead 上) | |
collie crew add <ssh-host> | 通过 您自己的 SSH 安装并注册对端(在 lead 上) | |
| `collie crew update <member>… \ | --all` | 预检每台机器,然后是 lead,接着通过 您自己的 SSH 依次预检每个 peer;首次失败即终止运行(详情) |
collie crew status | 模式、member、连通性、密钥提取,以及连接被拒绝的原因 | |
collie crew rotate | 重新签发 crew secret 并分发给每个可达的对等节点 | |
collie crew rename <name> | 为 crew 指定新名称(在 lead 上) | |
collie crew remove <member> | 取消固定并遗忘某个成员(在 lead 上) | |
collie crew set-address <member> <host:port> | 修正此 lead 连接成员的目标地址 | |
collie crew deputy <member> | 指定接管集群的唯一对端并装载;--revoke 表示不指定任何对端 | |
collie crew approve-promote <member> | 在 lead 上授权某个 member 接管,有效期 10 分钟,仅限单次使用;--cancel 可将其清除 | |
collie crew join <lead-address> [<token>] | 加入 crew(在要加入的机器上);未提供 token 时会提示输入,或传入 - 使用 stdin,或传入 @file | |
collie crew leave | 离开 crew;清除本机上的 crew 密钥以及所有其他 member 的固定证书 | |
collie promote | 将当前机器设为 lead(在接管集群的对端上运行;若 lead 已下线则使用 --force) | |
collie reconnect | 成员地址变动:重新指向其新地址,无需重新注册任何内容 |
collie join 和 collie leave 仍可正常使用。它们是 collie crew join 和 collie crew leave 的别名,具有相同的参数和退出代码。
此表中的每个动词也依然支持 collie pack。它是 collie crew 的别名,具有相同的参数和退出代码。collie docs pack 会输出此页面,Web 应用的 /pack 地址会重定向到 /crew。这三项都将在 2.0.0 中移除(ADR 0038)。
deputy、approve-promote 和 promote 命令用于管理故障转移。有关设置和恢复说明,请参见 docs/deployment.md → 备用入口 和 灾难恢复。
两台机器,一个 crew
添加机器需要执行两条命令,一条用于 Herdr,一条用于 Collie;对于 crew add 无法通过 SSH 连接的主机以及仅提供明文 HTTP 的 lead,提供了手动操作流程。
herdr machine add --label <name> <ssh-target> # prepare the remote host
collie crew add <ssh-host> # install Collie there and enroll itherdr machine add 在当前操作的机器上运行。它将 Herdr 安装到远程主机并保存到 Herdr 自身的列表中,但不会将该机器加入 crew。collie crew add 在 lead 上运行。<ssh-target> 和 <ssh-host> 指的是同一台主机,可写作 user@host 或来自 ~/.ssh/config 的 Host 别名,而 <name> 仅用于标记 Herdr 的列表。
collie crew add <ssh-host> 通过你自己的 ssh 在远程主机上安装 Collie 并完成注册。它在本地生成 token,在远程机器上配置 Collie,并在该机器上运行 collie crew join。此过程依赖 远程主机上预装的 Herdr(由 herdr machine add 部署),因此这两条命令需要配合使用。若 lead 仅提供明文 HTTP 服务,则需要额外的手动步骤,因为 crew add 不支持 --insecure:在要加入的机器上运行 collie crew join --insecure 即可。对于特定主机,只能在 crew add 和手动方式中二选一,切勿混用。不带目标的 collie crew add 会列出 ssh 配置和 Herdr 已知的主机,并按名称解析的目标主机进行合并,因此建议直接从该列表中选择,而不是换一种方式手动输入主机名(下方)。
crew add 会走其自身安装类型所指定的路径。从 git checkout 运行的 lead(如 Herdr 插件安装)会将自己的 commit 推送到 member 并在那里进行构建,因此该 member 需要 git 和 Bun。来自 独立安装 或软件包的 lead 没有 commit,因此它会通过同一 SSH 连接发送 Collie 自带的安装程序,直接从其自身运行的 release 安装 member,此时 member 改为需要 curl、tar 以及 sha256sum 或 shasum。--path 在第一条路径上指定远端 checkout,在第二条路径上指定安装根目录。对于已经运行了另一种安装类型的 member,系统会拒绝而非覆盖,拒绝提示中会列出解决此问题的唯一命令。在第二条路径上,member 会自行从 github.com 下载 release,因此无法访问 github.com 的 member 需要使用从 checkout 运行的 lead。
终端中的 collie crew update 走相同的两条路径,并读取相同的信息来决定走哪一条:checkout lead 会将自己的 commit 推送到每个 member,而来自独立安装或软件包的 lead 会将每个 member 同步到其自身运行的 release。在 release lead 上,运行 git checkout 的 member 会被跳过,并在其所在行指明用于迁移的一条命令,即在该机器上运行 collie update --to-tag v<version>。在 checkout lead 上,由 install.sh 安装的 member 则反向跳过:它接收 release,因此由手机的 Updates 页面进行同步。跳过的 member 不会中断运行,确认信息会单独统计。
手动操作流程只需四条命令。当 SSH 无法连接目标主机,或者 lead 仅提供明文 HTTP 时使用该流程,而非由于 lead 的安装类型:任何类型的 lead 都可以使用 crew add 添加 member。lead 是你手机已能访问的实例,而待加入的机器必须已安装并运行 Collie。
- 在 lead 上生成 token。
collie crew invite # prints the token, then the join command to runToken 只有一行:
<token>.<lead-fingerprint>。
- 在待加入的机器上加入 crew,并在提示时粘贴 token。
collie crew join https://lead.tail1234.ts.net从
invite的输出中复制该地址。请使用你的 lead 自己的地址,不要使用本示例。
- 在 lead 上重启服务,使运行中的进程加载新 member。
collie restart
- 在 lead 上检查连接是否已响应。
collie crew status # the new member, its address, and whether the link answered
Token 仅限单次使用,有效期十分钟,且仅显示一次。lead 仅存储其哈希值。运行 invite 会重启 lead 进程以接收传入的注册请求,并输出包含 lead 名称的加入命令。
第 3 步是第二次重启,join 在执行完毕时会进行提示。invite 重启了 lead 以便接收注册。随后 join 将新 member 写入磁盘,但在再次重启之前,运行中的进程不会向该 member 代理流量。
在交互式终端中,join 会提示输入 token。在脚本中,可以传入 - 并通过 stdin 提供 token:
collie crew join https://lead.tail1234.ts.net - # paste the token on stdin也可以传入 @<file> 从磁盘读取 token。直接将明文 token 作为参数传入会触发警告,因为进程列表会对所有本地用户暴露参数(CREW_PROTOCOL.md 第 8.3 节)。
将 lead 地址设置为从此节点可访问的任意主机名或 host:port。不带协议方案和端口的地址会解析为 https://<host>:8787,即 Collie 自身监听器绑定的端口。
crew invite 打印的内容取决于 lead 的发布方式。在默认的 HTTPS 模式下,lead 监听回环地址,tailscale serve 将其发布在端口 443 上。因此 invite 会打印 https://<full-tailnet-name>,以连接端口 443。如果你使用 COLLIE_SERVE_PORT 更改了入口端口,它会打印 <name>:<port>。使用 COLLIE_SERVE_MODE=http 时,lead 自身的监听器通过明文 HTTP 在端口 8787 上响应,invite 会打印简短名称,仅在你更改了端口时才会附带端口。通过明文 HTTP 发送 token 之前,join 会提示确认一次,--insecure 会自动确认此项。显式指定的 http:// 地址仍然需要 --insecure,并且不会出现任何提示。
crew add 为 member 提供相同的入口,因此 member 也会连接端口 443。仅提供裸主机名则意味着使用端口 8787,但位于 tailscale serve 后面的 lead 并不会向 tailnet 开放该端口。
crew join 上的 --address 是 lead 连接此机器时使用的地址,并且需要指定端口:--address <host>:8787。join 会拒绝不带端口的地址,因为那样 lead 将尝试连接端口 443。包含 https://host:8787 的地址仍会被接受,并存储为 host:8787。
多路复用器的选择属于每个节点的本地配置。 在该节点自身的 .env 中配置 COLLIE_MUX,二进制安装位于 ~/.config/collie/.env,Herdr 安装则位于 Herdr 的插件配置目录。crew 协议是机器之间的线路协议,不包含多路复用器专属字段。请注意,对等节点在 v1 中仅在 Herdr 下进行过测试(CREW_PROTOCOL.md 第 16 节)。
crew add 会为新 member 确定该值,因为 member 并不总能自行确定。仅运行单个多路复用器的 member 不受影响,并在首次启动时自行选择该复用器。运行多个复用器的 member 会促使 lead 提示你进行选择,你的选择会作为 COLLIE_MUX 写入该 member 的 .env。已经指定了一个复用器的 member 也不会被更改。传入 --mux <name> 可以提前指定,或替换 member 已有的名称。没有运行任何多路复用器的 member 会收到警告且不会写入任何内容,因为在有复用器运行之前其首次启动会被拒绝。
Herdr 机器与 crew
Herdr 保存的机器列表与 Collie crew 是两个独立的列表,彼此互不影响。
如需添加机器,请参见上文的 两台机器,一个 crew。
Herdr 的机器列表归属于您的 Herdr 窗口。Herdr 0.9.0 将保存的 ssh 目标保留在其客户端中,每次使用时均通过 ssh 打开各个目标。您在当前坐着的机器上,在该窗口内获取这些机器的终端。
crew 指的是在每台机器上运行 Collie,lead 通过 Collie 自带的加密链路访问各个 member,该链路通过基于 ssh 的安装过程一次性建立。crew 不仅能显示终端,还能承载更多功能:传输上传文件;将各机器的 journal 和审计日志保存在运行对应 pane 的机器上;只需在手机上确认一次即可更新整个 crew;在 lead 无响应时可将入口移交给 deputy。在完全没有机器列表概念的 tmux 和 zellij 下,它的工作方式完全一致。
| 内容 | Herdr 的机器列表 | Collie crew |
|---|---|---|
| 连接建立方 | 您的 Herdr 客户端 | lead Collie |
| 承载方式 | 每次使用都走 ssh | Collie 自带的加密链路 |
| 显示内容 | 终端 | 终端、上传、日志、审计日志、更新、故障转移 |
| 显示位置 | Herdr 窗口 | 手机 |
| 支持 tmux 和 zellij | 否 | 是 |
有三个事实将这两个列表区分开来,每一个都是独立的理由。
手机绝不保存 ssh 密钥。ssh 密钥意味着对机器拥有完整的 shell 权限,而手机存在丢失风险。手机保存的是 lead 签发的配对码,仅用于打开应用,无法执行其他操作,并且 collie devices revoke <label> 可以实时作废该配对码,无需重启(配对设备)。
上传文件、journal 和审计日志均保存在运行 pane 的机器上。此外,在 member 完成注册后,crew 链路(即 Collie 对两台机器之间加密线路的称呼)不再需要 ssh。
因此您无需配置两次相同的环境。配置一次 ssh,两个工具均可使用。Herdr 维护其自身窗口的列表,Collie 维护面向手机的 crew。向 Herdr 添加机器不会将其添加到 crew。从 Herdr 移除机器不会将其从 crew 移除。运行 tmux 或 zellij 的 crew 成员绝不会出现在 Herdr 的列表中。
不带目标的 collie crew add 会从两个列表中提供候选,避免重复输入主机名。它会读取 ~/.ssh/config 中的 Host 条目并运行 herdr machine list --json。它会根据每个名称解析到的 ssh 目标合并这两个列表。该命令会跟随该配置中深度为一层的 Include,且仅适用于 ~/.ssh/ 下的路径。对于从被包含文件中再次包含的文件,它不会提供其中的别名。每行都会显示名称的来源:ssh config、herdr 或两者都有。已在该 crew 中的机器对应行会显示该成员的 id,而不是数字。
crew 的连接架构
只有 lead 会暴露前端入口,crew 中的其他机器均不暴露入口。
lead 是受管的前端入口,负责托管 PWA。手机通过 /api/* 上的 HTTPS 访问它,不与其他任何组件通信。lead 通过 /crew/v1/* 访问各个 member,底层采用携带 crew 密钥的固定 mutual TLS 连接。member 是一个功能完整的 Collie 实例,但不提供前端入口,各自维护自身的 agent、journal、上传文件和审计日志。管理完全通过 CLI 进行,无需在 Herdr UI 中操作。底层传输协议规范见 CREW_PROTOCOL.md。
代码通过你自己的 ssh 部署到各台机器。crew add 以这种方式安装 member,crew update 则以此进行版本对齐。crew 链路仅承载运行时数据,绝不会作为代码分发通道。
deputy 是主节点预先指定的一个节点。它会绑定一个带有三条路由的备用入口,且该入口从不公开发布。主节点静默时会激活该入口,而你自己的配对凭据会消耗该入口,以便在主节点离线时手机仍能连接到 deputy。
非 install.sh 安装的成员
crew 会从手机更新除文件由其他程序所有的成员之外的每个成员。
collie crew update 和手机上的一键 crew 更新都会在旧版本旁暂存新版本并将其替换。这适用于安装脚本和 Herdr 创建的两种安装方式,但并不适用于所有安装,因此 crew 会报告其他安装方式而不是直接报错。
打包安装的成员等待其包管理器更新。 由 pacman、nix 或 brew 放置文件的位置,这些文件归该包管理器所有,Collie 不会替换不属于自己的文件(包管理器安装的实例)。crew 不会向其发送更新,将其显示为“等待包管理器”,并在能够确定命令时显示其前缀对应的命令,同时在不包含该机器的情况下将运行视为完成。当你在该机器上运行该命令后版本会拉平,下次检查时该行提示消失。
打包安装的 LEAD 仍会同步其成员节点。 出于相同原因,lead 会拒绝更新自身,且拒绝原因仅在于此:手机端和 collie crew update 都会将所有 member 同步到 lead 当前运行的版本,只需一次确认即可涵盖全部。在包管理器更新了 lead 并且你在其上运行了 collie restart 之后,没有任何组件会自动同步;在手机的 Updates 页面上再进行一次确认即可将 member 升级到 lead 的新版本。
在同样基于源码 checkout 的 lead 下,源码 checkout 属于完整 member。 你自己 clone 并构建的 member 会通过 checkout lead 接收组更新:该 lead 会推送其 commit、重新构建并重启,过程与更新自身完全一致。在没有 commit 的 lead 下,collie crew update 会跳过它并提示在该机器上运行 collie update --to-tag v<version>。
因此混合 crew 属于正常 crew。点击一次即可更新 lead 能够更新的所有成员,并列出无法更新的成员;运行对应的包管理器后,整个 crew 就会重新保持一致。
crew 名称
crew 名称属于显示数据,仅 lead 会显示它。collie crew invite --name "the shed" 会在创建 crew 时为其命名,创建时不带 --name 的 crew 命名为 "collie crew"。若要稍后修改,请在 lead 上运行 collie crew rename <name>。该操作会重写 lead 自身 crew-trust.json 中的名称并重启 bridge,因此 collie crew status 和手机的 crew 页面会立即显示新名称。
不会向 member 发送任何内容。名称只传输一次,即在 lead 对注册请求的响应中,member 存储该名称但绝不显示它。改名后加入的机器会收到新名称,而 crew 中已有的 member 则将旧字符串保留在一个无人读取的字段中。名称会被修剪首尾空格,最多 64 个字符,且不包含控制字符。在 peer 或未加入 crew 的机器上,该动词会拒绝执行并说明应在何处运行。
从 1.7.0 或 1.8.x 升级到 1.9.0
在将 lead 升级到 1.9.0 之前,先将所有 member 升级到 1.8.x。 1.9.0 只支持单一版本的 crew 链接,且 1.8.0 是支持该版本的最早构建。
你无需记住具体是哪些版本。从 1.8.0 开始,当后续版本更改 crew link 时,更新通知会在顶栏、Updates 卡片和每日推送中提醒你,并且那里的说明也是一致的:先更新 lead,随后更新成员。
1.8.0 重命名了机器读取的名称。线路路径、两个环境变量键、三个状态文件以及 journal 前缀现在都包含 crew(ADR 0039)。连接的行为与之前完全一致,你脚本中的内容无需在同一天迁移。
1.8.0 兼容了 1.7.0 member 一个版本,而 1.9.0 不再兼容。 1.8.0 lead 还会响应旧路径,因此仍处于 1.7.0 的 member 可以继续跟随。1.9.0 移除了该兼容逻辑,这与 ADR 0039 的规划一致。
在 1.9.0 lead 下仍处于 1.7.0 的 member 会出现两次提示,且都不会静默忽略。 lead 的预检使 version 检查标红,列出两个版本以及需要运行的命令,并且该标红会阻止 crew 更新,而不是启动一个无法完成的滚动更新。在 collie crew status 中,同一 member 显示为 incompatible,原因末尾为 "this build speaks 2"。
在 member 自己的机器上升级该节点。 1.9.0 lead 无法再通过链接访问它,因此请在该机器上运行 collie update,将其升级到 1.8.x 或更高版本,lead 会在下次轮询时识别到它。
新名称
| 1.7.0 | 1.8.0 | 你的机器上会发生什么 |
|---|---|---|
COLLIE_PACK_TIMEOUT_MS | COLLIE_CREW_TIMEOUT_MS | 在 1.9.0 中已移除。1.9.0 构建仅读取 crew 键,因此未重命名的旧键会使用默认预算 |
COLLIE_PACK_HELLO_TIMEOUT_MS | COLLIE_CREW_HELLO_TIMEOUT_MS | 同上 |
pack-trust.json、pack-ops.json、pack-runtime.json | crew-trust.json、crew-ops.json、crew-runtime.json | 在 1.8.x 中重命名过一次,位于 ~/.local/state/collie/。在 1.9.0 中已移除:从未经历过 1.8.x 的目录在启动时被命名,且 collie 保持独立运行 |
[pack] | [crew] | crew 自身 journal 行的前缀 |
/pack/v1/… | /crew/v1/… | lead 到 member 链接上的所有路径。在 1.9.0 中已移除 |
PACK_PROTOCOL.md | CREW_PROTOCOL.md | 线路协议本身 |
在升级到 1.9.0 之前,在你的 .env 中重命名这两个环境变量键。1.9.0 构建不会读取旧键,也不会对此发出警告。
对于跨越更新前后的 journal,同时 grep 这两个前缀:
journalctl --user -u collie | grep -E '\[(crew|pack)\]'更新前写入的行包含 [pack],更新后写入的行包含 [crew]。整个 crew 都升级到 1.8.0 后,仅过滤 [crew] 即可。