
我第一次尝试在 Linux 服务器上源码部署 DeepSeek Harness Web 时最大的错觉就是“本地能跑通服务器上一定也没问题”。结果前端页面打开了接口却全挂在半路防火墙、绑定地址、构建产物权限这些坑轮流出来打招呼。后来老老实实从头捋了一遍把部署流程固定成一套可复现的步骤才真正敢说“从零到远程访问”这件事我摸透了。这篇文章就按我当时实际操作的顺序来写从环境准备、源码拉取、依赖安装、本地启动到最后的远程访问配置和常见故障排查。适合两类人看一是刚买了一台 Linux 服务器、想把 DeepSeek Harness 这类工作流类 Web 项目跑起来的新手二是已经在用 Docker 部署、但想改成源码部署以方便二次改动或安装自定义插件的老手。下面我尽量把每个命令、每个配置背后的原因也讲清楚不光是让你“能跑”而是让你“出了问题知道去哪查”。1. 源码部署前先想清楚三件事部署方式、模型后端、访问路径很多人拿到 DeepSeek Harness下面统一叫 dsh的源码就急着git clone然后一路install到底。我的经验是先花十分钟想清楚下面三件事后面能省出一整天的排障时间。1.1 为什么选择源码部署而不是直接跑镜像如果项目方提供了现成的容器镜像直接用镜像确实快。但镜像方案有几个我实际遇到过的痛点镜像版本往往滞后于上游代码某些插件要装进容器里需要重新构建整个镜像而且如果服务器在内网环境、不方便拉取外部镜像源码部署几乎是唯一稳定可控的选择。源码部署的另一个优势是“看得见、改得动”。比如我想在 dsh 的 Web 端加一个内部工具入口或者调整某个页面组件的展示逻辑直接改前端代码重新npm run build就行不需要去折腾容器内部文件系统。对长期运维来说源码部署的服务就是一个普通进程systemd、日志、环境变量全都透明排查问题更直接。当然源码部署也不是没有代价你需要自己管理运行环境、依赖版本、构建产物和进程守护。这也是我写这篇文章的初衷——把这些“自己管理”的部分讲透。1.2 模型后端怎么接官方 API 还是本地推理服务这里必须说清楚dsh 这类项目只是个“Web 工作流外壳”它本身不内置大模型。你需要把模型服务接入进来用户才能在网页上真正对话。两条主流路径官方 API 方式在 dsh 的环境配置里填写模型服务地址一般兼容 OpenAI 风格接口和密钥。优点是部署最简单服务器不需要高性能显卡适合快速验证 dsh 的功能。本地推理方式在服务器上单独部署 Ollama、vLLM 这类推理服务让 dsh 指向本地地址。优点是数据不出内网、长期使用没有按次计费压力适合有 GPU 的服务器或离线环境。我的建议是第一次部署先用官方 API 方式把 dsh 跑通确认 Web 页面、工作流、插件都正常后再考虑要不要换本地模型。这样可以把“dsh 自身的问题”和“模型服务的问题”分开排查不会混在一起。1.3 远程访问的三种路径远程访问不是“把端口暴露出去就行”这么简单。我见过太多人直接把 8000 端口映射到公网结果三天后日志里全是扫描流量。按使用场景选路径有域名 服务器有公网 IP最推荐。用 Nginx 做流量转发把 80/443 端口作为对外入口dsh 服务本身只监听本机回环地址。这也是我下面详细展开的方式。暂时没有域名可以先用http://服务器IP:端口的方式访问但注意不要在公网直接裸奔至少要在防火墙上限制来源 IP。仅作临时排查用 SSH 端口转发技巧把服务器上的 dsh 临时映射到本地浏览器适合验证服务状态而不想改任何网络配置。2. 服务器环境初始化专用账户、目录规划和工具链很多教程上来就让你在 root 下操作省事是省事但我强烈建议为 dsh 单独创建一个系统用户。Web 服务一旦被攻击者拿到执行权限运行用户不是 root 至少还能把损失控制在一个范围内。2.1 更新系统并创建专用账号我用的是 Ubuntu 22.04 LTS下面命令在 Debian 系发行版上通用sudo apt update sudo apt upgrade -y # 创建一个不带登录 shell 的系统用户 sudo useradd -r -s /usr/sbin/nologin -d /opt/dsh dsh # 创建代码目录和日志目录 sudo mkdir -p /opt/dsh/src sudo mkdir -p /var/log/dsh sudo chown -R dsh:dsh /opt/dsh /var/log/dsh-r表示创建系统用户-s /usr/sbin/nologin让这个账号不能直接登录 shell只能用于运行服务。目录规划上我习惯把代码放在/opt/dsh/src日志单独放/var/log/dsh这样以后备份或日志轮转都非常清楚。2.2 安装 Git、Python 和 Node 工具链dsh 这类项目典型结构是“Python 后端 前端 Node 构建”所以三样东西必不可少sudo apt install -y git curl wget build-essential python3 python3-venv python3-pipNode 版本是源码部署里最容易翻车的一环。Ubuntu 自带的 nodejs 版本通常偏旧而前端依赖常常要求 Node 18 或更高。我的做法是用 nvm 管理避免影响系统其他项目的 Node 环境su - dsh curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.40.1/install.sh | bash source ~/.bashrc nvm install 20 node -v npm -v这里先切到 dsh 用户再装 nvm确保 nvm 的配置写到 dsh 用户的家目录里不会跟 root 的全局环境混在一起。2.3 为什么不让 root 直接跑服务有些教程为了省事直接在 root 下启动 Web 服务这个习惯在个人实验环境可以接受但放到正式环境风险很大。前端构建工具链里包含了大量第三方脚本任何一个依赖包出问题都可能执行任意命令后端进程如果以 root 运行一旦 Web 层被利用攻击者直接拿到整台服务器的最高权限。我用 dsh 用户跑服务后实际遇到的一个小麻烦是日志目录权限不对服务启动时报Permission denied。解决方式很简单运行前确认目录归属sudo chown -R dsh:dsh /opt/dsh /var/log/dsh权限问题宁可提前多花一分钟也不要半夜收到告警再爬起来处理。3. 拉取源码、安装依赖并完成前端构建环境准备好后才进入真正的“源码部署”环节。这里最常见的错误是没看清仓库结构就乱装依赖。3.1 克隆代码并识别目录结构以你拿到的 dsh 仓库地址为准克隆到/opt/dsh/srccd /opt/dsh git clone 你的 dsh 仓库地址 src cd src克隆完第一件事不是装依赖而是看目录结构。典型结构通常长这样/opt/dsh/src ├── server/ # Python/FastAPI 后端入口 ├── web/ # 前端项目目录 ├── docs/ # 项目文档 ├── requirements.txt # 后端依赖清单 ├── pyproject.toml # 或使用这个 └── README.md我在不止一个项目上栽过跟头直接跑到根目录执行pip install结果把前端依赖当 Python 包装了或者跑到server/里执行npm install然后对着不存在的node_modules发呆。先花两分钟读 README 和目录结构比什么技巧都管用。3.2 后端依赖装进虚拟环境不要装进全局Python 项目强烈建议使用虚拟环境避免污染系统 Python也避免不同项目间的依赖冲突cd /opt/dsh/src python3 -m venv .venv source .venv/bin/activate # 根据项目实际提供的依赖清单选择一条 pip install -r requirements.txt # 或者如果项目使用 pyproject.toml pip install -e .依赖安装过程中最常见的问题是某个包编译失败报gcc error或missing Python.h。这通常是因为系统缺少编译工具链回到上面第 2 节的build-essential装一遍就好。另一个高频问题是 pip 版本太旧先升级一下python3 -m pip install --upgrade pip3.3 前端依赖安装与构建产物前端部分一般在web/目录下。先看有没有package-lock.json有的话用npm ci替代npm install前者会严格按锁定版本安装更可控cd /opt/dsh/src/web npm ci npm run build构建完成后前端产物会被输出到dist/或类似目录以项目配置为准。构建成功不等于部署成功我习惯顺手确认一下构建产物确实存在ls -la /opt/dsh/src/web/dist如果这个目录是空的后续 Web 服务启动后访问页面只会看到空白页或 404。3.4 环境变量配置模型密钥、监听地址和服务端口dsh 服务需要通过环境变量读取模型配置。一般项目会提供一个.env.example作为模板把它复制成.env再改cd /opt/dsh/src cp .env.example .env一个典型的配置长这样具体字段名以项目文档为准DSH_HOST127.0.0.1 DSH_PORT8000 MODEL_API_BASEhttps://api.deepseek.com/v1 MODEL_API_KEYsk-xxxxxxxxxxxxxxxx MODEL_NAMEdeepseek-chat DSH_LOG_LEVELinfo这里重点注意DSH_HOST127.0.0.1。很多新手会把它改成0.0.0.0觉得这样外部就能访问了。这在公网环境是很危险的做法——服务完全没有鉴权就直接暴露给全网。我后面会在第 5 节说明正确姿势应该让服务只监听本机由 Nginx 在对外层做流量转发。4. 本地跑通服务调试启动、健康检查与开机自启这一步的目标很明确先在服务器本机上把服务跑起来确认没有环境问题再去碰远程访问。顺序不要乱否则你根本分不清问题是出在服务本身还是网络链路上。4.1 第一阶段前台跑通 Web 服务进入虚拟环境用前台方式启动服务。具体入口命令以项目 README 为准常见写法类似cd /opt/dsh/src source .venv/bin/activate python -m dsh_harness.web --host 127.0.0.1 --port 8000如果是第一次启动注意观察启动日志。正常情况应该能看到类似Uvicorn running on http://127.0.0.1:8000的输出。如果这里报模块找不到通常是虚拟环境没激活或者项目入口路径不是你猜的这个回去看 README不要硬猜。4.2 健康检查用 curl 确认服务真的起来了前台启动后另开一个终端窗口做健康检查curl -i http://127.0.0.1:8000期望看到 HTTP 状态码 200以及正常的 HTML 或 JSON 响应。如果 curl 卡住不动说明进程可能没真正监听该端口如果返回拒绝连接说明启动时可能没加载.env或绑定地址不对。这一步在远程访问配置前做能帮你把问题范围缩小到“服务本身”。4.3 设置 systemd 服务日志、自动重启、开机自启前台验证没问题后杀掉进程改由 systemd 守护。新建服务单元文件sudo tee /etc/systemd/system/dsh-web.service /dev/null EOF [Unit] DescriptionDeepSeek Harness Web Service Afternetwork-online.target Wantsnetwork-online.target [Service] Userdsh Groupdsh WorkingDirectory/opt/dsh/src EnvironmentFile/opt/dsh/src/.env ExecStart/opt/dsh/src/.venv/bin/python -m dsh_harness.web --host 127.0.0.1 --port 8000 Restarton-failure RestartSec5 NoNewPrivilegestrue PrivateTmptrue [Install] WantedBymulti-user.target EOF让 systemd 生效并查看状态sudo systemctl daemon-reload sudo systemctl enable --now dsh-web sudo systemctl status dsh-web这里有几个细节值得说。第一ExecStart里必须写成虚拟环境中 Python 的绝对路径不要写裸的python否则 systemd 启动时可能跑到系统 Python导致环境变量和依赖全部错乱。第二EnvironmentFile指向/opt/dsh/src/.env这样服务启动时自动加载模型配置。第三Restarton-failure让服务崩溃后自动拉起配合RestartSec5防止疯狂重启。查看日志用这条命令比看任何面板都直接journalctl -u dsh-web -f5. 从“本机可访问”到“远程浏览器可访问”Nginx 流量转发与 HTTPS这是标题里“远程访问”的核心环节。我不推荐直接把 8000 端口映射到公网而是让 dsh 只监听本机由 Nginx 作为公网入口把请求转发给 dsh。这样做的好处是对外只需要开放 80/443 端口攻击面小得多Nginx 还能顺手处理 HTTPS 证书、流量缓冲和超时策略。5.1 先梳理网络边界绑定地址和防火墙记住一条原则服务器上凡是能被公网扫到的端口都应该经过你深思熟虑的开放。dsh 服务本身绑定 127.0.0.1:8000所以公网根本接触不到这个端口能接触到的只有 Nginx 监听的 80/443。用系统自带的 ufw 管理防火墙sudo apt install -y ufw sudo ufw allow 22/tcp sudo ufw allow 80/tcp sudo ufw allow 443/tcp sudo ufw enable22 端口是提供给 SSH 管理的必须保留80/443 是 Nginx 对外入口。8000 端口不需要在防火墙里开放因为 dsh 只监听本机回环地址。5.2 用 Nginx 把对外请求转发到 dsh安装 Nginxsudo apt install -y nginx创建站点配置文件/etc/nginx/sites-available/dshserver { listen 80; server_name dsh.example.com; location / { proxy_pass http://127.0.0.1:8000; proxy_http_version 1.1; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto $scheme; # 流式对话场景需要关闭缓冲并调大超时 proxy_buffering off; proxy_read_timeout 3600s; } }建立软链接并重载sudo ln -s /etc/nginx/sites-available/dsh /etc/nginx/sites-enabled/ sudo nginx -t sudo systemctl reload nginxproxy_buffering off这一行很多人会忽略但它在大模型流式输出场景下非常重要。如果开着缓冲用户可能一等几十秒才能看到页面上蹦出第一个字体验极差。proxy_read_timeout也要设大一点模型推理慢的时候Nginx 默认 60 秒超时会直接断掉长连接。5.3 绑定域名并配置 HTTPS 证书访问入口用 IP 还是域名我的建议是尽早绑域名。IP 地址除了难记还有一个实际问题如果以后服务器迁移IP 变了所有用户收藏夹里的地址就全废了。域名配合 HTTPS 证书才是长期可维护的方案。用 certbot 自动申请和续期证书sudo apt install -y certbot python3-certbot-nginx sudo certbot --nginx -d dsh.example.com按提示完成即可。certbot 会自动修改 Nginx 配置把 80 端口流量重定向到 HTTPS并配置自动续期任务。证书快到期时系统会提醒你不需要人工干预。5.4 没有公网域名临时访问可以试试 SSH 端口转发有些场景下你只是想临时从自己的电脑看一眼服务状态或者服务器在局域网里还没配域名此时 SSH 端口转发是最快的方式# 在本地电脑执行不是服务器 ssh -N -L 8080:127.0.0.1:8000 dsh服务器IP然后本地浏览器访问http://localhost:8080流量会通过 SSH 加密通道转发到服务器的 127.0.0.1:8000。这个方式的好处是不需要额外开放任何端口特别适合排查 Nginx 配置出错时的备用通道。但它只适合临时使用长期访问还是要回到 Nginx 域名 HTTPS 的正规方案。6. 部署后必查的五类问题与排障思路源码部署项目从“能跑”到“稳定跑”之间隔着一堆实际问题。下面这五类是我部署 dsh 后真实遇到过、也被问过多次的问题直接做成排查表给你。现象可能原因排查命令 / 做法外部浏览器打不开页面systemd 没起来 / 防火墙没放行systemctl status dsh-web、ss -ltnp、ufw status页面能开但接口报 502Nginx 转发目标错误 / 后端没监听journalctl -u dsh-web、curl http://127.0.0.1:8000页面白屏 / 静态资源 404前端构建产物缺失ls web/dist、重新npm run build模型请求 401API Key 错误 / 模型地址配置错检查.env的MODEL_API_KEY和MODEL_API_BASE长时间无响应后断连Nginx 超时 / 缓冲未关proxy_read_timeout调大、proxy_buffering off6.1 外部访问不通先分清是哪一层的问题外部打不开时不要直接怀疑 dsh 进程。按链路一层层排查先看进程状态再看监听地址再看防火墙最后看 Nginx 配置。我经常用的快速定位命令systemctl status dsh-web ss -ltnp | grep 8000 curl -i http://127.0.0.1:8000 curl -i http://127.0.0.1/ # 测试 Nginx 是否正常如果本机 curl 正常、外部不行十有八九是防火墙或云平台安全组规则问题。如果在云服务商买过服务器除了服务器内部 ufw还要记得检查控制台上的安全组/防火墙规则是否放行了 80/443。6.2 页面白屏但接口 200问题多半在前端构建前端页面白屏但后端接口返回正常是特别容易误判的问题。我第一次遇到时以为是后端跨域问题查了半天才发现是前端构建产物是旧的。源码部署和本地开发有个重要区别本地开发时前端用 dev server 自动加载最新代码部署后你要手动npm run build让浏览器能拿到最新的 JS/CSS 文件。构建完成后在浏览器开发者工具里看 Network如果静态资源请求 404检查 Nginx 是否把静态文件路径指到了正确目录。6.3 模型请求 401 或超时环境变量在整条链路里保持一致性模型接口 401先别急着怀疑模型服务商回到.env里逐项核对MODEL_API_BASE是否填了正确的接口地址注意不要带多余的斜杠MODEL_API_KEY是否有多余空格或引号环境变量是否真的被 systemd 加载了用systemctl cat dsh-web可以回看EnvironmentFile的路径。改完.env后一定要执行sudo systemctl daemon-reload sudo systemctl restart dsh-web只重载不重启或者只改文件不daemon-reload都会让你误以为“我改了啊怎么还报错”。这个坑我至少踩了三次。6.4 流式输出卡顿或中断Nginx 对长连接不友好dsh Web 页面和模型对话时通常需要流式返回如果 Nginx 配置里开着默认缓冲或者超时时间太短就会出现“等了很久没反应”或“输出到一半断掉”。我的建议是只要项目涉及模型流式回复就在 Nginx 配置里显式加上proxy_buffering off和足够大的proxy_read_timeout。6.5 插件安装问题Web 版装插件后要重启服务进程好多人问 dsh 的 Web 版怎么装插件。我了解的模式基本是插件包放到项目指定目录或者在管理界面里上传安装但安装完成后往往不会立刻生效需要重启后端进程才能加载sudo systemctl restart dsh-web journalctl -u dsh-web -n 50如果插件还是没出现在页面上先看日志有没有加载失败的原因多半是依赖缺失或目录权限不对。插件这类功能最容易出现“自己机器上好好的、服务器上就不行”大概率不是代码问题而是服务器环境少了插件依赖的某个系统库。7. 我自己的维护习惯升级、备份与最小权限到这里部署链路已经完整了。最后分享几个我实际维护过程中沉淀下来的习惯不一定写在官方文档里但非常有用。第一源码部署的项目一定要养成“升级五连”的习惯。每次从上游拉新代码我都会按固定顺序执行git pull source .venv/bin/activate pip install -r requirements.txt # 有依赖变化才需要 cd web npm ci npm run build # 前端有变化才需要 cd .. sudo systemctl restart dsh-web sudo journalctl -u dsh-web -n 100 # 确认启动日志正常第二.env里是 API Key务必做好备份和权限控制。我在/opt/dsh/src/下执行过sudo chmod 600 /opt/dsh/src/.env sudo chown dsh:dsh /opt/dsh/src/.env这一行可能救过我不止一次——之前因为粗心给.env留了 644 权限结果同一台服务器上其他账号都能读走模型 API Key。第三开机自启在真实服务器上远比想象中重要。我遇到过服务器重启后Nginx 起来了dsh 却因为依赖的网络服务没就绪而启动失败。所以 systemd 单元文件里Afternetwork-online.target和Wantsnetwork-online.target两行别删它们是保证“开机后服务一定起来”的关键。最后关于“远程访问”我个人的最终建议是即便用 Nginx 做了转发也不要掉以轻心。dsh Web 如果暴露到公网最好再加上访问认证层或者限制来源 IP毕竟这类工作流工具背后往往连着模型 API 和大量内部数据多一层防护就少一分风险。希望这篇从零到远程访问的完整过程能帮你少走几个来回。