ARTICLE · INTELLIGENCE

战地情报 · 详情页

来自尧图项目组的一线实战观察与深度解析

Linux源码部署DeepSeek Harness Web:从环境准备到systemd服务守护

Linux源码部署DeepSeek Harness Web:从环境准备到systemd服务守护 1. 为什么要在 Linux 上源码部署 DeepSeek Harness Web把 DeepSeek Harness Web 跑在自己的 Linux 服务器上最直接的动机有三个数据不出内网、推理链路完全可控、以及可以按自己的硬件条件做定制。很多人第一反应是用容器一键拉起来省事是省事但一旦要改推理参数、换模型权重、调并发策略容器那层封装反而成了障碍。源码部署的好处就在这儿——每一行配置你都能看到、能改、能回滚。DeepSeek Harness Web 本质上是一个把模型推理能力包装成 Web 服务的中间层。它对外暴露 HTTP 接口对内负责加载模型、管理会话上下文、处理流式输出。源码部署意味着你要自己搞定 Python 环境、依赖编译、模型文件放置、服务守护这几件事。听起来繁琐但走通一遍之后你对整个推理服务的理解会上一个台阶。这篇内容适合三类人一是手里有台闲置 Linux 服务器、想把本地大模型能力开放给团队用的运维二是需要在内网环境做模型服务、对数据流向有硬性要求的开发者三是想搞清楚一个 Web 推理服务到底由哪些部件组成的技术爱好者。不管你用的是 Rocky 9、Debian 13 还是 Ubuntu核心流程是通的差异只在包管理命令上。我这次用的环境是一台 8 核 32G 内存、带一张 24G 显存显卡的机器系统是 Rocky 9。选 Rocky 而不是 Ubuntu主要是因为它默认的 systemd 版本较新后面做服务守护时少踩一些坑。下面从环境准备开始一步步把整个链路搭起来。2. 部署前的环境盘点与依赖决策2.1 硬件与系统版本的最低要求DeepSeek Harness Web 对硬件的要求取决于你要跑多大的模型。如果只是跑 7B 级别的量化模型16G 内存加一张 12G 显存的卡就能转起来如果要跑 32B 甚至更大的模型内存建议 64G 起步显存按模型参数量乘以 2 字节粗算再留 20% 余量给 KV Cache。系统层面内核版本建议 5.10 以上原因是较新的内核在显存管理和进程调度上更稳定。我用uname -r确认了一下Rocky 9 默认是 5.14 系列够用。另外确认一下glibc版本ldd --version看一眼低于 2.28 的话某些预编译的推理库可能加载不了。uname -r ldd --version | head -1 nvidia-smi free -h df -h /opt这几条命令分别看内核、glibc、显卡驱动、内存和磁盘。磁盘这块特别提醒一句模型文件动辄几十 G/opt分区如果只有 50G装到一半就爆了。我习惯把模型统一放在/data/models下单独挂一块大盘。2.2 Python 环境用系统自带还是自己编译这是第一个容易纠结的点。Rocky 9 自带的 Python 是 3.9而 DeepSeek Harness Web 的部分依赖要求 3.10 以上。你有两个选择一是用dnf装python3.11二是用 pyenv 自己编译一个。我的建议是优先用系统包管理器装。原因很简单自己编译的 Python 在后续装某些需要编译 C 扩展的包时头文件路径容易出问题排查起来费时间。Rocky 9 装 Python 3.11 的命令是dnf install -y python3.11 python3.11-devel python3.11-pip装完之后python3.11 --version确认一下。这里有个细节不要试图把系统默认的python3软链到 3.11因为系统里有些工具依赖 3.9改了会出乱子。正确做法是显式用python3.11调用或者在虚拟环境里操作。虚拟环境我强烈建议用venv而不是 conda。conda 虽然省事但它会往环境里塞一堆自己的库和系统库混在一起时LD_LIBRARY_PATH的优先级问题能让你调半天。venv干净出问题好定位。python3.11 -m venv /opt/deepseek-harness/venv source /opt/deepseek-harness/venv/bin/activate pip install --upgrade pip setuptools wheel2.3 显卡驱动与 CUDA 运行时的匹配逻辑显卡驱动和 CUDA 版本不匹配是新手最容易卡住的地方。记住一个原则驱动版本决定你能用的 CUDA 上限而不是反过来。nvidia-smi右上角显示的CUDA Version是驱动支持的最高版本你实际装的 CUDA Toolkit 只要不超过这个数就行。比如nvidia-smi显示CUDA Version: 12.4那你可以装 12.1、12.2、12.3、12.4但不能装 12.5。装 CUDA Toolkit 的时候如果只是跑推理其实不需要装完整的 Toolkit装cuda-runtime就够了体积小很多。dnf install -y cuda-runtime-12-4装完确认一下nvcc --version如果nvcc找不到说明只装了 runtime 没装 compiler这对纯推理场景是正常的不用慌。Python 侧的 PyTorch 会自带对应的 CUDA 库只要驱动版本够就行。3. 源码拉取与依赖安装的实操细节3.1 获取源码与目录结构规划源码获取方式取决于你的网络环境。如果服务器能直连代码托管平台直接git clone就行如果是内网机器就先把压缩包传上去再解压。我习惯把项目放在/opt/deepseek-harness模型放/data/models日志放/var/log/deepseek-harness三个目录分开后面做权限和备份都清晰。mkdir -p /opt/deepseek-harness mkdir -p /data/models mkdir -p /var/log/deepseek-harness cd /opt/deepseek-harness git clone 项目仓库地址 .拉下来之后先别急着装依赖花两分钟看一眼目录结构。重点看三个东西requirements.txt或pyproject.toml依赖清单、config目录配置文件、README里的启动命令。这一步能帮你预判后面可能遇到的问题。3.2 依赖安装中的编译陷阱依赖安装是源码部署里最耗时的环节也是最容易出错的。核心坑点在于有些包需要编译 C 扩展而编译需要系统里装好对应的开发库。dnf install -y gcc gcc-c make cmake dnf install -y openssl-devel bzip2-devel libffi-devel zlib-devel这几个开发库是基础缺了会在pip install时报找不到头文件之类的错。装完再进虚拟环境装依赖source /opt/deepseek-harness/venv/bin/activate pip install -r requirements.txt如果requirements.txt里锁定了 PyTorch 的版本注意看它是不是带cu124这种后缀。带后缀的版本需要从 PyTorch 官方源装直接pip install torch可能装到 CPU 版本跑起来慢得让你怀疑人生。确认方法python -c import torch; print(torch.cuda.is_available())返回True才算对。返回False的话要么是驱动问题要么是装成了 CPU 版。3.3 模型文件的放置与校验模型文件通常从模型托管平台下载格式可能是.safetensors或.bin。下载完之后一定要校验文件完整性否则加载到一半报错你还以为是代码问题。cd /data/models sha256sum deepseek-model.safetensors把结果和官方提供的哈希值对一下。对不上就重新下别抱侥幸心理。模型目录的结构一般是这样的/data/models/deepseek/ ├── config.json ├── tokenizer.json ├── model-00001-of-00002.safetensors └── model-00002-of-00002.safetensorsconfig.json里记录了模型的层数、隐藏维度、最大上下文长度这些关键参数Harness Web 启动时会读它。如果这个文件缺失或格式不对服务起不来。4. 配置文件的关键参数怎么定4.1 服务监听地址与端口的选择配置文件里第一个要改的是监听地址。默认可能是127.0.0.1这意味着只有本机能访问。如果你要让局域网内其他机器访问得改成0.0.0.0。但改成0.0.0.0之前先想清楚安全边界——这台机器是不是只在内网有没有防火墙server: host: 0.0.0.0 port: 8080 workers: 2workers这个参数控制并发处理的进程数。设成 1 的话同一时刻只能处理一个请求第二个请求得排队。设成 2 能同时处理两个但显存占用也会翻倍。我的经验是显存够就设 2不够就老实设 1别硬撑。4.2 模型加载参数与显存占用的关系模型加载这块几个参数直接决定显存占用和推理速度参数作用建议值dtype权重精度float16或bfloat16max_model_len最大上下文长度按实际需求别盲目拉满gpu_memory_utilization显存占用比例0.85 到 0.9tensor_parallel_size张量并行卡数单卡填 1max_model_len设太大KV Cache 会吃掉大量显存。比如你实际对话很少超过 4096 token就没必要设成 32768。gpu_memory_utilization设成 0.9 意味着留 10% 显存给系统和其他进程设成 1.0 容易 OOM。4.3 日志级别与输出路径日志配置容易被忽略但出问题时它是你唯一的线索。建议把日志级别设成INFO调试阶段可以临时开DEBUG但生产环境别开日志量太大会拖慢服务。logging: level: INFO file: /var/log/deepseek-harness/app.log max_size: 100MB backup_count: 5max_size和backup_count配合做日志轮转避免日志文件把磁盘写满。这个细节很多人不设跑几个月后磁盘告警才发现。5. 用 systemd 把服务管起来5.1 为什么不用 nohup 或 screennohup和screen能让你在断开 SSH 后保持进程运行但它们有个共同问题机器重启后服务不会自动起来而且进程挂了也没人拉。systemd 解决的就是这两个问题——开机自启、崩溃重启、日志统一管理。写一个 systemd unit 文件放在/etc/systemd/system/deepseek-harness.service[Unit] DescriptionDeepSeek Harness Web Service Afternetwork.target [Service] Typesimple Userdeepseek Groupdeepseek WorkingDirectory/opt/deepseek-harness EnvironmentPATH/opt/deepseek-harness/venv/bin:/usr/local/bin:/usr/bin ExecStart/opt/deepseek-harness/venv/bin/python -m harness_web --config /opt/deepseek-harness/config/prod.yaml Restarton-failure RestartSec10 StandardOutputappend:/var/log/deepseek-harness/stdout.log StandardErrorappend:/var/log/deepseek-harness/stderr.log [Install] WantedBymulti-user.target5.2 ExecStart 命令的写法与常见错误ExecStart这行是 unit 文件的核心。几个要点第一用绝对路径。python要写成/opt/deepseek-harness/venv/bin/python不能只写python因为 systemd 不读你的 shell 环境变量。第二Environment里显式声明PATH。有些依赖会在运行时调用系统命令PATH不对就找不到。第三如果启动命令里有管道或重定向systemd 不认得用bash -c ...包一层。但更推荐的做法是把复杂逻辑写成一个启动脚本ExecStart直接调脚本。#!/bin/bash source /opt/deepseek-harness/venv/bin/activate exec python -m harness_web --config /opt/deepseek-harness/config/prod.yaml5.3 服务账户与权限隔离别用 root 跑服务。创建一个专用账户useradd -r -s /sbin/nologin deepseek chown -R deepseek:deepseek /opt/deepseek-harness chown -R deepseek:deepseek /var/log/deepseek-harness chown -R deepseek:deepseek /data/models-r表示创建系统账户-s /sbin/nologin表示这个账户不能登录 shell。这样即使服务被攻破攻击者拿到的也只是一个没有登录权限的低权限账户。5.4 启动、验证与开机自启unit 文件写好后按顺序执行systemctl daemon-reload systemctl enable deepseek-harness systemctl start deepseek-harness systemctl status deepseek-harnessdaemon-reload是必须的改了 unit 文件不 reloadsystemd 读的还是旧配置。status看到active (running)就说明起来了。如果显示failed用journalctl -u deepseek-harness -n 50看最近 50 行日志错误信息一般都在里面。6. 远程访问的打通与安全边界6.1 防火墙放行与端口确认服务在0.0.0.0:8080监听了但防火墙没放行的话外部还是访问不了。Rocky 9 默认用firewalldfirewall-cmd --permanent --add-port8080/tcp firewall-cmd --reload firewall-cmd --list-ports确认端口放行后在本机用curl测一下curl http://127.0.0.1:8080/health返回{status:ok}之类的响应就说明服务本身没问题。然后再从另一台机器测curl http://服务器IP:8080/health如果本机能通、外部不通问题就在防火墙或网络策略上。6.2 反向代理与访问控制直接把推理服务的端口暴露出去不是个好主意。更稳妥的做法是前面挂一个反向代理由代理层做访问控制。Nginx 配置示例server { listen 443 ssl; server_name your-domain.com; ssl_certificate /etc/nginx/ssl/cert.pem; ssl_certificate_key /etc/nginx/ssl/key.pem; location / { proxy_pass http://127.0.0.1:8080; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_read_timeout 300s; } }proxy_read_timeout要设大一点因为模型推理是流式输出一个请求可能持续几十秒。默认的 60 秒会导致长回答被截断。6.3 访问日志与异常排查远程访问出问题时排查顺序是先看服务日志再看代理日志最后看网络层。服务日志在/var/log/deepseek-harness/代理日志在/var/log/nginx/。一个常见现象是请求发出去了服务也收到了但客户端一直转圈没响应。这通常是流式输出的缓冲问题。Nginx 默认会缓冲响应导致流式数据攒够一块才发。解决办法是在location里加proxy_buffering off;这个参数一加数据就能实时推给客户端了。7. 踩过的坑与排查链路复盘7.1 服务启动即退出从日志定位到根因第一次启动时systemctl status显示failedjournalctl里只有一行ModuleNotFoundError: No module named harness_web。这个错误说明 Python 找不到模块。原因是我在ExecStart里写的python -m harness_web但项目实际的模块名不是这个。排查方法进虚拟环境pip list看装了什么包找到实际的包名。改对之后服务就起来了。这个坑的教训是别猜模块名用pip list确认。7.2 显存不足导致的加载失败第二次启动服务起来了但加载模型时报CUDA out of memory。用nvidia-smi一看显存被另一个进程占了一半。fuser -v /dev/nvidia*找到占用进程确认是之前测试留下的僵尸进程kill掉之后重新启动就正常了。这个坑提醒我部署前先nvidia-smi确认显存是干净的。如果机器上有其他人共用最好约定好显存使用。7.3 远程访问超时的三层排查远程访问超时是最常见的求助场景。我的排查链路是三层第一层服务本身。curl 127.0.0.1:8080/health通不通不通就是服务问题看服务日志。第二层防火墙。firewall-cmd --list-ports看端口放行没有没放行就加。第三层网络。从客户端telnet 服务器IP 8080看能不能建连建连失败就是网络层的事可能是路由、可能是安全组。按这个顺序走90% 的远程访问问题都能定位到。7.4 日志文件权限导致的静默失败有一次服务启动后status显示running但日志文件是空的接口也不响应。查了半天发现是日志目录的属主是 root而服务以deepseek账户运行写不进去日志程序在初始化日志时就卡住了。ls -ld /var/log/deepseek-harness chown -R deepseek:deepseek /var/log/deepseek-harness改完属主重启服务就正常了。这个坑的教训是服务账户对涉及的所有目录都要有写权限包括日志、缓存、临时文件目录。8. 性能调优与日常维护的几个抓手8.1 并发数与显存的平衡workers设成几取决于你的显存和请求量。一个粗略的估算方法单次推理的显存占用大约是模型权重的 1.2 倍含 KV Cache如果模型占 20G那 24G 的卡只能跑一个 worker。想跑两个 worker要么换更大的卡要么用量化把模型压小。nvidia-smi --query-gpumemory.used,memory.total --formatcsv这条命令能快速看显存使用情况。调优时一边加 worker 一边看显存找到不 OOM 的最大值。8.2 日志轮转与磁盘监控日志不轮转磁盘迟早满。除了在应用层配置轮转系统层也可以用logrotate兜底/var/log/deepseek-harness/*.log { daily rotate 7 compress missingok notifempty }放在/etc/logrotate.d/deepseek-harness每天轮转一次保留 7 天。配合一个简单的磁盘监控脚本超过 80% 就告警。8.3 服务健康检查与自动重启systemd 的Restarton-failure能在进程崩溃时自动拉起但如果进程没崩、只是卡死了systemd 不会管。这种情况可以加一个健康检查定时任务*/5 * * * * curl -sf http://127.0.0.1:8080/health || systemctl restart deepseek-harness每 5 分钟检查一次健康检查失败就重启服务。这个兜底机制在无人值守的环境里特别有用。8.4 模型更新时的平滑切换模型更新时直接覆盖文件再重启服务会导致短暂不可用。更平滑的做法是新模型放新目录改配置指向新目录然后systemctl reload或restart。如果服务支持热加载那就更好了。不支持的话重启的几秒到几十秒中断得提前和用户打招呼。ln -sfn /data/models/deepseek-v2 /data/models/deepseek-current用软链接指向当前模型更新时只改软链接指向配置里写软链接路径这样切换时不用改配置文件。9. 我在这套流程里攒下的几条经验源码部署 DeepSeek Harness Web 这件事第一次做会觉得步骤多、坑也多但走通一遍之后你会发现每个环节都有它的道理。环境准备阶段的谨慎能省掉后面 80% 的排查时间systemd 配置的规范能让服务在无人值守时也稳如老狗远程访问的三层排查法是我处理过几十次类似问题后总结出的最快路径。有几个习惯我强烈建议你养成每次改配置前先备份cp config.yaml config.yaml.bak这条命令花不了两秒但能救命每次重启服务后先看status再看日志别假设它一定起来了模型文件下载后一定校验哈希别省这一步。最后说一个容易被忽略的点文档化。把你这次部署用到的命令、改过的配置、遇到的错误和解决办法记下来放在项目目录的DEPLOY.md里。下次换机器部署或者同事接手这份文档的价值比你想象的大得多。我自己就是靠这份习惯把一次部署的时间从半天压缩到了一个小时以内。
RELATED READING

延伸阅读

更多一线实战笔记与深度复盘,助您持续精进