群晖相关

群晖 Docker 部署 DeepSeek Harness(dsh)完整教程

2026-08-15 23 次阅读 2528 字 · 7 分钟

群晖 Docker 部署 DeepSeek Harness(dsh)完整教程



写在前面



DeepSeek Harness(@deepseek-ai/dsh)是 DeepSeek 官方的 Agent 框架,自带 Web UI,可以让 AI Agent 读写文件、执行命令、委派子任务。本教程基于 群晖 DS920+(J4125 / DSM 7.4 / Container Manager) 完整部署一套可用实例:

  • 公网入口:http://你的域名.com:3080(nps 内网穿透,已开启账号密码登录)

  • 局域网入口:http://你的群晖IP:3080(HTTP,已内置兼容补丁)或 https://你的群晖IP:3443(自签证书 HTTPS)

  • 工作目录:/volume1/deepseek(放在群晖 RAID 盘上,稳定持久)

  • 默认模型:deepseek-v4-flash,API Key 通过 DSH_HOME 下的凭据文件配置

  • 登录:nginx basic auth,用户名 dsh(密码部署时设定,见下文)


  • > 本文所有命令默认你已经通过 SSH 登录群晖(把 你的群晖用户名 换成实际账号),并拥有 sudo 权限。



    一、为什么用群晖而不是 PVE



    两个方案都能跑,区别在数据安全性:

  • PVE LXC:部署简单、管理方便,但存储通常是单盘,没有 RAID;

  • 群晖 Docker:数据在 RAID 阵列上,工作目录天然稳定,适合存放 Agent 长期产生的项目文件。


  • 最终方案是 服务跑在群晖 Docker 容器里,工作目录直接落在群晖存储卷上,兼顾稳定和易用。



    二、准备目录



    # 工作区(Agent 读写项目文件的目录,放在 RAID 盘上)
    sudo mkdir -p /volume1/deepseek
    
    # dsh 构建目录(Dockerfile、nginx 配置、DSH_HOME 数据)
    sudo mkdir -p /volume1/docker/dsh/home
    


    /volume1/deepseek 就是之后 Web UI 里选择的工作区。



    三、国内网络加速(关键)



    群晖直连 deb.debian.org 和 registry.npmjs.org 通常很慢甚至卡死,必须在 Dockerfile 里换源:

  • apt 源:mirrors.tuna.tsinghua.edu.cn(实测 45MB/s)

  • npm 源:https://registry.npmmirror.com




  • 四、Dockerfile



    写入 /volume1/docker/dsh/Dockerfile:

    FROM node:22-bookworm-slim
    ENV DSH_HOME=/opt/dsh
    RUN sed -i 's|http://deb.debian.org/debian|http://mirrors.tuna.tsinghua.edu.cn/debian|g; s|http://security.debian.org/debian-security|http://mirrors.tuna.tsinghua.edu.cn/debian-security|g' /etc/apt/sources.list.d/debian.sources \
     && apt-get update \
     && apt-get install -y --no-install-recommends build-essential python3 nginx openssl ca-certificates \
     && npm config set registry https://registry.npmmirror.com \
     && npm install -g @deepseek-ai/dsh \
     && rm -f /etc/nginx/sites-enabled/default \
     && rm -rf /var/lib/apt/lists/*
    RUN openssl req -x509 -newkey rsa:2048 -nodes -keyout /etc/nginx/dsh.key \
     -out /etc/nginx/dsh.crt -days 3650 -subj '/CN=你的群晖IP' \
     -addext 'subjectAltName=IP:你的群晖IP,DNS:localhost' 2>/dev/null
    COPY nginx.conf /etc/nginx/conf.d/dsh.conf
    COPY .htpasswd /etc/nginx/.htpasswd
    CMD ["sh", "-c", "nginx && exec dsh web --host 127.0.0.1 --port 3080 --trusted-host 你的群晖IP:3080 --trusted-host 你的群晖IP:3443 --trusted-host 你的域名.com --trusted-host 你的公网IP:3080"]
    


    几个要点:

  • build-essential 必须有,node-pty 需要本地编译(没有会报 make: command not found);

  • dsh 出于安全考虑禁止直接绑定 0.0.0.0,所以让 dsh 只监听 127.0.0.1,由 nginx 反向代理对外提供服务;

  • --trusted-host 是给 dsh 的 /api 跨域防护墙声明合法访问来源;

  • 自签证书是为了让浏览器处于"安全上下文"(见下文 crypto 问题)。




  • 五、nginx.conf



    写入 /volume1/docker/dsh/nginx.conf:

    server {
        listen 80;
        server_name _;
        auth_basic "dsh";
        auth_basic_user_file /etc/nginx/.htpasswd;
        client_max_body_size 0;
        location / {
            proxy_pass http://127.0.0.1:3080;
            proxy_http_version 1.1;
            proxy_set_header Host $http_host;
            proxy_set_header X-Real-IP $remote_addr;
            proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
            proxy_set_header Upgrade $http_upgrade;
            proxy_set_header Connection "upgrade";
            proxy_read_timeout 3600s;
            proxy_send_timeout 3600s;
            sub_filter_once off;
            sub_filter_types text/html;
            sub_filter '<head>' '<head><script>if(window.crypto&&!crypto.randomUUID){crypto.randomUUID=function(){var d=new Uint8Array(16);crypto.getRandomValues(d);d[6]=d[6]&15|64;d[8]=d[8]&63|128;var h=Array.from(d,function(b){return b.toString(16).padStart(2,"0")});return h[0]+h[1]+h[2]+h[3]+"-"+h[4]+h[5]+"-"+h[6]+h[7]+"-"+h[8]+h[9]+"-"+h[10]+h[11]+h[12]+h[13]+h[14]+h[15]}};</script>';
        }
    }
    server {
        listen 443 ssl;
        server_name _;
        auth_basic "dsh";
        auth_basic_user_file /etc/nginx/.htpasswd;
        ssl_certificate /etc/nginx/dsh.crt;
        ssl_certificate_key /etc/nginx/dsh.key;
        client_max_body_size 0;
        location / {
            proxy_pass http://127.0.0.1:3080;
            proxy_http_version 1.1;
            proxy_set_header Host $http_host;
            proxy_set_header X-Real-IP $remote_addr;
            proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
            proxy_set_header Upgrade $http_upgrade;
            proxy_set_header Connection "upgrade";
            proxy_read_timeout 3600s;
            proxy_send_timeout 3600s;
        }
    }
    


    登录账号与密码(basic auth)



    访问 dsh 需要账号密码(basic auth),nginx 层拦截,页面无法自助修改。生成密码文件写入构建目录(dsh 是用户名,你的密码 换成你想设的密码):

    printf 'dsh:%s\n' "$(openssl passwd -apr1 '你的密码')" | sudo tee /volume1/docker/dsh/.htpasswd
    


    之后每次访问页面、调 API 都要带这个账号密码。

    > 提醒:本教程部署的实例用户名固定为 dsh;密码由部署者私下设定,避免公网入口被他人直接登录。需要更换时按下面步骤操作。

    修改用户名或密码:重新生成 .htpasswd 文件(把 新用户名 和 新密码 换成你要的值),然后重建镜像并重建容器即可生效:

    # 1. 重新生成密码文件(改用户名/密码都走这一步)
    printf '新用户名:%s\n' "$(openssl passwd -apr1 '新密码')" | sudo tee /volume1/docker/dsh/.htpasswd
    
    # 2. 重建镜像(基础层已缓存,很快)
    cd /volume1/docker/dsh
    sudo /var/packages/ContainerManager/target/usr/bin/docker build -t dsh:latest .
    
    # 3. 重建容器(删除旧容器并重新运行)
    sudo /var/packages/ContainerManager/target/usr/bin/docker rm -f dsh
    sudo /var/packages/ContainerManager/target/usr/bin/docker run -d \
      --name dsh \
      --restart unless-stopped \
      -e DSH_HOME=/opt/dsh \
      -v /volume1/docker/dsh/home:/opt/dsh \
      -v /volume1/deepseek:/workspace \
      -p 3080:80 \
      -p 3443:443 \
      dsh:latest
    


    > 注意:.htpasswd 是打包进镜像的,所以改完必须重建镜像,不能只改群晖上的文件。

    为什么要在页面里注入 randomUUID 补丁



    dsh 的 Web UI 在选择工作区时会调用浏览器 API crypto.randomUUID(),而这个函数只在 HTTPS 或 localhost 的安全上下文中存在。局域网 IP 用明文 HTTP 访问时,浏览器没有这个函数,会报:

    crypto.randomUUID is not a function
    


    两个解决办法:

  • 用 HTTPS 访问(本文的 https://你的群晖IP:3443,自签证书首次访问需点"继续前往");

  • 通过 nginx sub_filter 在页面里注入一个 UUID v4 补丁(上面的配置),这样普通 HTTP 也能正常使用。




  • 六、构建镜像



    cd /volume1/docker/dsh
    sudo /var/packages/ContainerManager/target/usr/bin/docker build -t dsh:latest .
    


    实测:apt 走清华源 87MB 两秒拉完,npm 532 个包 59 秒装完,整体约 5 分钟。



    七、配置 API Key



    sudo mkdir -p /volume1/docker/dsh/home
    echo 'DEEPSEEK_API_KEY: sk-你的密钥' | sudo tee /volume1/docker/dsh/home/.credentials.yaml
    sudo chmod 600 /volume1/docker/dsh/home/.credentials.yaml
    


    该目录会挂载成容器里的 /opt/dsh(即 DSH_HOME)。



    八、运行容器



    sudo /var/packages/ContainerManager/target/usr/bin/docker run -d \
      --name dsh \
      --restart unless-stopped \
      -e DSH_HOME=/opt/dsh \
      -v /volume1/docker/dsh/home:/opt/dsh \
      -v /volume1/deepseek:/workspace \
      -p 3080:80 \
      -p 3443:443 \
      dsh:latest
    


    注意端口映射:容器内 nginx 监听 80/443,所以宿主机映射为 3080:80、3443:443,别映射反了(映射到容器内不监听的端口会导致连接被重置)。



    九、验证



    # 在群晖上(basic auth 已开启:不带账号返回 401,带上账号返回 200)
    curl -sS -o /dev/null -w '%{http_code}\n' -u '用户名:密码' http://127.0.0.1:3080   # 200
    curl -skS -o /dev/null -w '%{http_code}\n' -u '用户名:密码' https://127.0.0.1:3443  # 200
    
    # 查看日志
    sudo /var/packages/ContainerManager/target/usr/bin/docker logs dsh
    


    浏览器访问(首次会弹出 basic auth 登录框):

  • 公网:http://你的域名.com:3080

  • 局域网:http://你的群晖IP:3080(推荐,无需证书)

  • 或 https://你的群晖IP:3443(首次有自签证书提示)




十、开始使用



  • 打开 Web UI,先输入 basic auth 登录账号密码;

  • 设置 → 模型:确认 DeepSeek 提供方存在(Key 已由 .credentials.yaml 提供,无需在页面再填);

  • 选择工作区:添加 /workspace(对应群晖的 /volume1/deepseek)或其子目录;

  • 模型下拉框选择 DeepSeek-V4-Flash(默认第一个);

  • 发送消息即可,例如:


  • Summarize this repository and identify its main packages.
    




    十一、常见问题



    1. 构建时卡在 apt-get install



    群晖访问 deb.debian.org 不通,换成清华源即可(本文 Dockerfile 已内置)。

    2. 提示 make: command not found



    缺编译工具,node-pty 需要 build-essential(本文已包含)。

    3. 端口映射后连不上(连接被重置)



    检查映射关系:容器内 nginx 是 80/443,映射应为 3080:80、3443:443。

    4. 选工作区报 crypto.randomUUID is not a function



    用 HTTPS 访问,或使用本文 nginx 注入的 randomUUID 补丁(HTTP 也可以)。

    5. 公网访问



    已完成:在 nps 控制台为 dsh 建立隧道,公网 http://你的域名.com:3080 指向本机 127.0.0.1:3080,同时 nginx 开启了 basic auth 登录。dsh 的 Agent 具备文件与命令执行能力,公网入口务必保持账号密码保护;如要 HTTPS 公网访问,可后续接 Cloudflare 隧道或 nps HTTPS 代理。



    附录:完整文件清单



    /volume1/docker/dsh/
    ├── Dockerfile
    ├── nginx.conf
    └── home/                  # DSH_HOME,含 .credentials.yaml
    /volume1/deepseek/         # 工作区(RAID 盘)
    

    评论(0)

    5 + 5 =
    ← 返回首页