14 KiB
UniDesk Deployment Reference
主 server 使用根目录 docker-compose.yml 统一编排 database、backend-core、frontend、provider-gateway 以及必须留在主 server 的用户服务。当前环境本身就是主 server,因此 provider-gateway 也在同一台机器上启动,用与普通计算节点相同的 WebSocket 方式接入 core。Code Queue 不再属于主 server Compose,也不再由 backend-core 通过 provider-gateway 直连业务容器;它作为 v3sctl-managed 用户服务经 D601 v3sctl-adapter 进入 v3s 标准服务路由。
Services
database使用postgres:16-alpine,数据保存到 named volumeunidesk_pgdata_10gb,初始化 SQL 位于src/components/database/init/。backend-core是无状态核心服务,提供 Docker 内网 REST API、provider ingress WebSocket、任务调度入口和数据库访问层。frontend是唯一公开 Web 控制台,提供登录、从 TSX 转译出的 React 应用资产和到 backend-core 的同源代理。provider-gateway是当前主 server 的本机计算节点代理,通过 WebSocket 主动连到 provider ingress,挂载/var/run/docker.sock作为自动任务执行主路径,使用pid: "host"读取节点级进程资源,并周期性上报系统资源指标、进程占用与 Docker daemon 状态;计算节点 provider-gateway 还必须把 egress proxy 仅发布到宿主 loopback127.0.0.1:18789供本节点执行环境出网,维护用 Host SSH / WSL SSH 私钥目录只读挂载到/run/host-ssh,不得作为自动任务调度主路径。todo-note是主 server 承载的 Todo Note 纯后端用户服务,容器名todo-note-backend,只在 Compose 内网暴露4211/tcp,使用主 PostgreSQL 存储迁移后的 Todo Note 数据。v3sctl-adapter是 D601 上由 UniDesk 直管的 v3s 控制面适配微服务,容器名v3sctl-adapter,只绑定127.0.0.1:4266,由 UniDesk frontend/backend-core 通过用户服务代理访问并提供/api/control-plane可见性。code-queue是由 D601 v3s/k8s 控制面代管的 Codex/OpenCode 队列用户服务,active/standby 实例通过src/components/microservices/v3sctl-adapter/v3s/code-queue.v3s.json声明,运行对象通过code-queue.k8s.yaml创建 Kubernetes Deployment/ClusterIP Service;任务、queue、未读状态、控制状态和通知 outbox 一律写入主 PostgreSQL,不保留本地状态文件 fallback,浏览器只能通过 UniDesk frontend -> backend-core -> v3sctl-adapter -> Kubernetes API service proxy -> Code Queue Service 查看运行输出、追加 prompt、打断和重试。project-manager是主 server 承载的项目管理用户服务,容器名project-manager-backend,仅在 Compose 内网暴露4233/tcp,项目清单写入主 PostgreSQL,浏览器只能通过 UniDesk frontend 同源代理执行增删改查、Excel 导入和 Excel 导出。baidu-netdisk是主 server 承载的百度网盘存储用户服务,容器名baidu-netdisk-backend,仅在 Compose 内网暴露4244/tcp,OAuth/token/transfer 状态写入主 PostgreSQL,浏览器只能通过 UniDesk frontend 同源代理执行设备码登录、文件浏览和 staging 传输任务控制。
Public Exposure Boundary
Docker Compose 对最终用户只公开 frontend host port 和 provider ingress host port。backend-core REST API 仍不得映射公网;PostgreSQL 和 OA Event Flow 只允许为了受控 Code Queue active/standby 节点使用受限端口映射,并必须由 network.restrictedHostAccess.allowedSourceCidrs 生成的 DOCKER-USER 规则限制到 D601 出口地址,不能作为浏览器或任意公网客户端入口。浏览器访问 core API 必须通过 frontend 的同源代理完成。
计算节点上的用户服务后端也遵守同一边界:业务容器端口只绑定节点本机地址,并由 provider-gateway 主动连出 WebSocket 后接受 backend-core 的 microservice.http 调度访问。主 server 不为用户服务新增面向浏览器的公网反向代理端口;最终用户只通过 UniDesk frontend 的 React 页面访问结构化业务控件。
Docker Compose Runtime
CLI 会优先使用 docker compose v2 plugin;当 v2 plugin 不存在时才回退到 docker-compose v1。主 server 可以通过安装系统包形式的 Compose v2 plugin 完成无中断升级,因为该动作只增加 Docker CLI plugin,不要求重启 Docker daemon,也不要求重建或停止已有容器。若 Compose v2 build 提示 buildx plugin 不存在,应同样以系统包安装 buildx CLI plugin,而不是重启 Docker 或重建业务容器。
Compose v2 安装后仍然必须遵守 UniDesk 的服务控制入口:全栈生命周期用 server start / server stop,单服务重建用 server rebuild <service>。不要因为 v2 可用就直接在生产栈上手工执行未纳入 CLI 的 up --build、down -v 或跨项目清理命令;所有会影响容器的动作都应保持 job 可观测、Compose project 固定、database named volume 保留。
Start And Stop
bun scripts/cli.ts server start 与 bun scripts/cli.ts server stop 都是异步 job。启动 job 只执行固定 Compose project 的 up -d --build --remove-orphans,不得先 down,避免在 provider-gateway 旧容器或网络冲突时把长驻控制面容器先删掉又启动失败;停止 job 才允许执行 down --remove-orphans。启动和停止流程都禁止删除 Docker named volume。所有会改变主 server Compose 状态的 job 必须通过 .state/locks/server-compose.lock 串行化;连续 server rebuild 命令只代表连续创建异步 job,不能代表第一个 job 已结束,实际容器变更仍必须由 Compose lock 串行执行。
Single Service Rebuild
前端、backend-core、本机 provider-gateway 或主 server 承载的 Todo Note/Project Manager/Baidu Netdisk/OA Event Flow 用户服务需要重建时,统一使用 bun scripts/cli.ts server rebuild <service>,其中 <service> 只能是 backend-core、frontend、provider-gateway、todo-note、project-manager、baidu-netdisk 或 oa-event-flow。Code Queue、File Browser、FindJob、Pipeline、MET Nonlinear 和 ClaudeQQ 部署在计算节点,不属于主 server Compose 可重建服务。该命令先执行目标服务镜像构建,构建成功后才通过 up -d --no-deps --force-recreate <service> 替换目标容器,避免构建失败导致运行中的服务被提前停掉。
frontend 改动必须明确上线到公网:修改 src/components/frontend/src/、src/components/frontend/public/style.css、frontend 使用的共享 TSX/TS 模块或 WebUI 导航后,必须在同一变更集中执行 bun scripts/cli.ts server rebuild frontend,并等待 job 成功。公网 WebUI 的 /app.js 是 unidesk-frontend 容器启动时从镜像内源码转译生成的运行时 bundle;只改工作区文件、只跑 bun run check、只跑 Bun.build 或只刷新浏览器都不会替换已经运行的容器。
frontend 的 Docker 上线顺序为:先运行必要的本地校验,例如 bun scripts/cli.ts check 或对应的 frontend bundle 检查;然后运行 bun scripts/cli.ts server rebuild frontend;再用 bun scripts/cli.ts job status latest 或具体 job id 确认 status=succeeded;最后用 bun scripts/cli.ts server status、curl -fsS http://74.48.78.17:18081/health 和真实页面/深链接验证。若必须手工复现 CLI 行为,等价 Docker Compose 命令只能是固定 project/env 的 docker compose --env-file .state/docker-compose.env -f docker-compose.yml -p unidesk build frontend,随后 docker compose --env-file .state/docker-compose.env -f docker-compose.yml -p unidesk up -d --no-deps --force-recreate frontend;手工路径仍必须做同样的健康检查,不得改用全栈 up --build、down、删除容器或重启 database/backend-core 来“顺手上线”前端。
单服务重建必须由 CLI 解析出的 Compose 命令执行,且只能影响目标 service,不得连带重启 database、backend-core 或其他未指定服务。重建后 job 必须按 Docker Compose label 校验目标容器:com.docker.compose.project 等于 config.json 中的固定 project name,com.docker.compose.service 等于目标服务名,并等待容器进入 healthy;没有 healthcheck 的服务至少要进入 running。如果验证失败,job 必须失败并输出目标容器状态,禁止把“无输出”或“只完成 build”当作成功。
紧急灾备或数据迁移期间如需手工启动单个 Compose service,也必须保持与 CLI 相同的隔离语义:使用固定 --env-file .state/docker-compose.env 和 up -d --no-deps <service>,只启动目标容器;如果需要刷新 backend-core 的服务目录或环境变量,应把 backend-core 作为显式目标单独重建/替换,不能依赖 up 的依赖解析顺手重建 database、backend-core 或其他服务。
正式流程不得依赖人工 docker rm 兜底;手工删除旧容器后若 job、Docker client 或 daemon 在 up 前中断,会直接造成用户服务代理失败。server rebuild <service> 必须是可观测 job:build-first、Compose lock、no-deps force-recreate、post-up validation、保留 named volume。Code Queue 等计算节点长任务服务即使被重建也必须依赖服务自身 restart-recovery 恢复任务,不能用“避免重建”掩盖恢复缺陷。
User Service Restart Recovery
主 server 用户服务和计算节点用户服务都必须按 docs/reference/microservices.md 的 Runtime Configuration And Persistence Contract 设计。主 server Compose 内的用户服务通过固定 project、restart policy、健康检查、PostgreSQL 权威状态和 server rebuild <service> 的 no-deps force-recreate 恢复;计算节点用户服务通过节点本地 Compose/docker run、host bind mount 或业务数据库持久化、Docker restart policy、节点级 autorecover 脚本和 provider-gateway 私有代理验收恢复。上线说明不能只写“容器已启动”,必须说明 Docker daemon 或机器重启后如何重新进入 ready 状态,以及哪些路径/表保存了登录、队列、token、订阅和业务数据。
Health Criteria
服务跑通的最低标准是:backend-core 内网 /health 返回 ok,frontend 公网 /health 返回 ok,provider ingress 公网 /health 返回 ok,database 在容器内 pg_isready 可用,Todo Note 后端 /api/health 返回 storage=postgres,v3sctl-adapter /api/control-plane 可见 Kubernetes API service proxy 状态、D601 active serving healthy、D518 expected/missing 节点状态和 no-fallback 路径,Code Queue /health 经 v3s active Service 返回轻量 readiness、默认模型和 queue.storage,/api/tasks/overview 返回 PostgreSQL 队列总览,Project Manager /health 返回 storage.primary=postgres 和项目数量,backend-core /api/performance 返回性能指标,/api/nodes 中出现 main-server 和 D601 provider 且状态为 online,/api/nodes/system-status 中出现对应 CPU/内存/硬盘采样,/api/nodes/docker-status 中能看到主 server 与 D601 Docker 快照。D518 未接入前不得用 D601->D518 直连或 NodePort 绕过,也不得把 D518 missing 当作 D601 active Service 不可用;接入验收应证明 D518 通过 k3s/k8s 原生 agent/proxy/tunnel 或显式 provider 维护代理进入控制面。交付前还必须运行 bun scripts/cli.ts e2e run,并以 docs/reference/e2e.md 的门禁作为最终判定。
Code Queue D601 Resource Budget
Code Queue 已从主 server 迁移到 D601 v3s/k8s,但仍必须保持明确的 memory/swap 硬上限,默认 CODE_QUEUE_MAX_ACTIVE_QUEUES=0 以恢复 queue 间并行,仍保持 CODE_QUEUE_IN_MEMORY_OUTPUT_RECORDS=10、CODE_QUEUE_IN_MEMORY_EVENT_RECORDS=10 这类小热窗口;任务历史、队列统计和 Trace/output 读取必须优先从 PostgreSQL 直读或聚合,/health 只做轻量 readiness,不能为了性能便利在 Bun 进程内缓存全量历史。任何提高 Code Queue 热窗口、日志缓冲、Playwright/Codex 子进程常驻规模或容器上限的变更,或把 CODE_QUEUE_MAX_ACTIVE_QUEUES 显式改成正数,都必须在同一任务里说明 D601 资源预算来源,并通过 D601 kubectl -n unidesk get deploy,svc,pod、kubectl -n unidesk top pod 或等价 Docker stats、microservice health code-queue 和对应 E2E 证明未重新引入内存爆炸风险。
Database Connection Budget
主 PostgreSQL 的内存预算按“少量长驻服务连接池 + 短查询按需连接”设计,不允许每个 Bun 服务沿用默认 8 到 10 个连接。backend-core 默认 DATABASE_POOL_MAX=4,主 server 上的 oa-event-flow、baidu-netdisk 默认 DATABASE_POOL_MAX=2,project-manager 默认 DATABASE_POOL_MAX=1,D601 code-queue 默认 CODE_QUEUE_DATABASE_POOL_MAX=2;如需提高任一连接池上限,必须同时说明并发 SQL 需求、验证 pg_stat_activity 中该服务没有长期 idle 堆积,并确认 max_connections 仍有足够余量。PostgreSQL 基础配置固定保守值:shared_buffers=128MB、work_mem=4MB、maintenance_work_mem=64MB、max_connections=50,避免主 server 低内存环境被空闲 backend 和过大的 per-query 内存预算挤占。
排查 PostgreSQL 内存时以 docker stats unidesk-database、pg_stat_activity 分组和 pg_settings 为准;主机 ps 中每个 postgres 进程的 RSS 会重复计入共享内存,不能把所有 backend RSS 简单相加当作真实容器占用。所有 UniDesk PostgreSQL 客户端都必须设置可识别的 application_name,便于按服务统计连接数、状态和慢查询归属。
Database Volume
架构要求数据库使用 10 GB named volume;当前实现将 volume 命名为 unidesk_pgdata_10gb 以固定生命周期。Docker named volume 默认不强制容量上限;如需硬配额,应在主机存储层或 Docker volume driver 层配置。CLI server 控制只能使用不删除 volume 的 down / up 流程,禁止使用 down -v、docker volume rm 或删除 unidesk_pgdata_10gb。
/api/overview 会返回 pgdata 字段,frontend 态势总览 / 核心指标 必须展示当前 PostgreSQL 数据库占用、命名卷名称和配置容量。Docker 状态页中 unidesk_pgdata_10gb 的命名卷检测只对 main-server Provider 生效,其他计算节点不需要也不应被要求存在该数据库卷。