7519309a27
web-platform-ci / Standalone decision service (no cloud credentials) (push) Has been cancelled
web-platform-ci / TypeScript, lint, unit, build (push) Has been cancelled
web-platform-ci / Playwright E2E (push) Has been cancelled
lekiwi-compatibility / cpu-compatibility (push) Has been cancelled
64 lines
8.1 KiB
Markdown
64 lines
8.1 KiB
Markdown
# CADWorld Sim 网站部署与运维
|
||
|
||
目标:`https://cadworld-sim.robotquan.com`,主机 `root@47.93.31.109`。复用 1Panel 的 OpenResty,独立容器仅发布 `127.0.0.1:8768`。不安装训练/GPU 服务,不改系统 Python,不需要访问者开启终端。
|
||
|
||
当前在线版本:`20260924T081555Z-language-render-fix`,上一版本 `20260924T065821Z-language-v2`。前后端共同发布,修复预设阴影/高 DPI 渲染与 Jev 技能上下文;公网非付费检查 6/6 及硬件双相机验证通过。详见 [修复与发布记录](website-language-render-fix.md),其中包含保留 iframe 热更新的回滚步骤。
|
||
|
||
iframe 嵌入已通过响应头热更新放行 `https://cadworld.robotquan.com`,未重启应用;接入示例、验证范围与配置回滚见 [嵌入说明](website-embedding.md)。重新发布旧包会覆盖该热更新,应从当前源码重新构建。
|
||
|
||
## 产品边界
|
||
|
||
- 网站公开、无需注册或前端输入密钥。按用户新要求固定服务器 DeepSeek `deepseek-flash` 和 OpenRouter Jev `typesafe/jev-1.13`,费用由站长承担;前端移除模型/密钥/订阅配置。
|
||
- 官方订阅仍在旧本地/非共享服务实现中保留,但当前共享密钥网站关闭其接口(403),没有绕过官方网络限制或登录其他账号。
|
||
- 仅两项授权密钥存于 `/opt/cadworld-sim/secrets/model-keys.env`(0400、UID 10001,父目录 root/0700),只读挂载;不进入镜像、静态目录、发布包、浏览器或日志。每访客任务状态独立,空闲 30 分钟/最长 8 小时销毁;删除会话不会删除站长的服务器配置。
|
||
- ZIP/URDF/地图/物理仿真仍在浏览器。网站隐藏训练、调参和外控,浏览器 Python/ONNX 保留;本地训练/外控功能保留,语言面板与网站统一精简并使用同源模型后端。
|
||
- 容量:128 会话、每会话 1 推理/120 次每小时,生产全站并行上限 2。共享 SQLite 限额为每小时 60、滚动 24h 600 次上游调用;`/command` 保守预留 2 次,失败不退款,不自动重试。预算在 `/opt/cadworld-sim/budget`,不得在重启/发布时删除。匿名访问仍可能耗尽公共额度;建议供应商侧另设金额上限,当前不是货币金额硬预算。
|
||
- 备案已由站长确认;正式备案/公安备案展示号码由站长提供,不能编造。TLS 证书首次无邮箱注册,自动续期由 systemd timer 负责,建议管理员另行设置证书到期监控。
|
||
|
||
## 本地开发与测试
|
||
|
||
```bash
|
||
source .venv/bin/activate
|
||
python -m decision_server --website-origin http://127.0.0.1:5173 --website-dev
|
||
# 开发者另一个终端(仅开发时):
|
||
npm run dev:website -- --host 127.0.0.1
|
||
npm run build:website # 输出 website-dist/,不含 tuning.html
|
||
npm run test:e2e:website # 真实后端 + 假 HTTP 上游,无 API 额度消耗
|
||
```
|
||
|
||
上述不带密钥启动方式只调试界面,执行会提示服务未就绪;真实开发需另显式提供 `--website-key-file` 和 `--website-budget-file`,不自动读取仓库 `.env`。生产必须 HTTPS,不能携带 `--website-dev`。前后端 Origin 要一致;本机 Bearer 模式仍使用 `npm run decision-server`,两种模式不自动回退。契约见 [website-api.md](website-api.md)。
|
||
|
||
## 构建、传输与首次发布
|
||
|
||
1. 保留工作树变更基线,运行 TS/Vitest/Python/Playwright 门禁。`build/website-deployment/baseline/` 保存部署前工作树摘要,不包含 `.env`。
|
||
2. 使用 [官方 crane v0.20.6](https://github.com/google/go-containerregistry/releases/tag/v0.20.6),校验 release checksums。将 `deploy/cadworld/images.lock.json` 中的 linux/amd64 digest 拉为 docker archive(`crane pull --platform linux/amd64 REF FILE`);不需要本机 Docker daemon,也不让服务器访问不明镜像源。
|
||
3. 为 Python 3.12 下载 `requirements.lock.txt` 指定 wheels,验证 hash;从官方 npm `@openai/codex@0.147.0-linux-x64` 包验证 registry 的 integrity 后提取二进制。文件放在 `build/website-deployment/vendor/{wheels/,codex,codex-origin.json}`。不复制任何用户的 Codex HOME/auth.json。
|
||
4. `npm run build:website` 后运行 `python3 deploy/cadworld/prepare_bundle.py RELEASE`。脚本只打包显式白名单(含 v1 schema、v2 task schema、v2 scene 三个运行契约)、生成每文件 SHA-256,并输出归档 SHA-256;不打包 `.env/.git/.venv/build/`、测试代码或未批准的个人模型。明确内置的 `web_platform/assets/robots/lekiwi-v1.zip` 随 Vite 生成带哈希的静态资产,必须连同整个 `website-dist/` 发布;`/?robot=lekiwi-v1` 即可自动导入,详见 [内置机器人](builtin-robots.md)。
|
||
5. SSH 传送归档、两份镜像 tar 和 SHA-256 到 `/opt/cadworld-sim/uploads/`。核验 SHA-256 后解压到 `/opt/cadworld-sim/releases/RELEASE/`,运行该版本的 `deploy/import-images.sh IMAGE_DIRECTORY`。先确认 `172.30.88.0/24` 不与现有 Docker 网络冲突。
|
||
6. 首次运行 `bash /opt/cadworld-sim/releases/RELEASE/deploy/bootstrap-tls.sh`:备份原 OpenResty conf,创建独立 HTTP challenge vhost,签发域名证书,安装 HTTPS vhost 与续期 timer。不修改默认站点/1Panel账号;已存在同域名配置则拒绝覆盖。
|
||
7. 站长明确授权后,在本机执行 `source .venv/bin/activate && python deploy/cadworld/install-model-keys.py --confirm-server-keys`,只选取项目 `.env` 的两项固定变量,经 SSH 标准输入传输;同时扫描静态资源避免密钥泄漏。不会把整个 `.env` 复制过去;若既有值不同则拒绝覆盖,轮换须单独授权。该步骤在共享模式首次发布前完成。
|
||
8. `bash /opt/cadworld-sim/releases/RELEASE/deploy/publish.sh RELEASE`:逐文件验 hash、离线构建、无网络/无密钥挂载的运行时导入与 v2 契约预检、启动最小权限容器、健康检查、原子切换静态目录、`nginx -t`、reload。失败恢复上一版本,首次失败撤下新 vhost 恢复默认站点。成功生成 `current.txt/previous.txt/image-id.txt`。
|
||
|
||
只有 80/443 提供网站流量;8768 绑定回环。不挂载 Docker socket、SSH key、宿主 HOME;应用非 root、只读根、tmpfs、2 GiB/2 CPU/128 PID 限制。证书在已有 OpenResty www 挂载下独立 `ssl/`,不在站点 document root 内。
|
||
|
||
## 日常操作(目标主机)
|
||
|
||
```bash
|
||
cd /opt/cadworld-sim
|
||
docker compose --env-file release.env -f compose.yaml ps
|
||
docker compose --env-file release.env -f compose.yaml logs --tail 100 decision
|
||
curl -H 'Host: cadworld-sim.robotquan.com' http://127.0.0.1:8768/healthz
|
||
systemctl status cadworld-certificate.timer
|
||
bash /opt/cadworld-sim/renew-certificate.sh --dry-run
|
||
# 后台更新/重启会销毁访客会话;固定密钥和共享限额保留,用户再次执行建立新会话:
|
||
docker compose --env-file release.env -f compose.yaml restart decision
|
||
# 回滚:取 previous.txt 中的具体版本,调用那个版本的发布脚本,前后端一起恢复。
|
||
bash /opt/cadworld-sim/releases/PREVIOUS/deploy/publish.sh PREVIOUS
|
||
```
|
||
|
||
不要在 1Panel 重复创建同域名覆盖手工 vhost,不要将开发服务器或后台端口直接暴露公网。不要打印完整 Cookie、请求体、API_KEY、设备码或 OAuth 链接来排障。应用和 API 访问日志不记录请求正文;会话异常仅返回稳定错误码。
|
||
|
||
## 验证边界
|
||
|
||
普通测试使用假上游,不证明云端模型可用;mock 抓放使用真实 WASM 物理,不证明真实 LLM/Jev 被调用。真实 API 测试必须经站长授权并限定调用数:`CADWORLD_LIVE_API=1 npx playwright test -c web_platform/playwright.website-live.config.ts --output build/website-deployment/本次目录`。当前 v2 用例最多 4 次 DeepSeek/16 次 Jev,不读取浏览器凭据、不自动重试,任何安全停止如实记录。`HEAD` 的 200/401/403/404 只作为连通性证据,不是 POST 推理成功。生产证书、重启/回滚及浏览器验证结果另见实际发布记录。当前发布、调用费用和未完成的抓放复验见 [服务器内置与语言控制记录](website-hosted-control-2026-09-24.md)。回滚到此前 BYOK 版本会恢复旧界面;受保护密钥文件和预算目录保留但旧服务不使用它们。
|