简介:面向需要在 Linux 环境中手动部署 DeepSeek 的深度学习开发者与运维人员,这份 PDF 教程按部署阶段系统梳理了从环境准备到模型运行的全流程操作。文档从硬件与系统要求讲起,涵盖 CUDA/cuDNN 安装配置、Python 虚拟环境创建、代码库克隆、依赖包安装、config.yaml 配置调整,以及训练与推理命令的执行方式,各步骤均配有可直接参照的命令示例。针对部署中常见的依赖包冲突、GPU 未被检测、内存不足和文件权限等典型问题,文中亦提供了具体排查思路与解决建议,帮助读者避开常见陷阱。资源为单个 PDF 文件,大小 215KB,内容紧凑、步骤清晰,既适合初次接触 DeepSeek 部署的新手按图索骥,也可作为已有一定基础者的速查参考。目前已吸引 794 人浏览学习,对于计划在 Linux 下落地 DeepSeek 的用户具有实用参考价值。
1. 手动部署 DeepSeek 在 Linux 上的真实工作量:为什么值得自己动手
这个标题背后的需求其实很明确:你要在一台 Linux 服务器上,不靠 Docker 一键脚本,而是自己把 DeepSeek 的权重文件、推理框架、依赖环境一层层装起来,最终跑出一个可以被外部调用的模型服务。手动部署的价值在于你能看清每个环节——显存怎么分配、并发怎么控制、量化到什么程度,出了问题你知道去哪查,而不是面对一个黑匣子。适合有 Linux 基础、手里有 GPU 资源、需要在内网或定制环境里部署大模型的工程师。接下来我从环境准备讲起,一直讲到服务化调优和验证。
2. 部署前的硬件与环境盘点:显卡、驱动、CUDA 与 Python 虚拟环境
2.1 显存与模型规模匹配:先算清楚你能跑多大的模型
手动部署 DeepSeek 的第一件事不是装软件,而是搞清硬件上限。模型权重有多大会直接决定你有没有戏。如果你用的是 DeepSeek-R1 蒸馏版(比如 1.5B、7B、14B、32B),显存需求大致按参数规模成比例增长;如果你要部署的是 671B 的 DeepSeek-V3 完整版,FP16 权重就需要 1.3TB 显存,这不是单机八卡能轻松搞定的事,通常要配合量化、张量并行和 CPU offload。先看一张常用的估算表:
| 模型规模 | FP16 权重显存 | INT8 估算 | INT4 估算 | 推荐最低单卡配置 |
|---|---|---|---|---|
| 1.5B | 约 3GB | 约 2GB | 约 1.5GB | 12GB 消费卡 |
| 7B | 约 14GB | 约 8GB | 约 5GB | 24GB 消费卡 |
| 14B | 约 28GB | 约 16GB | 约 10GB | 40GB 数据中心卡 |
| 32B | 约 64GB | 约 34GB | 约 20GB | 80GB 单卡或双卡 |
注意,这里的显存估算只是权重本身,推理过程的 KV cache、激活值、框架自身的临时缓冲还会再吃掉一部分。我的经验是:按表格估算后,再留出 20% 到 30% 的余量,否则很容易在跑几个并发请求之后把显存打满。这也是为什么很多人会故意选择 INT4 或 INT8 量化版本来换余量。
如果你只有一张 24GB 的消费级显卡,最务实的选型是 7B 或 14B 的蒸馏模型,配合 AWQ 或 GPTQ 的 INT4 量化。如果公司有 A100 或 H 系列,32B 量化版是性价比很高的档位,代码、逻辑、中文能力都够用,而且单机就能跑起来,不需要上多机分布式,省掉一大堆跨机通信的配置工作。小模型跑业务场景未必不够,关键是匹配你的任务难度和并发诉求。
2.2 驱动与 CUDA:先把 GPU 状态查清楚
很多部署翻车不是模型的问题,而是驱动和 CUDA 版本不匹配。我见过不少人照着教程装完 vLLM,一启动就报CUDA driver version is insufficient,其实就是驱动太老或者 PyTorch 的 CUDA 版本和驱动对不上。在干净的系统上,我会按这个顺序确认:
# 查看 GPU 型号与当前驱动版本 nvidia-smi # 查看 PyTorch 实际使用的 CUDA 版本 python -c "import torch; print(torch.version.cuda)" # 查看驱动支持的最高 CUDA 版本 nvidia-smi | grep "CUDA Version"第一行看显卡有没有被系统识别;第三行nvidia-smi输出的右上角 CUDA Version 是驱动支持的最高 CUDA 版本号,不是当前正在使用的版本。PyTorch 编译时的 CUDA 版本只要不高于这个数字就能跑起来。比如驱动支持 12.4,那 torch 用 cu121 或 cu124 都没问题;如果驱动停在 11.8,那 torch 只能装 cu118 及以下。装深度学习框架前先确认这一点,能省下来回卸载重装的几个小时。
如果驱动没装好,nvidia-smi会直接报错。Debian/Ubuntu 系的做法是装驱动后重启,再用nvidia-smi验证。这个步骤很基础,但我坚持要求项目里每个人都先跑这三行命令,因为手动部署的排查链条里,环境问题往往是最难一眼看破的。驱动版本、CUDA 版本、PyTorch 版本三者匹配,后面才谈得上模型推理。
2.3 用 conda 隔离部署环境:避免系统 Python 被搞乱
手动部署的另一个原则是:永远不要往系统 Python 里直接pip install。系统 Python 往往被系统的包管理工具占用,直接往里面装 PyTorch、vLLM 很容易把依赖搞乱,甚至破坏系统工具。我一般会为每个部署项目单独建一个 conda 环境,Python 版本固定,依赖隔离。
# 创建独立环境,建议 Python 用 3.11 conda create -n deepseek python=3.11 -y conda activate deepseek # 后续所有安装都发生在这个环境里,升级 pip 避免依赖解析问题 pip install --upgrade pip注意 conda 环境不是必需的,但强烈建议。手动部署的每一步都可能需要重装某个依赖,有了独立环境,重来的成本是一个conda env remove,而不是把服务器折腾一遍。Python 版本方面,3.11 在当前生态里兼容性很好;如果推理框架对 3.12 支持还不充分,3.11 是最稳的选择。另外,装完环境后建议顺手记录一下 Python 路径,后续写 systemd 服务时会用到。
3. 推理框架与模型文件选型:为什么我选 vLLM 而不是直接用 Transformers
3.1 Transformers 够用但不划算:速度与显存的关键差距
把 DeepSeek 跑起来最简单的方式是pip install transformers,然后写一段加载模型的脚本。但这个方案在真正的服务场景下不够用。Transformers 默认的推理模式是逐请求顺序处理,每个新的并发请求都要重新计算 KV cache,而且显存分配是一次性预留的,不会精细管理。结果就是:单请求吞吐还行,但并发 5 个请求时延迟会明显上升,显存也容易被浪费。
现在的常见选择有三个:vLLM、SGLang、llama.cpp。其中 vLLM 是大家用得最多的,核心优势是 PagedAttention,它把 KV cache 按页分配,像操作系统管理内存一样管理显存,加上 continuous batching(连续批处理),能把 GPU 的算力吃得更满。SGLang 在 DeepSeek 这类 MoE 模型上有专门的优化(比如 RadixAttention),如果你部署的是 671B 完整版,可以考虑;但单机跑 7B 或 32B 蒸馏模型,vLLM 最省心。llama.cpp 主要用在纯 CPU 或 Mac 场景,Linux + GPU 的话我不太推荐作为首选。
我手动部署时的默认选择就是 vLLM,因为它的启动命令简单、API 兼容 OpenAI 格式,而且遇到性能问题时能查到的资料最多。如果你的诉求只是本地快速验证模型输出质量,那用 Transformers 写个脚本也算合理,但一旦走到「对外提供服务」这一步,vLLM 几乎是必须的。注意 vLLM 对模型的支持和硬件要求有版本差异,安装前先看一眼它的文档,确认对应模型架构在支持列表里。
3.2 量化版本怎么选:FP16、INT8、INT4 与 AWQ
DeepSeek 官方发布的是 FP16/BF16 权重,你可以直接拿来用,但显存不够时就要上量化版本。量化有两种来源:一是社区做好的量化权重(比如 AWQ、GPTQ 格式),二是 vLLM 提供的在线量化(比如 INT8/FP8)。搞清楚每个格式的特点能避免选错:
| 格式 | 显存占用 | 推理速度 | 部署难度 | 适用场景 |
|---|---|---|---|---|
| FP16/BF16 原版权重 | 高 | 最快 | 低 | 显存充足的多卡环境 |
| AWQ INT4 | 约 25%-30% | 快 | 低 | 消费级显卡、单卡跑大模型 |
| GPTQ INT4 | 约 25%-30% | 快 | 中 | 老牌方案,生态成熟但兼容性要确认 |
| FP8 | 约 50% | 很快 | 中 | 支持 FP8 的数据中心显卡 |
我的原则是:消费级显卡(24GB 以下)优先用 AWQ INT4;数据中心显卡(A100/H 系列)优先原版 FP16/BF16,显存不够再考虑 FP8。无论哪种量化,都会带来一点质量损失——在代码生成、数学推理这类任务上,INT4 相对 FP16 的差距能感知到,但通常不致命。具体部署后我会用同一组测试问题对比量化前后的输出,这个验证步骤放在最后一章讲。
3.3 下载模型文件:从 ModelScope 或 HF 镜像拉取,先确认目录结构
模型文件一般放在 Hugging Face 或 ModelScope。手动部署时,如果直接访问 Hugging Face 不稳,常见的做法是走 ModelScope,它在国内访问稳定,而且命令行工具支持指定本地目录下载。HF 官方镜像 hf-mirror 也可以用,需要设置环境变量HF_ENDPOINT:
# 方式一:modelscope 工具下载(国内网络环境更稳) pip install modelscope modelscope download --model deepseek-ai/DeepSeek-R1-Distill-Qwen-7B --local_dir ./deepseek-7b # 方式二:HF 镜像下载 export HF_ENDPOINT=https://hf-mirror.com huggingface-cli download deepseek-ai/DeepSeek-R1-Distill-Qwen-7B --local-dir ./deepseek-7b下载完成后,--local_dir指定目录里应该能看到config.json、model.safetensors.index.json,以及若干个.safetensors分片文件。这个目录结构在后续启动 vLLM 时会直接用,所以建议养成一个习惯:不同模型分目录存放,目录名包含模型名和量化格式,避免之后混淆。
这里有个关键坑:大模型权重文件通常有多个分片,如果用 git 方式下载且没装 git-lfs,会下载到一堆 LFS 指针小文件而不是真正的权重。手动部署时,如果发现权重文件只有几 KB 到几百 KB,那就是 LFS 没处理好。用modelscope工具下载不会有这个问题,它内部已经处理了 LFS 逻辑。下载完成后,养成用du -sh看一眼目录大小的习惯,和模型卡标注的权重体积对得上再继续。
4. 手动部署核心步骤:从安装 vLLM 到启动服务的完整命令
4.1 安装 vLLM 并验证 GPU 可用性
环境就绪后,第一步是安装 vLLM。vLLM 依赖 PyTorch,所以安装顺序是先装好 PyTorch(按 GPU 驱动支持的 CUDA 版本选),再装 vLLM。当前版本 vLLM 对 Python 3.10/3.11 支持最好,我在 2.3 节已经把 conda 环境建好了,这里直接进入安装。
# 进入之前的独立环境 conda activate deepseek # 安装 PyTorch(示例为 CUDA 12.1 版本,按驱动支持的版本调整) pip install torch --index-url https://download.pytorch.org/whl/cu121 # 安装 vLLM pip install vllm # 验证 vLLM 能识别 GPU python -c "from vllm import LLM; print('vllm ok')"验证时如果出现CUDA error或No module named 'vllm._C',先回头检查 torch 版本和驱动。vLLM 的安装包会带出对应的 torch 版本约束,手动装 torch 时要注意别和它冲突。最省事的做法是直接pip install vllm,让它自己决定依赖版本;但如果网络慢,先确定 torch 版本再装 vLLM 能避免反复下载。
这一步还有一个容易被忽略的问题:如果你的服务器上有多张显卡,而你想指定某一张来部署,需要在启动前设置CUDA_VISIBLE_DEVICES。比如只用物理编号 1 的卡,就export CUDA_VISIBLE_DEVICES=1。这个变量要写在启动服务的命令前,systemd 服务里也一样。不设置的话,vLLM 默认会尝试用所有可见 GPU,可能导致设备选错。
4.2 用 vLLM 启动 DeepSeek:一行命令的每个参数含义
模型下载完、vLLM 装好后,启动服务的命令其实就一行:
python -m vllm.entrypoints.openai.api_server \ --model ./deepseek-7b \ --tensor-parallel-size 1 \ --gpu-memory-utilization 0.85 \ --max-model-len 8192 \ --port 8000这条命令的参数值得逐个理解:
--model指向模型目录,可以是本地路径,也可以是 Hugging Face 模型 ID。手动部署时推荐用本地路径,因为生产环境里模型文件应该已经放在服务器上了,不依赖外网。
--tensor-parallel-size指定用几张 GPU 做张量并行。单卡就是 1;多卡时这个数要等于实际分给模型的 GPU 数量。这里有个前提:参与并行的显卡型号和显存要一致,否则 vLLM 会报错。多卡场景下,--tensor-parallel-size 2会同时占用两张卡,模型被切成两半分别计算。
--gpu-memory-utilization指定显存利用率上限。0.85 表示最多用 85% 显存,留一部分给 CUDA context 和其他开销。设得太高(比如 0.98)有时会导致 KV cache 分配失败,设得太低(比如 0.5)又浪费算力。我第一次部署时就吃过亏,设成 0.95 后一启动就 OOM,后来降到 0.85 才稳定。
--max-model-len是上下文最大长度。8192 对多数业务够用,但如果你要处理长文档,可以调到 16384 或 32768,前提是显存装得下。它直接影响 KV cache 大小,值越大,能并发处理的请求数越少,所以不要盲目拉满。
--port是服务端口。默认 8000,如果你的服务器上有其他服务占用,换成 8088 之类即可。启动成功后,日志里会显示模型加载完成、KV cache 大小、Uvicorn running on http://0.0.0.0:8000等字样。此时已经可以测试接口了,不要急着做常驻化,先确认服务本身没问题。
关于监听地址再说明一点:vLLM 默认监听0.0.0.0,意味着同一内网其他机器也能访问。如果只希望本机访问,加--host 127.0.0.1;如果要在内网提供 API,注意防火墙管控。生产环境建议通过反向代理转发,不要把服务端口直接暴露到公网。
4.3 测试推理:用 curl 与服务对话
vLLM 启动后,会提供一个 OpenAI 兼容的接口,路径是/v1/chat/completions。测试命令:
curl http://127.0.0.1:8000/v1/chat/completions \ -H "Content-Type: application/json" \ -d '{ "model": "./deepseek-7b", "messages": [{"role": "user", "content": "用一句话解释什么是死锁"}], "max_tokens": 128, "temperature": 0.7 }'返回的 JSON 中choices[0].message.content就是模型输出。model字段要和启动时指定的--model值保持一致,否则会报 model not found。测通这一步,说明模型权重没坏、框架能正常推理、网络接口也通了。
如果你更习惯用 Python 验证,也可以用 openai 客户端库,但 curl 在排查时有个不可替代的好处:它能看到原始 HTTP 状态码和请求耗时。第一次请求通常会慢一些,因为有模型预热;之后的延迟才是正常水平。我一般会连续发三个请求,观察延迟是否稳定,再进入下一步。
4.4 用 systemd 把服务变成常驻进程
手动部署的最后一步是把服务交给 systemd 管理,这样服务器重启后服务能自动拉起,日志也统一由 journald 管理。这一步最常见的问题是 conda 环境的 Python 路径写错。systemd 服务里要用ExecStart直接指向 conda 环境里的 Python 二进制,而不是先conda activate再执行。
sudo tee /etc/systemd/system/deepseek.service > /dev/null <<'EOF' [Unit] Description=DeepSeek vLLM Server After=network-online.target [Service] User=root Type=simple Environment=CUDA_VISIBLE_DEVICES=0 ExecStart=/opt/miniconda3/envs/deepseek/bin/python -m vllm.entrypoints.openai.api_server \ --model /data/models/deepseek-7b \ --tensor-parallel-size 1 \ --gpu-memory-utilization 0.85 \ --max-model-len 8192 \ --port 8000 Restart=on-failure RestartSec=5 [Install] WantedBy=multi-user.target EOF sudo systemctl daemon-reload sudo systemctl enable --now deepseekExecStart里的 Python 路径可以通过which python在 conda 环境里查出来,比如/opt/miniconda3/envs/deepseek/bin/python。Restart=on-failure会在服务崩溃时自动拉起,RestartSec=5控制重启间隔。之后想看日志就执行journalctl -u deepseek -f,排查问题时先看日志再看进程,这是最省时间的顺序。
还要注意一个细节:systemd 服务默认的Type=simple表示命令启动后就算服务成功,但如果 vLLM 加载模型需要几十秒,这个期间服务状态是 activating,属于正常现象,不要急着判断启动失败。等日志里出现Uvicorn running再确认服务状态。
5. 部署避坑手册:显存、并发、量化与环境的典型问题
5.1 现象:CUDA out of memory,但 nvidia-smi 看显存还有余量
这是手动部署里翻车最高频的问题之一。明明nvidia-smi看到显存还剩 20%,但 vLLM 一加载就报 OOM。原因有两层:第一,vLLM 的 KV cache 需要一大块连续显存,碎片化会导致分配失败;第二,CUDA context 本身要占用一小部分显存(通常几百 MB 到 1GB),gpu-memory-utilization设为 0.95 以上时,这部分空间没留够就会出现 OOM。解决方法是把gpu-memory-utilization降到 0.85 或 0.8,同时确认没有其他进程占着显存,用nvidia-smi清掉残留进程。如果显存真的不够,就换量化模型或换更小的模型,不要硬撑,服务的稳定性比峰值能力重要。
5.2 现象:模型加载极慢,卡在 Loading model weights 超过十几分钟
有一次项目里 A同学反映模型加载了 20 分钟都没好,日志一直停在加载权重。查下来发现是权重文件不完整,git-lfs 没装导致下载的是指针文件,vLLM 加载时反复去网上请求。检查方法也简单:看权重目录里safetensors文件大小是否跟模型卡标注一致。解决方法是重新用modelscope或huggingface-cli完整下载,下载完先校验文件大小再启动服务。加载慢的另一个常见原因是显存不足触发了 CPU offload,日志里能看到offload to CPU字样,这种情况要么减并发、要么换更小的模型,CPU offload 会让推理速度慢一个数量级,别指望它能提供可用体验。
5.3 现象:并发一高推理就明显变慢,甚至请求超时
vLLM 明明有 continuous batching,为什么并发 20 个请求就卡成狗?这个问题要分清原因:第一,max-model-len太大导致 KV cache 被占满,新请求只能排队;第二,gpu-memory-utilization设太高,模型权重和 KV cache 挤在一起后频繁淘汰旧缓存。解决方法是启动命令里显式加入--max-num-seqs 8之类的值,限制单批最大序列数,并调低max-model-len到业务实际需要的长度。如果业务真的需要高并发,就得加卡做张量并行,把 KV cache 总量撑上去。日志里如果出现Available KV cache很小,基本就是这个方向的问题。
5.4 现象:量化模型输出质量明显下降,逻辑题答错
不少人在 24GB 单卡上部署 32B INT4 模型,然后反馈输出质量远差于预期。这通常不是部署出错,而是量化造成的精度损失在某些任务上被放大了。AWQ 和 GPTQ 的 INT4 在通用对话上表现尚可,但遇到数学推理、多步逻辑、代码生成这类任务,误差会累积。我的做法是:如果业务对输出质量要求高,先跑 FP16 原版权重评估一下,再决定要不要量化。32B INT4 和 7B FP16 哪个更合适,要拿你自己的测试题来比,不要只看参数量。还有一个容易被忽略的点:温度参数也会影响逻辑题的稳定性,如果量化模型输出波动大,先把 temperature 调到 0 再测。
5.5 现象:systemd 服务频繁重启或启动后立即失败
服务通过 systemd 托管后,经常遇到「启动几秒又退出,反复重启」的情况。常见的三个原因:一是 conda 环境的 Python 路径写错,ExecStart 指向了系统 Python,导致 vLLM 模块找不到;二是环境变量CUDA_VISIBLE_DEVICES指定了不存在的 GPU,vLLM 启动时找不到设备直接退出;三是模型目录权限不对,服务用普通用户跑但模型文件放在 root 目录下。排查时先执行journalctl -u deepseek -n 100,日志会直接把原因打出来。我遇到过类似的问题,把User=root换成普通用户后,模型目录和日志目录权限没放开,服务一启动就崩。后来统一把模型文件放到/data/models并给部署用户开放读权限,才彻底解决。记住:systemd 服务的环境比命令行少了 shell 里的各种变量,所有依赖的路径都要写绝对路径。
6. 部署后的验证与调优手感:从测试集到三个关键参数
6.1 准备一组业务测试集,验证输出稳定性
服务能跑只是第一步,输出能不能用于生产是另一回事。我会准备一个 20 到 30 条的业务测试集,覆盖三种类型:事实类问答(看幻觉率)、逻辑推理(看推理链是否完整)、格式要求(看指令跟随能力)。每条请求跑三遍,观察回答是否稳定。这个测试集建议直接贴近你的真实使用场景,比如你是做日志分析的,就把几条典型日志丢进去;做客服问答的,就用真实用户问题做样本。手工跑一遍之后,用脚本统一比对输出长度、关键字段正确率。这一步能拦住大量后期问题,比如换了个量化版本后,某些场景下模型像换了个人一样不稳定。
#!/bin/bash # 简单的稳定性测试脚本:每条请求发 3 次,统计响应耗时和输出长度 for i in $(seq 1 3); do curl -s -w "耗时: %{time_total}s\n" \ http://127.0.0.1:8000/v1/chat/completions \ -H "Content-Type: application/json" \ -d '{ "model": "./deepseek-7b", "messages": [{"role": "user", "content": "请按以下格式输出:概述、原因、建议"}], "max_tokens": 256, "temperature": 0.3 }' | python3 -c " import sys, json for line in sys.stdin: line = line.strip() if line.startswith('耗时'): print(line) else: try: data = json.loads(line) print('输出长度:', len(data['choices'][0]['message']['content'])) except: pass " done这个脚本的价值不在于复杂,而在于让你在调参前后有可对比的基线数据。如果调完参数后同一组测试题的延迟变化超过 30%,就要停下来看看是不是显存或 KV cache 配置出了问题。
6.2 三个值得调的服务化参数:弄懂它们再动手
vLLM 的默认参数在很多情况下能直接用,但如果你想在同样的硬件上压榨更多吞吐,优先调这三个:
--gpu-memory-utilization从 0.85 开始,观察启动日志里 KV cache 大小。如果把并发压上去后出现 OOM,就往 0.8 或 0.75 降;如果显存还有大把空闲,再加并发请求数量继续观察,而不是盲目往 0.97 调。这个参数的本质是「预留多少显存给运行时开销」,留给 CUDA context 和碎片的空间越多,模型越稳定。
--max-num-seqs控制单批处理的序列上限,默认值偏保守。24GB 显卡跑 7B FP16 时,从默认值提到 8 到 16 通常问题不大,但要看 KV cache 是否够用。经验是:先加这个值,再加并发连接数,别一次性拉满。如果想测试实际吞吐上限,可以用hey或wrk这类压测工具模拟并发请求,观察延迟的 P95 值。
--max-model-len要看你的业务请求长度分布。如果大多数请求只有几百 token,却把它设成 32768,等于白白浪费了 KV cache 的显存空间。我一般会按 P95 请求长度再加 30% 余量来设置,而不是直接拉满。例如业务平均输入 800 token、输出 1000 token,那 4096 到 8192 就是合适的区间。改这个参数后一定要重启服务,因为它直接影响 KV cache 的预分配大小。
6.3 观察 GPU 与服务日志,形成自己的调参手感
部署完成后,不要只看 API 通不通。我每周都会固定看一次四个指标:GPU 利用率、KV cache 使用量、请求平均延迟、日志中的 OOM 和 abort 计数。这四个指标基本能说明服务是否健康。我的习惯是启动命令里加上--verbose记录每次请求的 token 数和延迟,然后写一个简单的脚本定时收集日志里的这些数据。
手动部署的价值就在这些细节里:你知道每一步改动意味着什么,也能在出问题时快速判断方向。条件有限时宁可多留显存余量,也不要为了那一点点并发把服务搞得不稳定。这个道理是我在好几次半夜收告警日志之后总结出来的,希望帮到你。
本文还有配套的精品资源,点击获取