@@ -10,7 +10,7 @@ Provider Gateway 是计算节点侧容器。它只主动连出到主 server 暴
当前主 server 公网 IP 是 `74.48.78.17` , `config.json` 中的 `network.publicHost` 必须保持为该地址;公网 frontend 入口是 `http://74.48.78.17:18081/` , provider gateway 对外接入入口是 `ws://74.48.78.17:18082/ws/provider` , provider ingress 健康检查是 `http://74.48.78.17:18082/health` 。主 server 本机 provider 由根目录 `docker-compose.yml` 的 `provider-gateway` 服务启动,容器内使用 Docker 内网地址 `ws://backend-core:8081/ws/provider` 自接入;外部计算节点部署 provider-gateway 时必须改用公网 provider ingress URL,并复用 `config.json` / `.state/docker-compose.env` 中的 provider token、心跳间隔和重连参数。
计算节点部署 provider-gateway 的最小方法是:准备可运行 `unidesk_provider-gateway` 镜像的 Docker 环境,为节点分配唯一 `PROVIDER_ID` 与可读 `PROVIDER_NAME` ,设置 `PROVIDER_SERVER_URL=ws://74.48.78.17:18082/ws/provider` 、`PROVIDER_TOKEN` 、`PROVIDER_LABELS_JSON` 、`HEARTBEAT_INTERVAL_MS` 、`RECONNECT_BASE_MS` 和 `RECONNECT_MAX_MS` ,并挂载 `/var/run/docker.sock:/var/run/docker.sock` 作为 Docker 状态采集、任务执行和远程升级的唯一自动化通道。需要支持 `provider.upgrade` 的 节点还 必须设 置 `PROVIDER_UPGRADE_*` 环境变量,把节点上的 UniDesk 仓库只读挂载到 `PROVIDER_UPGRADE_WORKSPACE_PATH` ,并确保升级命令只重建 `provider-gateway` service,不影响 database、backend-core、frontend。需要维护桥连接 WSL 的节点必须额外设置 `HOST_SSH_HOST=host.docker.internal` 、`HOST_SSH_PORT=22` 、`HOST_SSH_USER=<WSL 用户>` 、`HOST_SSH_KEY=/run/host-ssh/id_ed25519` 、`HOST_REMOTE_CWD=/home/<WSL 用户>` ,并把只含维护私钥的宿主目录只读挂载到 `/run/host-ssh` 。
计算节点部署 provider-gateway 的最小方法是:准备可运行 `unidesk_provider-gateway` 镜像的 Docker 环境,为节点分配唯一 `PROVIDER_ID` 与可读 `PROVIDER_NAME` ,设置 `PROVIDER_SERVER_URL=ws://74.48.78.17:18082/ws/provider` 、`PROVIDER_TOKEN` 、`PROVIDER_LABELS_JSON` 、`HEARTBEAT_INTERVAL_MS` 、`RECONNECT_BASE_MS` 和 `RECONNECT_MAX_MS` ,并挂载 `/var/run/docker.sock:/var/run/docker.sock` 作为 Docker 状态采集、任务执行和远程升级的唯一自动化通道。所有长期接入 节点都 必须配 置 `PROVIDER_UPGRADE_*` 环境变量,把节点上的 UniDesk 仓库只读挂载到 `PROVIDER_UPGRADE_WORKSPACE_PATH` ,并确保升级命令只重建 `provider-gateway` service,不影响 database、backend-core、frontend。需要维护桥连接 WSL 的节点必须额外设置 `HOST_SSH_HOST=host.docker.internal` 、`HOST_SSH_PORT=22` 、`HOST_SSH_USER=<WSL 用户>` 、`HOST_SSH_KEY=/run/host-ssh/id_ed25519` 、`HOST_REMOTE_CWD=/home/<WSL 用户>` ,并把只含维护私钥的宿主目录只读挂载到 `/run/host-ssh` 。
## WSL Compute Node Deployment
@@ -20,7 +20,7 @@ WSL 节点应优先使用 WSL 内部原生 Docker Engine 和 `/var/run/docker.so
WSL provider 的最小环境文件应放在节点本地私有路径,例如 `/home/ubuntu/unidesk/.state/provider-<ID>.env` ,并由 `docker run --env-file` 读取。`PROVIDER_LABELS_JSON` 在 Docker env-file 中可以写成单行 JSON;如果临时用 shell `source` 方式调试,必须对整段 JSON 加引号,否则 shell 会按 `{}` 和逗号拆分导致 JSON 解析失败。WSL 节点建议至少包含这些 labels:`host` 、`role=wsl-provider` 、`wsl=true` 、`distro` 、`docker=true` ;运行时 provider-gateway 会自动追加 `runtime` 、`dockerSocketPresent` 和 `gatewayUptimeSeconds` 。`.state/provider-<ID>.env` 、`logs/provider-<ID>/` 和容器日志属于节点本地运行态,必须保持在 `.gitignore` 覆盖范围内,不能提交 provider token、登录态或运行日志。
长期运行推荐用 systemd 管理 provider-gateway 容器,而不是只在交互 shell 中运行 Bun 进程。systemd unit 的稳定形态是:`ExecStartPre=-docker rm -f unidesk-provider-gateway-<ID>` 清理同名旧容器,`ExecStart=docker run --name unidesk-provider-gateway-<ID> --env-file ... -v /var/run/docker.sock:/var/run/docker.sock -v /home/ubuntu/unidesk:/workspace:ro -v /home/ubuntu/unidesk/logs/provider-<ID>:/var/log/unidesk unidesk_provider-gateway:<id>` , `ExecStop=docker stop unidesk-provider-gateway-<ID>` ,并设置 `Restart=always` 。临时部署可以直接使用 `docker run -d --restart unless-stopped` ,但仍要保证容器名、env 文件、日志目录和镜像 tag 都带上节点 ID,便于 frontend、Docker 状态和本地排障互相对应。临时或一次性外部 WSL 节点如果没有完整远程升级回滚方案,应设置 `PROVIDER_UPGRADE_ENABLED=false` ,保留 `provider.upgrade` 预检能力但禁止远端 schedule 自升级 。
长期运行推荐用 systemd 管理 provider-gateway 容器,而不是只在交互 shell 中运行 Bun 进程。systemd unit 的稳定形态是:`ExecStartPre=-docker rm -f unidesk-provider-gateway-<ID>` 清理同名旧容器,`ExecStart=docker run --name unidesk-provider-gateway-<ID> --env-file ... -v /var/run/docker.sock:/var/run/docker.sock -v /home/ubuntu/unidesk:/workspace:ro -v /home/ubuntu/unidesk/logs/provider-<ID>:/var/log/unidesk unidesk_provider-gateway:<id>` , `ExecStop=docker stop unidesk-provider-gateway-<ID>` ,并设置 `Restart=always` 。临时部署可以直接使用 `docker run -d --restart unless-stopped` ,但仍要保证容器名、env 文件、日志目录和镜像 tag 都带上节点 ID,便于 frontend、Docker 状态和本地排障互相对应。`provider.upgrade` 是长期接入节点的必备能力,provider-gateway 不提供 `PROVIDER_UPGRADE_ENABLED` 或等价禁用开关;如果节点缺少升级环境变量,必须修正节点部署,而不是在服务端接受只能预检不能升级的半成品状态 。
WSL 本身会在没有前台进程时被 Windows 回收;如果该节点要作为长期在线算力,必须通过 Windows 启动项、计划任务或后台 `wsl.exe -d <distro> -u root -- bash -lc "systemctl start docker unidesk-provider-gateway-<ID>.service; exec sleep infinity"` 这类 keepalive 进程保持发行版运行。仅启用 WSL 内 systemd service 不等价于 Windows 层面的常驻守护。
@@ -58,6 +58,10 @@ provider-gateway 连接成功后必须周期性上报节点 CPU、内存和硬
backend-core 可以通过真实 WebSocket 调度向在线 provider 下发 `provider.upgrade` 。`mode: "plan"` 只返回升级计划,用于 E2E 和人工预检;`mode: "schedule"` 会要求 provider-gateway 通过本地 Docker socket 启动一个 detached updater 容器,由 updater 在节点本地执行 `docker compose up -d --no-deps --build provider-gateway` 。`--no-deps` 是强制要求,升级 provider-gateway 时不得重建或停止 database、backend-core、frontend。升级执行路径使用 Docker socket 和只读仓库挂载,不使用 Host SSH 维护桥作为自动调度通道。
远程升级策略固定为 always-enabled:只要 provider-gateway 在线并声明 `provider.upgrade` , `mode: "schedule"` 就必须真正调度升级容器,不允许被 `PROVIDER_UPGRADE_ENABLED=false` 、前端隐藏按钮或服务端特殊名单禁用。升级能力的安全边界不是开关,而是显式 `PROVIDER_UPGRADE_*` 配置、Docker socket 权限、只读仓库挂载、固定 Compose service 和 `--no-deps` 约束。升级计划中必须展示 `policy: "always-enabled"` 、updater 容器名、runner image、workspace、Compose project/service、env file、compose file 和实际 `docker run` 命令,方便前端任务历史与 CLI debug 直接诊断。
旧版 provider-gateway 如果只能返回 plan 或因为旧环境中的 `PROVIDER_UPGRADE_ENABLED=false` 拒绝 schedule,需要先通过任意现有维护通道手动 bootstrap 一次。bootstrap 的目标不是长期流程,而是把节点更新到支持 always-enabled 远程升级和 Host SSH / WSL SSH 维护桥的版本;完成后必须立刻用 `bun scripts/cli.ts debug dispatch <PROVIDER_ID> provider.upgrade --mode schedule --wait-ms 15000` 做一次真实一键升级验证,再用 `bun scripts/cli.ts debug health` 或公网 frontend 确认该节点仍在线、`unideskCapabilities` 包含 `provider.upgrade` ,需要 SSH 维护的 WSL 节点还必须包含 `host.ssh` 。
## Host SSH Maintenance Bridge
宿主 SSH / WSL SSH 转发只作为应急维护辅助路径,不用于自动计算任务调度。实现参考 `../web-terminal` 的经验:容器内使用只读挂载的私钥,主动连接宿主或 WSL sshd,并设置 `BatchMode=yes` 、`StrictHostKeyChecking=accept-new` 、`ServerAliveInterval=20` 和 `ServerAliveCountMax=3` 。主 server Compose 会把 `config.json` 的 `sshForwarding.keyDir` 只读挂载为 `/run/host-ssh` , provider 标签会上报 `hostSshConfigured` 、`hostSshKeyPresent` 和 `hostSshTarget` ,便于在前端节点清单确认维护桥是否具备条件。