docs: switch license to GPL-3.0; rewrite DEPLOY for official dsh@latest

Image installs official @deepseek-ai/dsh from npm on start. Document
port forward 3080→3081, mandatory browser token, and drop obsolete
fork release/monorepo guidance.
This commit is contained in:
SKYMirror committed 2026-09-16 00:30:59 +08:00
1 parent 0bfe0b4a65
commit 7444ba9ca0
5 files changed
+844 -255

No files matched your search

+167 -219
View File
@@ -1,285 +1,233 @@
# DeepSeek Harness (DSH) Docker 部署指南
# DeepSeek Harness Docker 部署
## 概述
本仓库只包含 **Docker 部署封装**,不含 DSH 源码。运行时从 npm 安装官方 [`@deepseek-ai/dsh`](https://www.npmjs.com/package/@deepseek-ai/dsh)。
本仓库是 DeepSeek Harness 源码 + Docker 构建配置。镜像**只内置工具链**,不把 dsh 钉死在构建层:容器每次启动由 `docker/entrypoint.sh` 从 npm 安装官方 `@deepseek-ai/dsh@latest`(可用环境变量覆盖),安装失败则非零退出,不回退到旧版本。因此**启动时必须能访问 npm registry**。
### 端口拓扑(当前实现)
官方 `dsh web` **只监听 `127.0.0.1`**,并明确拒绝 `--host 0.0.0.0`(安全原因:Web 面可远程执行代码)。Docker 的 `-p` 打不到容器内 loopback,故 entrypoint 额外起一层 L4 转发:
```
宿主机 :3080 → 容器 0.0.0.0:3080 → 127.0.0.1:3081 (dsh web --no-open --port 3081)
```
```
┌──────────┐ http://localhost:3080/?token=… ┌─────────────────────────────┐
│ 浏览器 │ ────────────────────────────────▶ │ 0.0.0.0:3080 (转发) │
└──────────┘ │ ↓ │
│ 127.0.0.1:3081 (dsh web) │
└──────────────┬──────────────┘
┌──────────────────────────────┼──────────────┐
│ dsh-data 卷 │ ./workspace │
│ /home/dsh/.dsh │ /workspace │
│ (会话/设置/插件/配置) │ (工作目录) │
└──────────────────────────────┴──────────────┘
```
### 浏览器认证(官方包行为)
官方 `@latest` **强制浏览器认证**,没有免 token 的开关:
1. 从 `docker logs` 复制带 `?token=` 的 URL,用浏览器打开**一次**
2. 服务端 `303` 下发 **30 天签名 cookie**
3. 之后同一浏览器直接访问 `http://<host>:3080/` 即可
**说明**:历史 fork 镜像里的 `DSH_TRUST_ALL=1`、`DSH_TRUSTED_HOSTS` 环境变量、默认绑定 `0.0.0.0` 均**不在官方 npm 包中**。官方侧 Host/Origin 围栏可用 **`--trusted-host`**(compose `command` 传入)扩展非 loopback 权威;cookie 认证仍必须完成。
## 镜像信息
**当前 Dockerfile 为「运行时拉取」模型**:构建产出工具链底座(约 747MB,linux/arm64 本地实测),dsh 版本由每次容器启动时的 `@deepseek-ai/dsh@latest` 决定,不体现在镜像 tag 里。
| 项目 | 值 |
|------|-----|
| 基础镜像 | `registry.cn-hangzhou.aliyuncs.com/skymirror/node:24.19.0-bookworm-slim`(`ARG BASE_IMAGE` 可覆盖;内容同 `docker.io/library/node:24.19.0-bookworm-slim`) |
| dsh 安装 | 启动时 `npm install -g @deepseek-ai/dsh@latest`(默认 `registry.npmjs.org`) |
| 宿主机端口 | `3080`(compose `3080:3080`) |
| 容器内 dsh 端口 | `3081`(仅 `127.0.0.1`) |
| ENTRYPOINT | `/usr/local/bin/dsh-entrypoint` |
### 历史镜像存档(已过时,勿按其行为部署)
以下 digest 对应**旧 fork 构建时安装**镜像(`0.1.2-alpha.2-skymirror.1`,`dist/` pack + `file:` 闭包、`DSH_TRUST_ALL`、默认 `0.0.0.0`)。当前 Dockerfile 已取代该模型,哈希仅备查:
| 项目 | 值 |
|------|-----|
| manifest list | `sha256:4c8636d1f20804a39757829de5adb27dccf6435b99610e2a70e74fe8cc95b5bd` |
| 更早 monorepo 编译镜像 | `sha256:8a5d6406abd56f5cc3f175aa6b751e226bc91430dfb896add1fcf9a3f970f39e` |
| | |
|---|---|
| 构建产出 | 工具链底座(Node 24 / Python 3.11 / gcc / pnpm / rustup 引导),约 750MB(arm64 实测) |
| 每次启动 | `npm install -g @deepseek-ai/dsh@latest`,失败则容器退出(无内置 dsh 兜底) |
| 宿主机端口 | `3080` |
| 数据 | 命名卷 `dsh-data` → `/home/dsh/.dsh`(会话、设置、插件、npm cache) |
## 快速开始
```bash
git clone <仓库地址>
git clone <本仓库地址>
cd DSH
docker compose build
docker compose up -d
docker compose up -d --build
docker logs -f dsh
# 等待出现类似:
# dsh-entry: installed 0.1.5-rc.1
# dsh-entry: forwarding 0.0.0.0:3080 -> 127.0.0.1:3081
# dsh web: http://127.0.0.1:3081/?token=<一次性令牌>
# 把日志 URL 中的 127.0.0.1:3081 换成 127.0.0.1:3080(或服务器 IP:3080)后打开
```
首次使用时在 Web UI 中配置模型 API Key(DeepSeek 或任意 OpenAI 兼容端点)。
日志中会出现:
国内网络建议在 compose 的 `environment` 里加:
```text
dsh-entry: installed 0.1.5-rc.1 ← 版本以 npm 上 latest 为准
dsh-entry: forwarding 0.0.0.0:3080 -> 127.0.0.1:3081
dsh web: http://127.0.0.1:3081/?token=<一次性令牌>
```
1. 把 URL 里的 **`127.0.0.1:3081` 换成 `127.0.0.1:3080`**(或 `服务器IP:3080`)
2. 浏览器打开**一次**带 `?token=` 的地址 → 写入 30 天 cookie
3. 之后直接访问 `http://127.0.0.1:3080/` 即可
国内网络建议在 `docker-compose.yml` 的 `environment` 中启用:
```yaml
- DSH_NPM_REGISTRY=https://registry.npmmirror.com
```
## 启动环境变量与参数
> 官方 `@latest` **强制浏览器认证**,没有免 token 开关。`DSH_TRUST_ALL` 只存在于历史 fork 镜像,当前路径无效。
### 环境变量(`docker/entrypoint.sh`)
## 端口拓扑
官方 `dsh web` 只监听 `127.0.0.1`,并拒绝 `--host 0.0.0.0`。Docker `-p` 打不到容器内 loopback,因此 entrypoint 做 L4 转发:
```text
宿主机 :3080
→ 容器 0.0.0.0:3080 (entrypoint 转发)
→ 127.0.0.1:3081 (dsh web --no-open --port 3081)
```
```text
浏览器 ──http://host:3080/?token=…──▶ 0.0.0.0:3080 ──▶ 127.0.0.1:3081
│
┌─────────────────────────┴─────────────────────────┐
│ dsh-data:/home/dsh/.dsh ./workspace:/workspace │
│ 会话/设置/插件/npm-cache agent 工作目录 │
└───────────────────────────────────────────────────┘
```
**不要**单独 `-p` 映射容器内 `3081`;Docker 映射的应是转发口 `3080`。
## 配置参考
### 环境变量(entrypoint)
| 变量 | 默认 | 说明 |
|------|------|------|
| `DSH_PACKAGE` | `@deepseek-ai/dsh` | 要安装的包名 |
| `DSH_DIST_TAG` | `latest` | npm dist-tag 或精确版本(如 `0.1.5-rc.1`) |
| `DSH_NPM_REGISTRY` | `https://registry.npmjs.org` | 拉包 registry;国内可设 `https://registry.npmmirror.com` |
| `DSH_NPM_CACHE` | `$DSH_HOME/npm-cache` | npm cache(`dsh-data` 卷内,重启可命中) |
| `DSH_INTERNAL_PORT` | `3081` | dsh 监听端口(仅 `127.0.0.1`) |
| `DSH_FORWARD_PORT` | `3080` | entrypoint 在 `0.0.0.0` 上的转发端口(Docker `-p` 应映射此端口) |
| `DSH_PACKAGE` | `@deepseek-ai/dsh` | npm 包名 |
| `DSH_DIST_TAG` | `latest` | dist-tag 或精确版本(如 `0.1.5-rc.1`) |
| `DSH_NPM_REGISTRY` | `https://registry.npmjs.org` | 安装用 registry |
| `DSH_NPM_CACHE` | `$DSH_HOME/npm-cache` | npm cache(卷内,加速重启) |
| `DSH_INTERNAL_PORT` | `3081` | dsh 监听端口(仅 loopback) |
| `DSH_FORWARD_PORT` | `3080` | `0.0.0.0` 转发端口;与 compose `ports` 一致 |
### 传给 `dsh web` 的参数
entrypoint 固定附加 `--no-open --port ${DSH_INTERNAL_PORT}`。compose 的 `command` 会**追加**在后面,例如:
entrypoint 固定执行:
```yaml
command: ["--trusted-host", "203.0.113.10"]
```text
dsh web --no-open --port $DSH_INTERNAL_PORT <compose command...>
```
| 参数 | 作用 |
|------|------|
| `--trusted-host <authority>` | 向 Host/Origin 围栏追加非 loopback 权威(可重复);**不**替代 cookie 认证 |
| `--port` / `--host` | 不要通过 command 覆盖:端口由 `DSH_INTERNAL_PORT` 管理;`--host 0.0.0.0` 会被官方拒绝 |
compose 的 `command` 会追加在后面。常用:
```yaml
# 从其它机器用 IP/域名访问时,声明 Host/Origin 围栏权威(仍需 token cookie)
command: ["--trusted-host", "203.0.113.10"]
```
| 不要通过 command 传 | 原因 |
|---------------------|------|
| `--host 0.0.0.0` | 官方明确拒绝 |
| `--port` | 用 `DSH_INTERNAL_PORT` |
| `--no-open` | entrypoint 已固定 |
### 改端口
```yaml
environment:
- DSH_FORWARD_PORT=8080
ports:
- "8080:8080"
```
### 持久化与工作区
```yaml
volumes:
- dsh-data:/home/dsh/.dsh # 会话/设置/插件(保留)
- ./workspace:/workspace # agent 工作目录;HOME 指向这里
```
插件装在 `$DSH_HOME/profiles/<name>/`,**在卷内,重启不丢**:
```bash
docker exec -it dsh dsh plugin --profile web add <package>
docker restart dsh # 插件变更需重启容器
```
## 从其它机器访问
1. **围栏**:Host 为 `http://<IP>:3080` 时不是 loopback,需
`command: ["--trusted-host", "<IP或域名>"]`
2. **认证**:仍须用日志里的 `?token=` URL 打开一次
3. **安全**:DSH 可执行代码。公网请前置带认证/TLS 的反向代理,不要裸暴露
## 前置条件
- Docker Engine 20.10+(BuildKit)
- Docker Compose v2
- 启动时能访问 npm registry(官方 npmjs 或 npmmirror)
- 容器启动时能访问所配置的 npm registry
## 一、不用 Docker 直接跑
## 构建说明
```bash
# Node.js ^22.19 || >=24
npx --yes @deepseek-ai/dsh@latest web
# 或
npm install -g @deepseek-ai/dsh@latest
dsh web --no-open --port 3080
```
本机默认只监听 `127.0.0.1`,浏览器打开日志里的 `?token=` URL。管理插件需要 `corepack enable pnpm`。
查看可用版本:
```bash
npm view @deepseek-ai/dsh dist-tags
```
### npm 路径 vs Docker
| | `npx @latest` | Docker(本仓库) |
|---|---|---|
| 启动耗时 | 首次拉包约 15s–数分钟(视网络/cache) | 镜像已有工具链;每次启动装 dsh(有 cache 更快) |
| 构建 | 无 | 只装 apt/工具链,不编译 monorepo |
| 工具链 | 仅本机 Node | Python / gcc·g++ / pnpm / Rust 按需 |
| 升级 | 每次 `npx@latest` | 每次容器启动装 `@latest`(失败退出) |
| 数据 | `$DSH_HOME`(默认 `~/.dsh`) | `dsh-data` → `/home/dsh/.dsh` |
| 远程端口 | 只听 loopback | entrypoint `3080` → dsh `3081` |
## 二、本地构建并运行
```bash
docker compose build
docker compose build # 只装 apt/工具链,无 monorepo 编译
docker compose up -d
```
构建不需要 `pnpm build`、`release:pack` 或 `dist/`。
### 基础镜像
### 基础镜像拉不动时
Docker Hub 在部分网络不可达。可先把官方 node 镜像同步到自己 registry,再:
默认 `ARG BASE_IMAGE=registry.cn-hangzhou.aliyuncs.com/skymirror/node:24.19.0-bookworm-slim`(与 Docker Hub `node:24.19.0-bookworm-slim` 同源)。Docker Hub 不可达时可继续用该 ACR 地址,或自建同步后:
```bash
docker buildx build \
--build-arg BASE_IMAGE=registry.cn-hangzhou.aliyuncs.com/skymirror/node:24.19.0-bookworm-slim \
--platform linux/arm64 \
-t dsh:local-arm64 --load .
--build-arg BASE_IMAGE=<你的registry>/node:24.19.0-bookworm-slim \
--platform linux/arm64 -t dsh:local --load .
```
### 构建源(国内默认)
| 组件 | 默认源 |
|------|--------|
| apt | 清华 Debian |
| npm(构建期 npm/pnpm) | npmmirror |
| rustup 引导 | USTC / rsproxy |
| pip | 清华 |
可用 `--build-arg NPM_REGISTRY=...`、`APT_MIRROR=...` 覆盖。
### 清理 buildx 缓存
```bash
docker buildx du
docker buildx prune --filter until=24h
# 或
docker buildx prune --all
# docker buildx prune --all
```
## 三、云效构建并推送
## 镜像内置环境
流水线「镜像构建」使用仓库根目录 `Dockerfile`,推送到 ACR。目标机:
```bash
docker pull registry.cn-hangzhou.aliyuncs.com/skymirror/dsh:latest
docker run -d --name dsh \
-p 3080:3080 \
-v dsh-data:/home/dsh/.dsh \
-v /path/to/workspace:/workspace \
-e DSH_NPM_REGISTRY=https://registry.npmmirror.com \
registry.cn-hangzhou.aliyuncs.com/skymirror/dsh:latest
```
注意:推上去的是**工具链底座**;运行时仍会拉 npm 上的 dsh。请保证部署机可访问所配置的 registry。
## 四、目录 / 端口映射
| 项目 | 说明 |
|------|------|
| 宿主机 `3080` → 容器 `3080` | entrypoint 的 `0.0.0.0` 转发口 |
| 容器内 dsh `3081` | 仅 `127.0.0.1`,不要单独 `-p` 映射 |
| `dsh-data` → `/home/dsh/.dsh` | 会话、settings、storages、profiles(插件)、npm-cache |
| `./workspace` → `/workspace` | agent 工作目录;目录选择器默认起点(`HOME`) |
| `$DSH_HOME/profiles/<name>` | 插件与 profile,卷内**持久化** |
| `dsh` 命令 | 启动时 `npm install -g` 装入 PATH,供 `docker exec` 管理插件 |
插件示例:
```bash
docker exec -it dsh dsh plugin --profile web add <package>
# 改完插件需重启容器
docker restart dsh
```
## 五、镜像内置语言环境
| 语言/工具 | 版本 | 说明 |
|-----------|------|------|
| 工具 | 版本 | 说明 |
|------|------|------|
| Node.js | 24.19.0 | 基础镜像 |
| npm | 11.17.0 | 全局升级 |
| pnpm | 11.7.0 | corepack;`dsh plugin add` 使用 |
| Python | 3.11 | apt,含 pip/venv/Python.h |
| Rust | stable 按需 | 首次调用 `cargo`/`rustc`/`rustup` 时装进 `dsh-data` 卷 |
| npm | 11.17.0 | 构建期升级 |
| pnpm | 11.7.0 | `dsh plugin add` 依赖 |
| Python | 3.11 | apt;含 pip / venv / Python.h |
| gcc / g++ / make | bookworm | 原生模块编译 |
| Rust | stable,按需 | 首次 `cargo`/`rustc`/`rustup` 时装入 `dsh-data` 卷 |
| git / curl | apt | |
不内置 JDK、Go(体积取舍)。需要时进容器自行安装。
不内置 JDK、Go。需要时进容器安装。
> 构建默认走国内源:npm `npmmirror`、Debian 清华、rustup USTC、pip 清华。云效等可出网环境可用 `--build-arg NPM_REGISTRY=...` 等覆盖。
### Rust 按需安装
`/usr/local/bin/ensure-rust` 包装器在首次调用时把 toolchain 装进 `$RUSTUP_HOME` / `$CARGO_HOME`(均在 `dsh-data` 卷)。可提前 `docker exec dsh cargo --version` 触发。
## 六、自定义配置
### 修改端口
```yaml
environment:
- DSH_FORWARD_PORT=8080 # 容器 0.0.0.0 转发口
- DSH_INTERNAL_PORT=3081 # dsh 本机端口(一般不用改)
ports:
- "8080:8080"
```bash
# 预触发 Rust 安装(可选)
docker exec dsh cargo --version
```
### 从其它机器访问
## 不用 Docker
1. **Host/Origin 围栏**:浏览器地址为 `http://<服务器IP>:3080` 时,Host 不是 loopback,需在 compose 中追加:
```yaml
command: ["--trusted-host", "<服务器IP或域名>"]
```
2. **浏览器认证**:仍必须用日志里的 `?token=` URL 打开一次,换 30 天 cookie。
3. **传输与入口**:DSH 可执行代码。公网暴露请在前面加带认证/TLS 的反向代理,不要裸奔。
### 挂载自己的数据目录
```yaml
volumes:
- /data/dsh:/home/dsh/.dsh
- /data/work:/workspace
```bash
# Node ^22.19 || >=24
npx --yes @deepseek-ai/dsh@latest web
npm view @deepseek-ai/dsh dist-tags
```
## 七、常见问题
本机只监听 `127.0.0.1`,打开日志中的 `?token=` URL。插件管理需 `corepack enable pnpm`。
| | `npx` | 本仓库 Docker |
|---|---|---|
| 工具链 | 仅本机 Node | Python / gcc / pnpm / Rust 按需 |
| 升级 | 每次 `@latest` | 每次容器启动 `@latest` |
| 数据 | `~/.dsh` | `dsh-data` 卷 |
| 远程访问 | 仅 loopback | `3080` 转发进容器 |
## 仓库布局
```text
Dockerfile 工具链镜像 + entrypoint
docker-compose.yml 端口 / 卷 / 环境变量
docker/entrypoint.sh 安装 @latest → 转发 → dsh web
docker/ensure-rust.sh Rust 按需安装包装器
DEPLOY.md 本文档
LICENSE GPL-3.0
```
## 常见问题
| 现象 | 处理 |
|------|------|
| 启动很慢或退出 | 看 `docker logs`:多半是装 `@latest` 网络失败;设 `DSH_NPM_REGISTRY=https://registry.npmmirror.com` |
| `authentication required; reopen the URL printed by dsh web` | 正常:用带 `?token=` 的 URL 打开一次;官方包**不能**免 token |
| `GET /` 401 | 同上 |
| `/api` 403 | Host 不在围栏内:给 `dsh web` 加 `--trusted-host <你的Host>` |
| 宿主机 3080 连不上 / connection reset | 确认映射的是转发口 `3080:3080`,且 logs 有 `forwarding 0.0.0.0:3080 -> 127.0.0.1:3081` |
| `--host 0.0.0.0 is intentionally not supported` | 官方限制;不要传 `--host 0.0.0.0`,依赖 entrypoint 转发 |
| 端口被占用 | 改 compose 的 `ports` 与 `DSH_FORWARD_PORT` |
| `Cannot find module .../dsh/lib/bin.js` | 自定义了 `entrypoint:`/`command:` 绕过安装入口;去掉覆盖后重建 |
| 插件重启后消失 | 插件应装在 `$DSH_HOME/profiles`(卷内);确认未挂错卷 |
| 设置了 `DSH_TRUST_ALL=1` 仍要 token | **预期行为**:官方包无此变量;历史 fork 镜像才有 |
| 启动慢或立刻退出 | `docker logs`:多半 npm 安装失败。设 `DSH_NPM_REGISTRY=https://registry.npmmirror.com`,或钉版本 `DSH_DIST_TAG=0.1.5-rc.1` |
| `authentication required; reopen the URL printed by dsh web` | 正常。用带 `?token=` 的 URL 打开一次;官方包不能免 token |
| `GET /` 返回 401 | 同上 |
| `/api` 返回 403 | Host 不在围栏:`command: ["--trusted-host", "<你的Host>"]` |
| 宿主机 3080 connection reset | 映射应为 `3080:3080`;logs 需有 `forwarding 0.0.0.0:3080 -> 127.0.0.1:3081` |
| `--host 0.0.0.0 is intentionally not supported` | 官方限制。不要传该参数,依赖转发 |
| 端口占用 | 同时改 `ports` 与 `DSH_FORWARD_PORT` |
| `Cannot find module .../dsh/lib/bin.js` | 覆盖了 `entrypoint:`/`command:`,绕过了安装入口;去掉覆盖后重建 |
| 插件重启后消失 | 确认插件装在 `$DSH_HOME/profiles`,且 `dsh-data` 卷未挂错 |
| 设置了 `DSH_TRUST_ALL=1` 仍要 token | 预期行为。官方包无此变量 |
## 附录:fork 私有 npm 发布(可选,当前 Docker 不用)
## 许可
当前 Docker 路径**只消费官方 npm `@latest`**。若仍需维护 skymirror fork 并发到私有 registry,可继续用仓库内 release 脚本(与镜像构建解耦):
```bash
pnpm run release:dsh <version>
pnpm run build:official
pnpm run release:pack --family dsh --out dist/npm
# … vendor / landlock / verify / publish,见 scripts/release/
```
fork 的 `DSH_TRUST_ALL`、`DSH_TRUSTED_HOSTS` 环境变量、默认 `0.0.0.0` 绑定等部署补丁只存在于 fork 编译产物中;要在容器里用它们,需把 `DSH_PACKAGE` / `DSH_DIST_TAG` / `DSH_NPM_REGISTRY` 指到私有 registry 的 fork 包,并自行承担与官方 `@latest` 的行为差异。
本仓库部署封装为 [GPL-3.0](LICENSE)。上游 DeepSeek Harness 为 MIT,运行时安装的官方 npm 包不受本仓库许可证变更影响。