Skip to content

CLI 与脚本参考

本文是 Node Agent 命令行、以及仓库里所有运维 / 开发脚本的参考清单。配置项(env / TOML)见 configuration.md;具体操作流程见 ../guides/

Node Agent CLI

入口:python -m agent.main(安装后亦可用 dn42-node-agent)。参数解析见 apps/node-agent/agent/main.py:56。

默认行为是后台常驻守护进程:启动即 reconcile 一次,然后连接控制面 WebSocket,收到事件再 reconcile。--once / --plan-only / --doctor 是诊断用的单次模式。

参数含义默认
--config PATHagent.toml 配置文件路径内置默认 / /etc/dn42-control/agent.toml
--controller-url URLControl Server 基础 URL(常驻模式必需)
--enrollment-token TOKEN一次性 enrollment token
--requested-node-id ID希望绑定的 node_id无(从状态目录推断)
--hostname FQDN覆盖 inventory 中的 hostname自动探测
--state-dir PATH本地状态目录/var/lib/dn42-control
--rendered-dir PATH渲染输出目录(覆盖默认)<state-dir>/nodes/<node_id>/rendered
--desired-state PATH离线运行使用的 DesiredState JSON
--mode {apply,write-rendered,plan-only}reconcile 深度apply
--once诊断:只跑一次 reconcile 后退出
--plan-only诊断:等价于 --once --mode plan-only
--doctor诊断:跑一次自检后退出
--log-level LEVEL日志级别(INFO / DEBUG …)INFO

模式与约束

  • apply(默认):写盘 + 部署容器 + 本机收敛(WG / BIRD 热重载)。
  • write-rendered:只写渲染文件,不碰容器(无 Docker 的演示 / 调试环境)。
  • plan-only:只渲染 + 规划,不写不部署。
  • 互斥:--once / --plan-only / --doctor 三选一(同一个互斥组)。
  • 冲突:--plan-only 与非 plan-only--mode 同给即 SystemExit--controller-url--desired-state 互斥(XOR)——在线(连控制面)与离线(吃本地 JSON)二选一。
  • 常驻模式前置:必须有 --controller-url,且 mode 不能是 plan-only(仅供单次诊断);不满足即退出并提示。
  • 收到 SIGTERM / SIGINT 优雅退出。

详细运行模式与守护循环见 ../internals/node-agent.md

退出码

模式退出码
--doctor自检全过 0,否则 1
--once / --plan-onlydeploy 失败 1,否则 0(输出 JSON 摘要到 stdout)
常驻收到信号优雅退出 0

