64 lines
11 KiB
Markdown
64 lines
11 KiB
Markdown
# Provider Gateway Reference
|
||
|
||
Provider Gateway 是计算节点侧容器。它只主动连出到主 server 暴露的 provider ingress WebSocket,不要求计算节点有公网 IP,适合 NAT、内网和防火墙后的机器。
|
||
|
||
## Main Server Self Provider
|
||
|
||
当前主 server 也运行一个 provider-gateway,`providerId` 固定来自 `config.json` 的 `providerGateway.id`。这让单机环境也能验证完整的分布式调度闭环:frontend 发起任务,core 写数据库并通过 provider ingress WebSocket 下发,provider gateway 执行后回传状态。
|
||
|
||
## Deployment Method
|
||
|
||
当前主 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 Compute Node Deployment
|
||
|
||
WSL 计算节点和普通外部计算节点使用同一套 provider ingress 协议:节点不暴露入站端口,只需要从 WSL 主动连出到 `ws://74.48.78.17:18082/ws/provider`。已验证的 WSL 节点命名口径是短设备编号,例如 `PROVIDER_ID=D601` 与 `PROVIDER_NAME=D601` 保持一致;新增 WSL 节点必须替换为唯一 ID,避免把旧节点状态覆盖到同一条 `unidesk_nodes.provider_id` 记录。
|
||
|
||
WSL 节点应优先使用 WSL 内部原生 Docker Engine 和 `/var/run/docker.sock`,让资源采样、Docker 状态和任务执行都反映 WSL 计算环境本身,而不是 Windows 或 Docker Desktop 的代理上下文。如果当前 Docker CLI 实际连接 Docker Desktop daemon,也可以先作为可用计算节点接入,但必须在 `PROVIDER_LABELS_JSON` 中显式标注 `dockerContext=docker-desktop` 或等价标签,避免在 frontend 中把 Docker Desktop 资源误判为 WSL 原生资源。UniDesk 仓库建议放在 WSL 原生文件系统,例如 `/home/ubuntu/unidesk`,再按需从 Windows 工作区同步源码;长期运行和升级挂载应使用 WSL 原生路径只读挂到容器内 `/workspace`,减少 `/mnt/c` 权限、性能和路径转换问题。
|
||
|
||
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 自升级。
|
||
|
||
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 层面的常驻守护。
|
||
|
||
## WSL Image Build Fallback
|
||
|
||
标准镜像构建路径使用 `src/components/provider-gateway/Dockerfile`,基础镜像为 `oven/bun:1-alpine`,并在镜像内安装 Bun、Docker CLI、Compose plugin、`df` 和 provider-gateway 源码。WSL 或 Docker Desktop 环境中的 registry mirror、DNS 或代理配置可能导致 `oven/bun` 元数据拉取失败;此时不要修改 provider ingress 协议或服务端配置,应改用节点侧镜像交付兜底。
|
||
|
||
可复用的兜底方式是使用本机已存在的 Debian/Node 基础镜像,复制 WSL 本地 `bun`、`docker` 和 `docker-compose` plugin 到镜像内,再复制 `src/components/provider-gateway/src` 与 `src/components/shared/src`。该镜像必须能在容器内执行 `bun --version`、`docker version`、`docker compose version`,并通过挂载的 `/var/run/docker.sock` 执行 `docker info` 和 `docker ps`;只有这些命令通过后才能作为 `unidesk_provider-gateway:<provider-id>` 运行。该兜底镜像只改变节点侧交付方式,不改变 `PROVIDER_SERVER_URL`、token、heartbeat、labels、Docker socket 挂载和服务端验收标准。
|
||
|
||
## Deployment Verification
|
||
|
||
provider-gateway 部署是否成功必须以 UniDesk frontend 中可见的 Provider 信息为准,不能只看节点容器 `running`。验证时访问 `http://74.48.78.17:18081/`,使用配置中的账号密码登录,进入 `资源节点 / 节点清单`,确认目标 `PROVIDER_ID`、`PROVIDER_NAME`、`online` 状态、`lastHeartbeat` 和 labels 可见;点击该节点的 `查看原始JSON`,确认 raw payload 中的 `providerId`、`name`、`status`、`labels` 与部署环境变量一致。随后进入 `资源节点 / 资源监控`,确认该 Provider 有 CPU、实际内存和硬盘采样曲线;进入 `资源节点 / Docker 状态`,确认 Docker daemon、containers、images、volumes、networks 已渲染出来,且 `dockerSocketPresent` 或 Docker ready 状态与预期一致。只有这些前端信息都能通过 UniDesk 正常读取,才说明 provider-gateway 已经真正挂载到主 server。
|
||
|
||
WSL 节点还应补充一次真实调度验证:向该 `PROVIDER_ID` 下发 `docker.ps`,任务必须从 `dispatched` 进入 `succeeded`,并在结果中看到 WSL Docker daemon 返回的容器列表;对于容器化运行的 provider-gateway,列表中通常应包含 `unidesk-provider-gateway-<PROVIDER_ID>`。这一步可以同时证明 provider WebSocket、服务端任务路由、节点侧 Docker socket 和结果回传链路都已贯通。
|
||
|
||
自动化验证必须使用 Playwright 访问公网 frontend,而不是在容器内直接调 core API 代替浏览器验收。标准命令是 `bun scripts/cli.ts e2e run`;该命令会让 Playwright 打开公网 `http://74.48.78.17:18081/`、登录、抓取页面中的 Provider 信息和 `查看原始JSON` 内容,并检查 Provider 自接入、资源指标、Docker 状态和 `provider.upgrade` 预检。外部新增节点的人工验收应复用同一套前端路径:先确认 Provider 信息出现在节点清单,再确认资源监控和 Docker 状态页面有该节点的数据,最后通过任务调度向该 Provider 下发 `echo` 或 `docker.ps` 并在任务历史中查看耗时、状态和失败原因。
|
||
|
||
## Provider Ingress
|
||
|
||
provider ingress 是唯一允许公网暴露的 provider 连接接口,当前由 backend-core 容器的独立端口提供 `/ws/provider` 和 `/health`。backend-core REST API 仍只在 Docker 内网开放,外部计算节点只应连接 provider ingress。
|
||
|
||
## Docker Socket Path
|
||
|
||
自动任务执行只允许走本地 Docker socket。Compose 将 `/var/run/docker.sock` 挂入 provider-gateway,provider 标签会报告 `dockerSocketPresent`,`docker.ps` 调试任务会通过该 socket 查询宿主 Docker 容器。
|
||
|
||
## Docker Status Telemetry
|
||
|
||
provider-gateway 连接成功后必须周期性上报 Docker daemon 状态,数据来源是本地 Docker socket 上的 `docker info`、`docker ps -a`、`docker images`、`docker volume ls` 和 `docker network ls`。backend-core 将最新快照保存到 `unidesk_node_docker_status`,frontend 的资源节点 `Docker 状态` 子标签用该快照渲染 Docker Desktop 风格视图;该能力仍然只通过 provider 主动上报,不要求主 server 反向连接计算节点。
|
||
|
||
## System Status Telemetry
|
||
|
||
provider-gateway 连接成功后必须周期性上报节点 CPU、内存和硬盘用量。采集来源是节点本地 `/proc/stat`、`/proc/loadavg`、`/proc/meminfo` 与 `df -PB1`,backend-core 将最新快照保存到 `unidesk_node_system_status`,并将历史采样保存到 `unidesk_node_metric_samples` 供 frontend 绘制任务管理器风格曲线。内存使用量采用实际占用口径:`MemTotal - MemFree - Buffers - Cached - SReclaimable + Shmem`,也就是不把 Linux page cache / buffer 计入占用;上报中同时保留 `cacheBytes` 便于排查。该链路仍然由 provider 主动上报,主 server 不反向探测计算节点。
|
||
|
||
## Remote Provider Upgrade
|
||
|
||
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 维护桥作为自动调度通道。
|
||
|
||
## Host SSH Maintenance Bridge
|
||
|
||
宿主 SSH 转发只作为应急维护辅助路径,不用于自动任务调度。实现参考 `../web-terminal` 的经验:容器内使用只读挂载的私钥,通过 `ssh -tt` 主动连接宿主 sshd,并设置 `StrictHostKeyChecking=accept-new`、`ServerAliveInterval` 和 `ServerAliveCountMax`。本仓库保留 `src/components/provider-gateway/scripts/host-ssh-shell.sh` 作为维护桥接脚本,默认 Compose 不挂载私钥,避免把 SSH 路径误用为调度通道。
|