分布式部署指南
Adora 支持跨多台机器部署数据流,用于多机器人车队、边缘 AI 流水线和分布式机器人系统。本指南涵盖集群管理、节点调度、二进制分发、自动恢复和运维最佳实践。
目录
- Overview
- 快速开始
- 特性概览
- 集群配置参考
- 集群命令参考
- 节点调度
- 二进制分发
- systemd 服务管理
- Auto-Recovery
- 滚动升级
- 使用场景
- 运维手册
- 部署 YAML 参考
- 最佳实践
概述
Adora 的分布式架构有三个层级:
CLI --> Coordinator --> Daemon(s) --> Nodes / Operators
(one) (per machine) (user code)
- CLI 向协调器发送控制命令(构建、启动、停止)。
- 协调器编排守护进程、解析节点放置、管理数据流生命周期。
- 守护进程在每台机器上运行,生成和监控节点进程。
- 节点通过共享内存(同一机器)或 Zenoh 发布/订阅(跨机器)通信。
分布式部署有两种路径:
临时部署 —— 在每台机器上手动启动 adora daemon,然后使用协调器进行控制。适合开发和测试。参见 CLI 参考中的分布式部署。
托管部署 (cluster.yml) —— 在 YAML 文件中定义集群拓扑,然后使用 adora cluster 命令进行基于 SSH 的生命周期管理。本指南重点介绍托管路径。
快速开始
- 创建
cluster.yml:
coordinator:
addr: 10.0.0.1
machines:
- id: robot
host: 10.0.0.2
user: ubuntu
- id: gpu-server
host: 10.0.0.3
user: ubuntu
- 启动集群:
adora cluster up cluster.yml
- 启动数据流:
adora start dataflow.yml --name my-app --attach
- 检查集群健康状态:
adora cluster status
- 关闭:
adora cluster down
功能一览
| 特性 | 命令 / 配置 | 描述 |
|---|---|---|
| 集群生命周期 | adora cluster up/status/down | 从单台机器进行基于 SSH 的守护进程管理 |
| 标签调度 | _unstable_deploy.labels | 通过键值标签将节点路由到守护进程 |
| 二进制分发 | _unstable_deploy.distribute | local、scp 或 http 策略 |
| systemd 服务 | adora cluster install/uninstall | 可在重启后存活的持久化守护进程服务 |
| Auto-recovery | Automatic | 守护进程重新连接时重新生成节点 |
| 滚动升级 | adora cluster upgrade | SCP 二进制文件 + 逐台机器顺序重启 |
| 数据流重启 | adora cluster restart | 按名称或 UUID 重启运行中的数据流 |
集群配置参考
cluster.yml 文件定义协调器地址和集群中的机器集合。
完整模式
coordinator:
addr: 10.0.0.1 # IP address the coordinator binds to (required)
port: 6013 # WebSocket port (default: 6013)
machines:
- id: edge-01 # 唯一机器标识符(必填)
host: 10.0.0.2 # SSH-reachable hostname or IP (required)
user: ubuntu # SSH 用户(可选,默认为当前用户)
labels: # Key-value labels for scheduling (optional)
gpu: "true"
arch: arm64
- id: edge-02
host: 10.0.0.3
labels:
arch: arm64
字段
coordinator
| Field | 类型 | 默认 | 描述 |
|---|---|---|---|
addr | IP 地址 | (required) | 协调器绑定的地址 |
port | u16 | 6013 | WebSocket 端口 |
machines[]
| Field | 类型 | 默认 | 描述 |
|---|---|---|---|
id | string | (required) | 唯一机器标识符,用于 _unstable_deploy.machine |
host | string | (required) | 可通过 SSH 访问的主机名或 IP 地址 |
user | string | 当前用户 | SSH 用户名 |
labels | map | empty | 用于基于标签调度的键值对 |
验证规则
- 至少必须定义一台机器。
- 机器 ID 必须非空且唯一。
- 机器主机名必须非空。
- 未知字段会被拒绝(
deny_unknown_fields)。
示例:3 机器 GPU 集群
coordinator:
addr: 192.168.1.1
machines:
- id: coordinator-host
host: 192.168.1.1
labels:
role: control
- id: gpu-a100
host: 192.168.1.10
user: ml
labels:
gpu: a100
arch: x86_64
- id: jetson-01
host: 192.168.1.20
user: nvidia
labels:
gpu: jetson
arch: arm64
集群命令参考
所有 adora cluster 命令都基于 cluster.yml 文件操作,并使用 SSH 管理远程机器。
使用的 SSH 选项:BatchMode=yes、ConnectTimeout=10、StrictHostKeyChecking=accept-new。
adora cluster up
从 cluster.yml 文件启动多机器集群。在本地启动协调器,然后通过 SSH 登录每台机器启动守护进程。
adora cluster up <PATH>
Arguments:
| Argument | 描述 |
|---|---|
PATH | 集群配置文件的路径 |
Behavior:
- 加载并验证集群配置。
- 在本地
addr:port上启动协调器。 - 对每台机器,通过 SSH 登录并运行
nohup adora daemon --machine-id <id> --coordinator-addr <addr> --coordinator-port <port> [--labels k1=v1,k2=v2] --quiet。 - 轮询直到所有预期的守护进程注册到协调器(30 秒超时)。
Example:
$ adora cluster up cluster.yml
Starting coordinator on 10.0.0.1:6013...
Starting daemon on robot (ubuntu@10.0.0.2)... OK
Starting daemon on gpu-server (ubuntu@10.0.0.3)... OK
All 2 daemons connected.
adora cluster status
显示集群的当前状态。展示已连接的守护进程和活跃数据流数量。
adora cluster status [--coordinator-addr ADDR] [--coordinator-port PORT]
Flags:
| 标志 | 默认 | 描述 |
|---|---|---|
--coordinator-addr | localhost | 协调器主机名或 IP |
--coordinator-port | 6013 | 协调器 WebSocket 端口 |
Example:
$ adora cluster status
DAEMON ID LAST HEARTBEAT
robot 2s ago
gpu-server 1s ago
Active dataflows: 1
adora cluster down
关闭集群(协调器和所有守护进程)。
adora cluster down [--coordinator-addr ADDR] [--coordinator-port PORT]
终止所有守护进程和协调器进程。
adora cluster install
在每台机器上将 adora-daemon 安装为 systemd 服务。通过 SSH 登录每台机器,写入 systemd 单元文件并启用服务。
adora cluster install <PATH>
Arguments:
| Argument | 描述 |
|---|---|
PATH | 集群配置文件的路径 |
Behavior:
对每台机器,创建并启用名为 adora-daemon-<id> 的 systemd 服务。单元文件:
[Unit]
Description=Adora Daemon (<id>)
After=network-online.target
Wants=network-online.target
[Service]
ExecStart=adora daemon --machine-id <id> --coordinator-addr <addr> --coordinator-port <port> --labels k1=v1,k2=v2 --quiet
Restart=on-failure
RestartSec=5
[Install]
WantedBy=multi-user.target
Example:
$ adora cluster install cluster.yml
Installing adora-daemon-robot on ubuntu@10.0.0.2... OK
Installing adora-daemon-gpu-server on ubuntu@10.0.0.3... OK
2/2 succeeded.
adora cluster uninstall
从每台机器卸载 adora-daemon systemd 服务。停止、禁用并移除 systemd 单元。
adora cluster uninstall <PATH>
Behavior:
对每台机器运行:
sudo systemctl stop adora-daemon-<id>
sudo systemctl disable adora-daemon-<id>
sudo rm -f /etc/systemd/system/adora-daemon-<id>.service
sudo systemctl daemon-reload
adora cluster upgrade
滚动升级:通过 SCP 将本地 adora 二进制文件传输到每台机器并重启守护进程。按顺序处理机器以保持可用性。
adora cluster upgrade <PATH>
Behavior:
对每台机器按顺序执行:
- 通过 SCP 将本地
adora二进制文件传输到目标机器的/usr/local/bin/adora。 - 通过
sudo systemctl restart adora-daemon-<id>重启 systemd 服务。 - 轮询协调器直到守护进程重新连接(30 秒超时,500 毫秒间隔)。
在每台机器升级期间,其他机器上的节点继续运行。
Example:
$ adora cluster upgrade cluster.yml
Upgrading robot (ubuntu@10.0.0.2)...
SCP binary... OK
Restart service... OK
Waiting for reconnect... OK (3.2s)
Upgrading gpu-server (ubuntu@10.0.0.3)...
SCP binary... OK
Restart service... OK
Waiting for reconnect... OK (2.8s)
2/2 succeeded.
adora cluster restart
按名称或 UUID 重启运行中的数据流。停止数据流并立即使用存储的描述符重新启动(不需要 YAML 路径)。
adora cluster restart <DATAFLOW>
Arguments:
| Argument | 描述 |
|---|---|
DATAFLOW | 要重启的数据流的名称或 UUID |
Example:
$ adora cluster restart my-app
Restarting dataflow `my-app`
dataflow restarted: a1b2c3d4-... -> e5f6a7b8-...
节点调度
当协调器收到数据流时,它根据数据流 YAML 中的 _unstable_deploy 部分决定哪个守护进程运行每个节点。解析优先级:machine > labels > unnamed。
基于机器的调度
通过 cluster.yml 中的 id 将节点分配到特定机器:
nodes:
- id: camera
_unstable_deploy:
machine: robot
path: ./camera-driver
outputs:
- frames
协调器查找 machine-id 匹配的守护进程。如果没有匹配的守护进程连接,部署将失败并提示:no matching daemon for machine id "robot"。
基于标签的调度
通过要求目标守护进程具有特定标签来分配节点:
nodes:
- id: inference
_unstable_deploy:
labels:
gpu: "true"
path: ./ml-model
inputs:
frames: camera/frames
outputs:
- predictions
协调器查找第一个标签是所需标签超集的已连接守护进程。所有必需的键值对必须完全匹配。如果没有守护进程满足要求,部署将失败并提示:no daemon matches labels {"gpu": "true"}。
未分配的节点
没有 _unstable_deploy 部分(或该部分为空)的节点被分配到第一个未命名的守护进程——即未使用 --machine-id 标志连接的守护进程。
resolve_daemon() 的内部工作原理
协调器在 coordinator/run/mod.rs 中解析节点放置:
resolve_daemon(connections, deploy) -> DaemonId
1. If deploy.machine is Some(id):
-> look up daemon by machine-id
2. Else if deploy.labels is non-empty:
-> find first daemon where all required labels match
3. Else:
-> pick first unnamed daemon
标签匹配函数遍历所有已连接的守护进程,检查每个必需的键值对是否存在于守护进程的标签集中(conn.labels.get(k) == Some(v))。这是一个超集检查:具有 {gpu: "true", arch: "arm64", role: "edge"} 的守护进程满足 {gpu: "true"} 的要求。
二进制分发
通过 distribute 字段控制节点二进制文件如何交付到远程守护进程。
Local(默认)
每个守护进程在自己的机器上从源码构建。这是当前的默认行为。
nodes:
- id: my-node
_unstable_deploy:
machine: edge-01
distribute: local
path: ./my-node
SCP 模式
CLI 在生成前通过 SSH/SCP 将本地构建的二进制文件推送到目标机器。
nodes:
- id: my-node
_unstable_deploy:
machine: edge-01
distribute: scp
path: ./my-node
HTTP 模式
协调器运行制品存储。守护进程在生成前通过 HTTP 从协调器拉取二进制文件。
nodes:
- id: my-node
_unstable_deploy:
machine: edge-01
distribute: http
path: ./my-node
制品通过协调器 WebSocket 端口上的 GET /api/artifacts/{build_id}/{node_id} 提供。该端点需要认证(Bearer token)并对节点 ID 进行清理以防止路径遍历。
何时使用每种策略
| Strategy | 最适用于 | Tradeoffs |
|---|---|---|
local | 同构集群、CI 构建 | 需要在每台机器上安装构建工具链 |
scp | 异构集群、交叉编译的二进制文件 | 需要从 CLI 到所有机器的 SSH 访问 |
http | 隔离的守护进程、防火墙网络 | 需要所有守护进程能访问协调器 |
systemd 服务管理
对于生产部署,将守护进程安装为 systemd 服务,使其在重启后存活并在故障时自动重启。
Install
adora cluster install cluster.yml
在每台机器上创建 systemd 单元文件(完整单元模板见 adora cluster install)。关键属性:
- Restart=on-failure 配合 RestartSec=5:守护进程崩溃时自动重启。
- After=network-online.target:启动前等待网络就绪。
- WantedBy=multi-user.target:开机启动。
Uninstall
adora cluster uninstall cluster.yml
停止、禁用并从每台机器移除单元文件,然后重新加载 systemd 守护进程。
验证服务状态
安装后,直接检查服务:
ssh ubuntu@10.0.0.2 sudo systemctl status adora-daemon-robot
自动恢复
当守护进程断开并重新连接时(例如网络闪断、机器重启或服务重启后),协调器自动在该守护进程上重新生成所有缺失的数据流。
工作原理
- 守护进程重新连接并发送
StatusReport,列出当前运行的数据流。 - 协调器将报告与其预期状态(应在此守护进程上有节点的数据流)进行比较。
- 对于分配到此守护进程但守护进程未报告的每个运行中数据流,协调器发送
SpawnDataflowNodes命令以重新生成缺失的节点。
30 秒退避
为防止崩溃循环(例如节点在生成时立即崩溃),恢复使用每守护进程、每数据流的退避:
- 恢复尝试后,协调器记录时间戳。
- 同一守护进程/数据流对的后续恢复在 30 秒过去之前被跳过。
- 当守护进程报告数据流再次运行时,退避清除。
这意味着立即崩溃的节点只会每 30 秒重新生成一次,而不是紧密循环。
限制
- 自动恢复仅适用于通过
adora start启动的数据流(协调器管理的)。本地adora run数据流不受协调器跟踪。 - 恢复会重新生成分配到重新连接的守护进程的所有节点,而非单个节点。对于节点崩溃时的单节点重启,请使用重启策略。
滚动升级
使用逐台机器顺序升级,以零停机时间升级所有集群机器上的 adora 二进制文件。
Process
adora cluster upgrade cluster.yml
对每台机器按顺序执行:
- SCP 将本地
adora二进制文件传输到目标机器的/usr/local/bin/adora。 - 重启 systemd 服务(
systemctl restart adora-daemon-<id>)。 - 轮询协调器直到守护进程重新连接(30 秒超时)。
由于机器逐台升级,其他机器上的节点继续运行。守护进程重新连接后,自动恢复会重新生成在该机器上运行的所有数据流节点。
前提条件
- 守护进程必须已安装为 systemd 服务(
adora cluster install)。 - 本地
adora二进制文件必须与集群的协调器版本兼容。 - 所有目标机器上具有
sudo权限的 SSH 访问。
使用场景
1. 边缘 AI 流水线(机器人 + GPU 服务器)
摄像头节点在机器人上运行,将帧发送到 GPU 服务器进行推理,结果流回机器人上的执行器。
cluster.yml:
coordinator:
addr: 192.168.1.1
machines:
- id: robot
host: 192.168.1.10
user: ubuntu
labels:
role: edge
- id: gpu-server
host: 192.168.1.20
user: ml
labels:
gpu: "true"
dataflow.yml:
nodes:
- id: camera
_unstable_deploy:
machine: robot
path: ./camera-driver
outputs:
- frames
- id: inference
_unstable_deploy:
labels:
gpu: "true"
path: ./ml-model
inputs:
frames: camera/frames
outputs:
- predictions
- id: actuator
_unstable_deploy:
machine: robot
path: ./actuator-driver
inputs:
commands: inference/predictions
2. 多机器人车队
中央协调器管理 N 个具有异构硬件的机器人。标签调度将节点路由到正确的机器,无需硬编码机器 ID。
cluster.yml:
coordinator:
addr: 10.0.0.1
machines:
- id: bot-01
host: 10.0.0.11
user: robot
labels:
fleet: warehouse
lidar: "true"
- id: bot-02
host: 10.0.0.12
user: robot
labels:
fleet: warehouse
camera: rgbd
- id: bot-03
host: 10.0.0.13
user: robot
labels:
fleet: warehouse
lidar: "true"
camera: rgbd
dataflow.yml:
nodes:
- id: lidar-driver
_unstable_deploy:
labels:
lidar: "true"
path: ./lidar-driver
outputs:
- scans
- id: camera-driver
_unstable_deploy:
labels:
camera: rgbd
path: ./camera-driver
outputs:
- frames
使用此配置,lidar-driver 在 bot-01 或 bot-03 上运行,camera-driver 在 bot-02 或 bot-03 上运行。
3. 机器人 CI/CD 流水线
在 CI 中自动化集群管理:
# Setup
adora cluster install cluster.yml
# Deploy new version
adora cluster upgrade cluster.yml
# Run integration tests
adora start test-dataflow.yml --name integration-test --attach
# Monitor
adora cluster status
adora top
# Cleanup
adora stop integration-test
4. 从开发到生产
| Stage | Approach | 命令 |
|---|---|---|
| 本地开发 | 单进程,无协调器 | adora run dataflow.yml |
| Staging | 临时守护进程,手动设置 | 每台机器上 adora up + adora daemon |
| Production | 托管集群,systemd 服务 | adora cluster install cluster.yml |
运维手册
初始设置清单
- SSH 密钥:分发 SSH 密钥,使 CLI 机器无需密码即可连接所有集群机器(
BatchMode=yes)。 - Adora 二进制文件:在所有机器上安装
adora二进制文件(相同版本)。 - 网络:确保所有机器可访问协调器端口(默认 6013)。确保守护进程之间的 Zenoh 端口开放,以支持跨机器节点通信。
- cluster.yml:创建包含正确 IP、用户和标签的集群配置。
日常运维
# Start a dataflow
adora start dataflow.yml --name my-app --attach
# List running dataflows
adora list
# Monitor resource usage
adora top
# View node logs
adora logs my-app <node-id> --follow
# Stop a dataflow
adora stop my-app
# Check cluster health
adora cluster status
Upgrading
- 在本地构建或下载新的
adora二进制文件。 - 运行
adora cluster upgrade cluster.yml。 - 使用
adora cluster status验证所有守护进程已重新连接。 - 运行中的数据流通过自动恢复机制自动重新生成。
故障排除
守护进程未连接
- 验证协调器正在运行且可访问:
curl http://<addr>:6013/api/health(或检查协调器日志)。 - 检查守护进程日志:
journalctl -u adora-daemon-<id> -f(systemd)或守护进程的 stderr 输出(临时部署)。 - 确认
--coordinator-addr和--coordinator-port与协调器的实际绑定地址匹配。
集群命令期间的 SSH 失败
- 确保从 CLI 机器可以成功执行
ssh -o BatchMode=yes <user>@<host> echo ok。 - 检查
StrictHostKeyChecking=accept-new对你的环境是否可接受(首次连接自动接受主机密钥)。 - 验证
cluster.yml中的user字段与目标机器上的有效 SSH 用户匹配。
标签不匹配错误
- 错误:
no daemon matches labels {"gpu": "true"}。 - 检查守护进程是否使用了正确的
--labels标志启动。 - 运行
adora cluster status查看已连接的守护进程。标签在守护进程启动时从cluster.yml设置,运行时无法更改。
自动恢复未触发
- 自动恢复仅适用于协调器管理的数据流(
adora start),不适用于adora run。 - 检查协调器日志中的
auto-recovery: re-spawning消息。 - 如果节点立即崩溃,恢复被限制为每个守护进程每个数据流每 30 秒一次。
部署 YAML 参考
每个节点上的 _unstable_deploy 部分控制放置和分发。所有字段都是可选的。
nodes:
- id: my-node
_unstable_deploy:
machine: edge-01 # 来自 cluster.yml 的目标机器 ID
labels: # Label requirements (superset match)
gpu: "true"
arch: arm64
distribute: local # local | scp | http
working_dir: /opt/my-app # 目标机器上的工作目录
path: ./my-node
字段
| Field | 类型 | 默认 | 描述 |
|---|---|---|---|
machine | string | none | 目标机器 ID。优先于标签。 |
labels | map | empty | 必需的守护进程标签。所有键值对必须匹配。 |
distribute | string | local | 二进制分发策略:local、scp 或 http。 |
working_dir | path | none | 目标机器上的工作目录。 |
解析优先级
- machine —— 如果设置,节点被分配到具有该机器 ID 的守护进程。
- labels —— 如果设置(且未设置 machine),节点被分配到标签是所需标签超集的第一个守护进程。
- 回退 —— 如果两者都未设置,节点被分配到第一个未命名(无 machine-id)的守护进程。
最佳实践
- 优先使用标签而非机器 ID 以获得灵活性。标签将数据流与特定机器解耦,使添加、移除或替换硬件更容易。
- 生产环境使用 systemd 安装。守护进程服务在重启后存活,并通过
Restart=on-failure在故障时自动重启。 - 在集群中使用协调器持久化(
adora coordinator --store redb),使协调器在重启后存活。参见协调器状态持久化。 - 在节点上设置重启策略以实现每节点弹性。与自动恢复结合实现纵深防御。参见重启策略。
- 使用多种工具监控:
adora cluster status查看守护进程健康状态,adora top查看资源使用,adora logs查看节点输出。 - 先在本地测试。使用
adora run dataflow.yml开发,然后部署到集群。相同的数据流 YAML 在两种模式下都有效——_unstable_deploy字段在本地模式下被忽略。 - 使用滚动升级而非停止整个集群。
adora cluster upgrade每次处理一台机器以保持可用性。 - 将 cluster.yml 与数据流定义一起纳入版本控制。