运维脚本(deploy/

这些是面向生产 fleet 的脚本,多数通过 Admin API(环境变量 DN42_CP = 控制面 URL、DN42_ADMIN_TOKEN = 管理 token)操作,默认 dry-run,需显式 apply / deploy 才落地,并写 .json 备份供回滚。

当前升级机制:控制面驱动的 agent 自更新

现行主机制是 agent 自更新(架构见 deploy/agent-self-update/README.md,agent 侧实现 apps/node-agent/agent/self_update.py):

  1. admin 构建 wheel 后上传到控制面(POST /api/v1/admin/agent-releases,一次上传取代 scp × N),再设全局目标版本(POST /api/v1/admin/agent-releases/target)。
  2. agent 经 WS 心跳周期上报版本;控制面发现版本低于目标即推送 agent_update_available (含 wheel 清单与 sha256)。
  3. agent 下载 wheel(GET /api/v1/agent/update/wheels/<version>/<filename>),逐字节核对 manifest 中的 sha256(不符即弃、绝不安装),暂存后触发独立 systemd 更新器。
  4. 更新器(独立进程,不随被升级的 agent 一起被杀)离线 pip install -U 整组安装全部 5 个一方 wheel → 重启 agent → 健康门轮询(等 agent 重写出 version==目标 的健康文件) → 超时未见即自动回滚旧版 wheel + 重启 + 记 CRITICAL。
  5. 只升不降:数字版本严格比较,目标 > 当前才更新(防回退 / 防降级攻击)。

下列两个脚本降级为首次引导 / 兜底重滚用途(含自更新代码的首版仍需用它们滚一次; 更新器脚本 / unit 另需每节点装一次,见下节):

脚本作用
build_wheels.sh构建 5 个一方 wheel(dn42-{common,schemas,runtime,templates} + node-agent),版本 = 1.0.<git-rev-count>,产物入 dist/
agent_pip_rollout.shSSH 到节点,scp dist/*.whl/opt/dn42-wheels,离线 pip install -U --no-index --find-links,重启 dn42-node-agent.service

升级流程详见 ../guides/upgrades-and-migrations.md

自更新子系统(deploy/agent-self-update/

文件作用
install-updater.sh每节点一次性安装更新器:scp agent-update.sh/opt/dn42-agent/、unit → /etc/systemd/system/,建 /opt/dn42-wheels/run/dn42-control。之后所有版本走自更新,不再逐台手动滚
agent-update.sh更新器本体(节点上 /opt/dn42-agent/agent-update.sh):读 agent 写的更新标记 → flock 串行化 → 快照当前版本 wheel 到回滚目录 → 离线 pip -U 整组安装 → 重启 agent → 健康门轮询(90s)→ 失败回滚旧版并记 CRITICAL
dn42-node-agent-update.servicesystemd oneshot 更新器 unit,由 agent systemctl start --no-block 触发(进程隔离,不随被升级的 agent 被杀)
README.md架构说明、首次引导步骤与发版操作序列

systemd 单元(deploy/systemd/

文件作用
dn42-control-server.servicePython 温备控制面的 systemd 单元:uvicorn 起 app.main:app:8000),Restart=on-failure + ProtectSystem=strict 加固;DSN / token 经 Environment= 注入(生产建议指向 PostgreSQL),安装步骤见文件头注释
dn42-node-agent@.servicenode-agent 的 templated 常驻单元(%i = node_id,可同机多实例);节点专属变量放 /etc/dn42-control/<node_id>.env

一次性迁移 / 编排脚本(Python)

脚本作用
renumber_loopbacks.py把节点 loopback 对齐到单播 /27;同步所有节点 internal_topology.hosts + DNS 记录。默认 dry-run,apply 落地,--verify 校验
unify_internal_topology.py把各节点 internal_topology 统一为全节点 full-mesh(修 pvg2 缺路由)。deploy / --verify / --rollback。⚠️ 历史版本硬编码旧地址,renumber 后须先更新再跑
dns_anycast_lo_cleanup.py把 anycast DNS 地址从 dn42-lo(每节点)迁到 dns-anycast dummy(共享),剥重复。--dry-run / apply / --rollback
bird_socket_mount_rollout.py预注入 /run/bird 可写挂载(路由采集从 docker exec birdc 切到直连 socket)。deploy / --verify / --rollback

历史一次性 rollout(已被 wheel 升级取代,留档)

agent_arc11_full_rollout.shagent_drop_unknown_rollout.shagent_filtered_fulllist_rollout.shagent_filtered_routes_rollout.shagent_import_limit_rollout.shagent_invalid_routes_rollout.shagent_prefilter_rollout.shagent_reject_reason_rollout.sh——都是 wheel 发布机制成型前,针对单次能力上线的手工滚动脚本。新升级一律走控制面驱动的 agent 自更新(见上「当前升级机制」),这些仅作历史参考。


开发脚本(scripts/

scripts/dev/ —— 本地演示 / 渲染

脚本作用
provision-three-node.py等控制面 /healthz 就绪后,向 POST /api/v1/admin/provision 灌入 3 个节点的本地 lab(独立脚本,对任意运行中的控制面用,如 docker 全栈方案起的控制面)
render-local-three-node.py把三节点 lab 渲染到磁盘(独立渲染,不依赖控制面)
render-local-two-node.py两节点 iBGP 拓扑本地渲染
render-two-internal-one-ebgp-demo.py2 内部 + 1 外部 eBGP 对端的多场景渲染
sanitize_examples.py清洗示例渲染产物(脱敏)

scripts/tools/ —— 数据导入 / backfill

脚本作用
import_node_config.py解析既有节点配置(bird.confwireguard/*.confscripts/wg/apply-*.sh)为 DesiredState,导入 DB 或 --dry-run 输出 JSON
backfill_node_lla.py从 WG 接口 addresses 剥掉本端 fe80::X/64 副本(LLA 集中到 NodeSpec.link_local 后)。dry-run / apply / 幂等。见 addressing-model.md
create_rdns_26.py/26 寻址创建反向 DNS zone + 记录

scripts/db/

预留目录(DB 管理工具)。Alembic 迁移见 database.md../guides/upgrades-and-migrations.md


离线恢复工具(tools/dn42-recover/

dn42_recover.py —— 离线 WireGuard 私钥 escrow 恢复 CLI(只依赖 dn42_common.crypto永不在控制面运行):

子命令作用
keygen生成 RSA 恢复密钥对(私钥用口令加密 → recovery-private.pem,公钥 → recovery-public.pem
recover用恢复私钥解封 wg_interfaces / 上报里的 escrow 密文,还原节点 WG 私钥;可 --expect-public 校验

完整 escrow 模型与恢复演练见 ../guides/secret-recovery.md