把 DSH 装进 Docker 之前,先看清这三处约束
“用 Docker 装 DSH”是常见诉求,但 DSH 不是无状态服务,容器化前要先看清结构。
一、DSH 的运行时结构
DSH 围绕本地目录 组织状态:
- 会话记录:
,按会话累积写入;/sessions/session.v4.jsonl.zstd - 渠道插件配置:
;/integrations/dsh-im/ - 投递目标:
里的/integrations/dsh-<渠道>/workspaces.json deliveryTargets数组。
对外只暴露一个仅监听本机回环的投递接口:
POST http://127.0.0.1:3080/api/dsh-im/delivery/messages
Content-Type: application/json
{
"botId": "example-bot",
"targetId": "example-target",
"text": "hello",
"format": "plain"
}
format 只接受 plain 或 markdown,受理成功返回 {"sent": true}。接口不额外鉴权,前提是请求来自本机回环。
二、直接 docker run 的三个问题
- 回环语义:容器内的
127.0.0.1指容器自身,宿主机调用方打不进去;用-p 3080:3080发布端口等于把无鉴权接口暴露到所有网卡,不建议。 - 状态目录:
必须持久化,否则容器重建后sessions/和integrations/全部丢失。挂载卷时宿主机与容器内路径映射必须一致,否则workspaces.json里的deliveryTargets与实际位置对不上。 - 定时任务:schedule 依赖进程常驻并在重启后补发最近一次提示词,容器被
docker stop或编排回收后补发行为取决于进程是否正常退出,容易与预期不符。
三、可行做法:宿主机进程 + 容器内调用方
如果目标只是让别的容器能给 DSH 发消息,不必容器化 DSH。保持 DSH 在宿主机以本地进程运行,让调用方容器通过宿主机网关访问回环接口:
services:
collector:
image: example/collector:latest
extra_hosts:
- "host.docker.internal:host-gateway"
environment:
DSH_ENDPOINT: "http://host.docker.internal:3080/api/dsh-im/delivery/messages"
这要求 DSH 监听地址允许来自宿主机网关的连接。若当前版本仍严格绑定 127.0.0.1,只能在宿主机跑转发脚本调用回环接口。监听配置以当前版本为准,不要凭猜测改。
targetId 必须是 deliveryTargets 里已存在的条目,否则接口不受理。
四、验证步骤
- 宿主机直连验证:
curl -s -X POST http://127.0.0.1:3080/api/dsh-im/delivery/messages \
-H 'Content-Type: application/json' \
-d '{"botId":"example-bot","targetId":"example-target","text":"ping","format":"plain"}'
返回 {"sent": true} 表示受理。其他返回先查 targetId 是否在 deliveryTargets 里,再查 format 是否合法。
- 容器内验证连通性:
docker compose exec collector \
curl -s -o /dev/null -w '%{http_code}\n' \
http://host.docker.internal:3080/api/dsh-im/delivery/messages
连不上说明网络路径没打通,不是 DSH 的问题。
- 检查
下对应会话的/sessions/ session.v4.jsonl.zstd是否有新增内容。
五、边界与常见误解
- 自行构建镜像需完整挂载
并处理回环绑定,维护成本高于直接跑宿主机进程。 - 把 3080 发布到
0.0.0.0会移除“仅本机”保护,该接口无额外鉴权,不要这么做。 host.docker.internal在非 Linux 平台或旧版 Docker 上行为不同,需逐环境验证。- 需要完全隔离时,更稳的路径是保留 DSH 在宿主机,把隔离放在调用方一侧。