Files
plane-agentd/docs/deploy-lxc.md
T
jj.wang 20e4e4ed73 Docs: LXC deployment guide, remote POSIX transport in the executor contract
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).
2026-10-21 08:25:14 +00:00

6.8 KiB
Raw Blame History

在精简 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: ~/helpers
  • projects.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)。
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)

  1. Warpgate 加 SSH target(如 mac),给 agent 身份授权;agent-access.py … run --target mac --timeout 30 -- bash -c 'echo ok; uname' 能通。
  2. 在终端上装好 agent CLI 并登录(Claude Code / Codex),clone 工作仓库;记下二进制绝对路径。
  3. 配置:
    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]
    
    远程 shell 是目标账户的登录 shell(bash/zsh 均可,agentd 发的是 bash -c '<script>');终端需有 bash base64 nohup pgrep pkill(Linux 另需 setsid,macOS 需 perl)。
  4. 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。
  5. cli/agentd check-config,然后 cli/agentd smoke GWENT mac。