20e4e4ed73
deploy-lxc.md: Debian 12 packages, vault client + identity, Warpgate host key (pinned by the identity), helper scripts and protocol file, all-remote config, cron under exec --secret-env, logs/heartbeat, adding a terminal. design.md 9: P1-6 done for Linux/macOS; Windows still on the helper scripts (TODO unify).
6.8 KiB
6.8 KiB
在精简 LXC 上部署 agentd(design §9 阶段 3)
目标:调度器跑在一台不装任何 agent CLI 的 Debian 12 LXC(agentd-01)上;所有终端(win10、oregon、mac…)都经 Warpgate SSH 远程启动(transport: warpgate-ssh)。LXC 只做调度、回帖、通知。
1. 系统包
apt-get update
apt-get install -y python3 python3-yaml openssh-client git cron ca-certificates curl
# win10 的 helper 脚本另需(Debian 12 默认已有):bash coreutils(base64/sha256sum) gzip libc-bin(iconv)
adduser --disabled-password --gecos '' agentd # 普通用户运行 agentd,不用 root
Python ≥ 3.10(Debian 12 是 3.11)。jsonschema 可选,没有就用内置子集校验器。
2. 网络
| 去向 | 用途 |
|---|---|
nas.tail9531b.ts.net:8443(tailnet) |
Agent Vault 身份配置 identity/agent.json(含 vault token 与 Warpgate 主机公钥)→ LXC 必须加入 tailnet(tailscale up,LXC 需 /dev/net/tun) |
| Vault(身份配置里的地址) | 取 Plane token、Warpgate 密码 |
Warpgate SSH 115.28.210.101:2222 |
所有终端的中继 |
https://plane.kappiee.com |
Plane API |
3. Agent Vault 客户端与身份
sudo -u agentd mkdir -p ~agentd/.local/share/server-access-client
# 从 oregon 复制(权限 700,只给 agentd 用户):
scp oregon:~/.local/share/server-access-client/{agent-access.py,agent-access-askpass.py} \
agentd-01:~agentd/.local/share/server-access-client/
chmod 700 ~agentd/.local/share/server-access-client/*
cd ~agentd/.local/share/server-access-client && sudo -u agentd python3 -I agent-access.py \
--config https://nas.tail9531b.ts.net:8443/identity/agent.json list # authenticated: true,能看到 win10 / oregon 等 target
- 身份是 tailnet 上的 URL(
--config https://…/identity/agent.json),不落盘;也可以换成本地私有 JSON 文件(chmod 600),把vault.config指过去。 - Warpgate known_hosts 不用配:客户端每次用身份配置里的
warpgate.host_key生成临时 known_hosts(StrictHostKeyChecking=yes),不读~/.ssh/known_hosts。只要求有ssh可执行文件。 askpass必须与agent-access.py同目录且可执行。- Warpgate 侧:该身份(
agent用户)要有每个终端 target 的权限(现有:win10、oregon)。
4. 代码与配置
sudo -u agentd git clone https://git.kappiee.com/kappiee/plane-agentd.git ~agentd/plane-agentd
# win10 仍用 helper 脚本(见 design §9 TODO),复制这两个:
sudo -u agentd mkdir -p ~agentd/helpers
scp oregon:/home/ubuntu/Documents/wjj/project/gwent-design/tools/{win10-ps.sh,win10-put.sh} agentd-01:~agentd/helpers/
# 看板协作协议(拼进每轮 prompt,由 agentd 在本机读取):
scp oregon:/home/ubuntu/Documents/wjj/project/gwent/design/board-protocol.md agentd-01:~agentd/board-protocol.md
cp ~agentd/plane-agentd/config/agentd.yaml ~agentd/plane-agentd/config/agentd.local.yaml # git-ignored,优先于 agentd.yaml
config/agentd.local.yaml 里改:
vault.client: ~/.local/share/server-access-client/agent-access.py(~在本机展开)agentd.state_dir: ~/state(JSON 状态、日志、心跳);agentd.helpers_dir: ~/helpersprojects.GWENT.protocol_file: ~/board-protocol.md(它不在终端上读,是 LXC 本机路径;协议更新时重新复制)- 所有终端都是
transport: warpgate-ssh:- win10:
target: win10, os: windows, launch: schtasks(不变) - oregon:
target: oregon, os: linux, launch: nohup, repo: /home/ubuntu/Documents/wjj/project/gwent, run_root: /home/ubuntu/.gwent-board/agentd-runs, bins: {claude: /home/ubuntu/.local/bin/claude} - 远程 POSIX 终端的
repo、run_root必须是绝对路径(在远程展开,~不行);bins写远程的绝对路径(SSH 非登录 shell 只有系统 PATH)。
- win10:
cd ~agentd/plane-agentd && python3 -m unittest && python3 cli/agentd check-config
5. cron
cron/agentd-tick.sh 里写死了 oregon 的路径;在 LXC 上用同样的形状(单行 crontab 或复制脚本改路径):
*/2 * * * * flock -n $HOME/state/cron.lock timeout 900 python3 -I $HOME/.local/share/server-access-client/agent-access.py --config https://nas.tail9531b.ts.net:8443/identity/agent.json exec --secret-env PLANE_OREGON_API_TOKEN=PLANE_OREGON_API_TOKEN --secret-env PLANE_WINDOW10_API_TOKEN=PLANE_WINDOW10_API_TOKEN --timeout 850 -- python3 $HOME/plane-agentd/cli/agentd tick --once >> $HOME/state/agentd-cron.log 2>&1
- token 只经
exec --secret-env进 agentd 进程内存;agentd 起的中继子进程环境里去掉了所有token_key(Config.scrubbed_env()),远程执行器在终端上由 sshd 起,本来就拿不到 LXC 的环境。 --secret-env的完整列表:cli/agentd check-config最后一行。- 切换时先停 oregon 上的 cron(
crontab -e注释掉agentd-tick.sh),再在 LXC 上启用;两个调度器同时跑会重复派单。状态文件~/.gwent-board/agentd/agentd-state.json从 oregon 复制到 LXC 的state_dir,在途运行(handle 里是终端上的绝对路径)照常被新调度器 poll/collect。
6. 日志与心跳
| 路径(LXC) | 内容 |
|---|---|
~/state/agentd.log |
每 tick 的动作、错误 |
~/state/heartbeat.json |
at 超过 10 分钟不更新 = 调度器没在跑 |
~/state/agentd-state.json |
全部工作项状态 |
~/state/agentd-cron.log |
cron 输出(空闲时为空;skip: another tick holds … 无害) |
终端上 <run_root>/<run_id>/ |
每轮的 prompt.md run.sh/run.ps1 pid.txt launch.pid exit.txt stderr.txt + 执行器输出 |
7. 加一个终端(例:mac)
- Warpgate 加 SSH target(如
mac),给agent身份授权;agent-access.py … run --target mac --timeout 30 -- bash -c 'echo ok; uname'能通。 - 在终端上装好 agent CLI 并登录(Claude Code / Codex),clone 工作仓库;记下二进制绝对路径。
- 配置:
远程 shell 是目标账户的登录 shell(bash/zsh 均可,agentd 发的是
mac: transport: warpgate-ssh target: mac os: darwin # linux | darwin;darwin 用 perl POSIX::setsid 脱离会话(无 setsid 命令) launch: nohup repo: /Users/me/src/gwent run_root: /Users/me/.agentd-runs bins: {claude: /Users/me/.local/bin/claude} plane_user: mac token_key: PLANE_MAC_API_TOKEN concurrency: 1 executors: [claude-code]bash -c '<script>');终端需有bash base64 nohup pgrep pkill(Linux 另需setsid,macOS 需perl)。 - Plane:建用户
mac,token 存 Vault 键PLANE_MAC_API_TOKEN;项目routing.by_assignee加mac: mac,default_executor加mac: claude-code;cron 加--secret-env PLANE_MAC_API_TOKEN=PLANE_MAC_API_TOKEN。 cli/agentd check-config,然后cli/agentd smoke GWENT mac。