“用 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 的三个问题

  1. 回环语义:容器内的 127.0.0.1 指容器自身,宿主机调用方打不进去;用 -p 3080:3080 发布端口等于把无鉴权接口暴露到所有网卡,不建议。
  2. 状态目录: 必须持久化,否则容器重建后 sessions/ 和 integrations/ 全部丢失。挂载卷时宿主机与容器内路径映射必须一致,否则 workspaces.json 里的 deliveryTargets 与实际位置对不上。
  3. 定时任务: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 里已存在的条目,否则接口不受理。

四、验证步骤

  1. 宿主机直连验证:
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 是否合法。

  1. 容器内验证连通性:
docker compose exec collector \
  curl -s -o /dev/null -w '%{http_code}\n' \
  http://host.docker.internal:3080/api/dsh-im/delivery/messages

连不上说明网络路径没打通,不是 DSH 的问题。

  1. 检查 /sessions/ 下对应会话的 session.v4.jsonl.zstd 是否有新增内容。

五、边界与常见误解

  • 自行构建镜像需完整挂载 并处理回环绑定,维护成本高于直接跑宿主机进程。
  • 把 3080 发布到 0.0.0.0 会移除“仅本机”保护,该接口无额外鉴权,不要这么做。
  • host.docker.internal 在非 Linux 平台或旧版 Docker 上行为不同,需逐环境验证。
  • 需要完全隔离时,更稳的路径是保留 DSH 在宿主机,把隔离放在调用方一侧。

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注