Files
dsh_ai1net_server/dsh-server-docs/DEPLOY-本部署.md
T
admin e6207aa691
build / build-and-scan (push) Canceled after 0s
chore(仓库对齐): 文档库结构治理 + IM/插件线落地
文档库:目录改为编号制(01-规范/02-架构设计/03-数据库/04-调整方案/
05-交接单/06-ops/07-scripts/08-skills/09-archive),顶层散文件归入 01-规范/;
INDEX.md 与 docs-manifest.json 重刷(档案 146 篇);旧目录名引用全量对齐。

IM 线:src/im/**(SDK / hub / store / presence / ws / gateway-token)、
src/web/routes/im.ts、src/db/plugin-data/**、src/supervisor/plugin-assembly.ts
及对应 test/**。

插件线:poc/{im-agent-bridge,im-connection-gateway,im-conversation-tabs,
business-plugins-im,carbon-mcp-probe}、src/web/routes/{sessions,overlay-device}.ts、
src/net/relay/{device-grant,instance-credential}.ts。

仓库卫生:清出 40 个历史误入库 / 已改名文件(34 个交接单归档 + 6 个旧结构,
本地均有副本);dsh-server-docs/.gitignore 补 tmp/;交接单不入库(政策)。
2026-09-24 07:25:16 +08:00

8.1 KiB
Raw Blame History

DEPLOY — 本部署说明(ai1net.com / 47.77.182.89)

用途:把"这套平台到底部署在哪、装了什么、改了什么、怎么重建/回滚"一次讲清,供后来人(或未来的我)在不翻聊天记录的前提下独立重建。 对应档案 19 §C9。最后更新:2026-09-19(域名迁至 ai1net.com)

1. 拓扑与入口

项 值
服务器 47.77.182.89(Alibaba Cloud Linux 8,阿里云)|SSH 别名 bt-server(端口 32022,root)
门户 / 用户实例 https://ai1net.com / https://<用户名>.ai1net.com(Cloudflare 代理,回源走 443)
旧域名 alotbuy.com(含 *.alotbuy.com、*.dsh.alotbuy.com)→ 301 到新域(2026-09-19 降为过渡装置;relay-direct.alotbuy.com 与 /dshs-relay、/dshs-overlay/bootstrap 仍透传)
面板 宝塔(站点 vhost 由面板管理,注意:面板保存会重写 vhost,可能抹掉自定义指令)
编排器 自研 dshs(Node/TS),systemd 单元 dshs,监听 127.0.0.1:3080
被托管运行时 官方 @deepseek-ai/[email protected](全局安装于 /usr/local/lib/node_modules/@deepseek-ai/dsh,平台不改其任何文件)

2. 目录布局

路径 说明
/opt/dshs 编排器源码仓库(也是 git 仓库;lib/ 被 .gitignore 忽略 → 需构建)
/var/lib/dshs dataRoot:dshs.db(better-sqlite3)、users/<uuid>/
<userRoot> = dataRoot/users/<uuid> 每用户根:ws/(工作区,用户可见)、home/(DSH_HOME:profiles/sessions/storages/skills)、tmp/(1777)
<profile> = <userRoot>/home/profiles/web 该用户的 dsh profile:node_modules/、cordis.patch.yml、package.json、pnpm-lock.yaml
/opt/dsh/ 运维脚本与备份:switch-domain-alotbuy.sh、backups/、artifacts/(插件 tgz)
/www/server/panel/vhost/nginx/ ai1net.com.conf(现役门户)、relay-direct.conf(443/TCP 兜底)、alotbuy.com.conf(2026-09-19 降级为 301)、dsh.alotbuy.com.conf(301)+ 多份 .bak-*
/etc/letsencrypt/live/{ai1net.com,alotbuy.com,dsh.alotbuy.com}/ 证书(新域 DNS-01 via /etc/cloudflare-ai1net.ini;旧域 via /etc/cloudflare.ini)

3. 环境变量(/etc/dshs.env)

键 作用
DSHS_PORT 编排器监听端口(127.0.0.1:3080)
DSHS_DATA_ROOT dataRoot
DSHS_BASE_DOMAIN 门户/用户子域基域(现 ai1net.com)
DSHS_COOKIE_DOMAIN 会话 Cookie 域(现 .ai1net.com;见档案 22 §五 取舍说明)
DSHS_SECURE_COOKIES Cookie Secure 标记(HTTPS 下应开)
DSHS_DSH_BIN 子实例可执行路径
DSHS_ISOLATION_MODE 隔离模式(本部署 account = bwrap + setpriv + systemd-run scope)
DSHS_BASE_UID 每用户 uid 起始值

其余可调项走代码默认值(崩溃退避/熔断、idle-reap、bundled skill dir 等),见 src/config.ts 的 DEFAULT_* 与档案 20。

4. 外部依赖(版本为实测)

依赖 版本 用途
Node 22.23.2 编排器与子实例运行时
pnpm 9.15.9 profile 侧插件安装账本(pnpm-lock.yaml)
bubblewrap 0.4.0 每用户沙箱(合成根 + 只读系统 + 私有 tmp + --unshare-pid)
systemd-run — per-instance scope(MemoryHigh=448M(基础/软限) + MemoryMax=1024M(上界/硬限),与插件开关无关(档案 96)/ CPUQuota=150% / TasksMax=128)
nginx 1.28.3(宝塔) 站点与反代(关键自定义指令见档案 21/22)
nftables — 出网护栏(/etc/nftables-dsh-egress.nft + dsh-egress.service)
sqlite3 CLI 3.26 仅运维排查(平台内置用 better-sqlite3)
certbot + dns-cloudflare — 证书签发/续期(必须 --dns-cloudflare-propagation-seconds 60,否则失败)

5. 平台侧脚本族

脚本 作用
ensure-role-profile-patch.cjs 角色化 profile patch(非 admin 隐藏模型分区/收归插件开关)
ensure-workspace-picker.cjs 目录选择器收敛全量/新用户铺开(写平台段 + 逐用户 pnpm add;幂等,--dry-run/--restart)
07-scripts/install-workspace-picker.sh <tgz> 逐用户安装插件包(自动判断是否加 pnpm -w)
/opt/dsh/switch-domain-alotbuy.sh 域名切换(改 env → drain → 重启)
/usr/local/bin/provision-new-users.sh 新用户供应(建 OS 账号 + chown + 调用 picker 铺开);由 dsh-provision.path 触发
07-scripts/ci.sh 类型检查 + 构建 + 单测(不含需要实例/凭据的 smoke)

6. 构建 / 部署 / 回滚(三步)

# 构建(改代码后必做;lib/ 不入库)
cd /opt/dshs && npm run build && bash 07-scripts/ci.sh

# 部署(drain: 停服务 → 清残留实例 → 起服务)
systemctl stop dshs; for p in $(ps -eo pid,user,args | awk '$2 ~ /^dsh-/ {print $1}'); do kill $p; done; systemctl start dshs

# 回滚(示例:回滚到某个提交)
git -C /opt/dshs revert <sha> && npm run build && systemctl restart dshs
  • 本机 ↔ 服务器同步:服务器无 Gitea 凭据 → 服务器 git bundle create → 本机 git fetch <bundle>(路径必须用 D:/…)→ merge --ff-only → push。
  • 文档库:bash 07-scripts/docs-sync-check.sh(双端 md5 对账,0 = 全绿);服务器侧 /opt/dsh/docs(root 600)。

6.4 数据库初始化(PostgreSQL 13 / 47 本机进程)

DB 不在容器里,是 47 上本机进程(127.0.0.1:15432)。重建时按序跑:

-- ① 建角色 + 库(初始形态)
CREATE ROLE dshs LOGIN PASSWORD '<见 config/platform.env>';
CREATE DATABASE dshs OWNER dshs ENCODING 'UTF8' TEMPLATE template0;

-- ② 🔴 插件建库权限(2026-09-23 · B 档拍板)
--    用途 = 平台侧为每个业务插件建独立库 `dshs_pl_<pluginId>`(一插件一库)
--    ⛔ 只给这一位;⛔ 不得顺手给 CREATEROLE / SUPERUSER
ALTER ROLE dshs CREATEDB;
  • 连接串只从 systemd drop-in dshs.service.d/cluster.conf 取(DSHS_DB_URL);⚠️ source /etc/dshs.env 会拿到错的 5432(见 CODEBUDDY.md §8)。
  • 回滚②:ALTER ROLE dshs NOCREATEDB; ⇒ 已建的 dshs_pl_* 库不受影响,仅新插件建不了库(平台建库口返回 PG_CREATEDB_MISSING / 503)。
  • ⚠️ 权限校验判据(重建后必跑):rolsuper=false · rolcreatedb=true · rolcreaterole=false —— 多点一位即为权限失控。
  • ⚠️ PG 没有"只允许 dshs_pl_* 前缀"的原生机制(实测建任意名库成功)⇒ 库名白名单只靠平台代码兜(点生成 + 正则复校 + 建库落台账)。详见 03-数据库/DB-03-插件数据面规范.md §六。
  • 迁移表由平台启动时 runPgMigrations 自动落(v15 起含台账表 plugin_datastores / plugin_data_audit)⇒ 部署新版本代码后随 systemctl restart dshs 自动补齐,⛔ 不需手工建表。

6.5 定时维护(cron,2026-09-11 建立)

任务 文件 时间
存储用量快照 /etc/cron.d/dsh-maintenance 每小时 :05
工作区清理 / 会话回收 / 回收站清空 同上 04:10 / 04:20 / 04:40
平台全量备份 + 旧备份清理 /etc/cron.d/dsh-backup 每周日 05:00 / 05:30

备份脚本 /opt/dsh/backup.sh(入库副本 07-scripts/backup-platform.sh)含 SQLite 一致性快照;恢复步骤见档案 02 附录 C.8。

7. 默认红线(改任何东西前先读)

  1. 不自动升级 dsh;升级必须走"测试 → 评估 → 修复",并按档案 26 回归清单逐项验。
  2. 不改官方 dsh 主程序与缓存(/usr/local/lib/node_modules/@deepseek-ai/dsh/** 保持原封)。
  3. client bundle 禁 exports.default(R3)。
  4. 未明确要求不擅自 commit/push(服务器 git 与文档 git 都一样)。
  5. 服务器 /opt/dsh/docs 保持 root 600。

相关档案:01(架构)|02(运维,含附录 C 速查)|21(nginx 性能/流式)|22(域名迁移)|26(升级耦合点与回归清单)