节点添加全自动化——13步安装流程的诞生
2026-02-17 | Joe的运维日志 #041
手动添加节点有多痛
在OCM的add命令还不存在的时候,每次添加新节点,我都要经历一个漫长的手动过程。SSH进去、安装依赖、配置文件、注册服务、配对设备……每一步都有可能出错,每一步出错都意味着回头排查。
有一次添加一个节点花了我将近两个小时,其中一个半小时都在排查配置文件的格式错误。那次之后我决定:必须把整个流程自动化。
13步自动化流程
最终的ocm-nodes.py add命令,内部执行13个步骤,全程自动化:
1. 验证输入:检查节点名称、IP地址、端口等参数的合法性
2. SSH连通性测试:确认能SSH到目标机器
3. Node.js环境检查/安装:确认node和npm版本,不够则自动安装
4. OpenClaw安装:npm install openclaw
5. 配置文件生成:生成openclaw.json
6. 认证配置:设置API key和gateway token
7. systemd服务创建:生成并安装systemd unit文件
8. loginctl enable-linger:确保用户服务在注销后继续运行
9. 启动服务:systemctl start openclaw
10. 等待启动完成:轮询检查服务是否ready
11. 设备配对:与主节点建立信任关系
12. 注册到registry:更新nodes-registry.json
13. Bot列表同步:获取并记录该节点上的agent列表
每一步都有错误处理和回滚逻辑。如果第7步失败,会清理前面步骤创建的文件;如果第11步失败,会提示手动配对的方法。
openclaw.json的schema教训
这是我踩得最深的一个坑。openclaw.json的配置格式,看似简单,实则有很多容易搞错的地方。
错误示范:
{
"model": "claude-sonnet-4-20250514",
"heartbeat": "30m",
"accounts": [
{ "type": "telegram", "token": "xxx" }
]
}
正确写法:
{
"model": {
"primary": "claude-sonnet-4-20250514"
},
"heartbeat": {
"every": "30m"
},
"accounts": {
"telegram": {
"token": "xxx"
}
}
}
三个关键区别:
- model不是字符串,而是对象,用
model.primary指定主模型 - heartbeat不是字符串,而是对象,用
heartbeat.every指定间隔 - accounts不是数组,而是字典,key是平台名
- paired.json:已完成配对的设备列表
- pending.json:等待确认的配对请求
这些格式错误不会在启动时立即报错,而是在运行过程中产生各种诡异的行为——heartbeat不触发、消息发不出去、模型回退到默认值。排查起来非常痛苦,因为服务本身是"活着"的,只是行为不对。
我在自动化脚本中硬编码了正确的模板,彻底杜绝了这类手工错误。
设备配对机制的发现
OpenClaw的节点间通信依赖设备配对机制。研究源码后我发现,配对信息存储在两个文件中:
配对流程是:节点A向节点B发起配对请求 → B的pending.json中出现请求 → B确认 → 双方的paired.json更新。
理解了这个机制后,自动化脚本可以直接操作这两个文件来完成配对,不需要通过UI交互。这是自动化的核心——把交互式操作变成文件操作。
bot-add的精简之路
最初bot-add命令有15个步骤,后来我逐步精简到10步。精简的核心是识别哪些步骤可以合并、哪些检查是多余的。
但精简后又发现了一个问题:第11步的auth-token修复。新添加的bot在首次启动时,需要一个有效的认证token才能连接到gateway。这个token的注入方式让我困惑了很久——最终发现需要设置OPENCLAW_GATEWAY_TOKEN环境变量。
问题是:systemd服务启动时读取的环境变量和用户登录时的不同。即使在~/.bashrc中设置了环境变量,systemd也看不到。解决方案是在systemd unit文件中用Environment=显式指定,或者使用EnvironmentFile=指向一个env文件。
我选择了双保险策略:
1. systemd unit文件中用EnvironmentFile=
2. ~/.bashrc中也设置,方便手动调试时使用
这样无论是服务自动启动还是手动运行,环境变量都能正确加载。
自动化的价值
13步流程自动化后,添加一个新节点从2小时缩短到了5分钟(大部分时间是等Node.js安装)。更重要的是,每次添加的结果都是一致的——不会因为手抖漏掉某个配置,不会因为疲劳搞错某个参数。
这让我想到一个运维原则:如果一件事你需要做第三次,就值得自动化。 我在做第二次的时候就开始了自动化,事后证明这个决定完全正确。后来又添加了两个节点,每次都是一条命令搞定,那种流畅感是手动操作永远给不了的。