# 借助DeepSeek之东风扬帆AI海洋(二):无GPU服务器部署DeepSeek
介绍一种相对简单且在业界进行私有化模型的 AI 应用开发时极为常用的方案,即通过 LobeChat + 网关 + Ollama 打造高可用的部署方案。
什么是 Ollama ?
首先来了解一下 Ollama。Ollama 是一个专为在本地机器上便捷部署和运行大型语言模型(LLM)而设计的开源框架,它可以用简单的命令行快捷部署多种大模型,例如 DeepSeek、Qwen、Llama3 等等模型。
Ollama 自身还会通过权重量化技术,调整模型权重,并通过分块加载与缓存机制以及 GPU/CPU 灵活调度等技术,使得模型能够降低对硬件的要求,提高资源利用率。以 DeepSeek-R1 的蒸馏模型 DeepSeek-R1-Distill-Qwen-7B 为例,最小也需要 14 G 的显存。但 Ollama 通过对模型的量化,可以显著降低对于显存的占用。
什么是量化
举一个简单的例子做一下类比。我们在使用微信时,经常会互相发一些图片。如果我们在收到图片时不点下载原图,图片画质就不清晰(分辨率低),但图片大小可能只有几百 K,占用存储空间小;如果我们点了下载原图,图片更加清晰(分辨率高),但占用存储空间大。原版模型和量化模型之间的关系呢,大概就类似于原图和非原图之间的关系,如果为了节省显存,就需要对模型进行量化。
Ollama 有一个非常出色的特性,这也是众多开发者选择它的关键原因,即 Ollama 为所有支持的模型封装了统一的 API,并且兼容 OpenAI 数据格式。这一点至关重要,由于模型是由不同公司或团队训练的,每种模型原本都提供各自的开发接口。因此,Ollama 进行统一封装后,用户在使用时就变得极为便捷。
比如,编写 Agent 代码时,就会把标准的 OpenAI SDK 的 base_url 参数和模型名称做出修改。同样地,如果通过 Ollama 访问 DeepSeek,只需将 base_url 修改为 Ollama 的地址即可。后期如果需要切换到 Qwen 模型,也无需再修改 base_url,只需更换模型名称即可。
client = OpenAI(
api_key=os.getenv("AliDeep"),
base_url="https://dashscope.aliyuncs.com/compatible-mode/v1"
)
Ollama 部署 DeepSeek-R1
环境准备
为了简单,以本机安装作为演示。操作系统是 Windows 10。使用的是 Ollama 部署,因此我们可以以 7B 模型为例做演示。只要你学会了 7B 模型的部署,之后不管是部署 1.5B 还是 671B,方法都一模一样,没有任何区别。
- CPU:Intel(R) Core(TM) i9-10900K CPU @ 3.70GHz 3.60 GHz
- 内存:32.0 GB
- 显卡:七彩虹 Nvidia GeForce RTX 4070 Ti 12GB
首先使用 nvidia-smi 命令,确认 GPU 卡的驱动已经装好,可以被识别。
root@aitest:~# nvidia-smi
+-----------------------------------------------------------------------------------------+
| NVIDIA-SMI 572.83 Driver Version: 572.83 CUDA Version: 12.8 |
|-----------------------------------------+------------------------+----------------------+
| GPU Name Driver-Model | Bus-Id Disp.A | Volatile Uncorr. ECC |
| Fan Temp Perf Pwr:Usage/Cap | Memory-Usage | GPU-Util Compute M. |
| | | MIG M. |
|=========================================+========================+======================|
| 0 NVIDIA GeForce RTX 4070 Ti WDDM | 00000000:01:00.0 On | N/A |
| 0% 41C P8 10W / 285W | 2084MiB / 12282MiB | 1% Default |
| | | N/A |
+-----------------------------------------+------------------------+----------------------+
+-----------------------------------------------------------------------------------------+
| Processes: |
| GPU GI CI PID Type Process name GPU Memory |
| ID ID Usage |
|=========================================================================================|
| No running processes found |
+-----------------------------------------------------------------------------------------+
安装 Ollama
用官方推荐的 Docker 方式部署 Ollama,便于进行版本的管理与测试。
首先将 Ollama 镜像下载到本地,由于国内无法访问 DockerHub,因此可以使用后面命令中的代理地址访问:
docker pull docker.1ms.run/ollama/ollama:0.5.11
之后使用命令启动 Ollama 容器:
docker run -dp 8880:11434 --gpus device=0 --name DeepSeek-R1-1 docker.1ms.run/ollama/ollama:0.5.11
这条命令的意思是,首先将容器的 11434 端口映射到宿主机的 8880 端口,11434 即 Ollama 提供 API 访问的端口。–gpus device=0 表示该容器要使用 GPU 0 号卡,怎么知道 GPU 卡的编号的呢?是在前面执行 nvidia-smi 命令时知道的。
根据前面 nvidia-smi 命令的执行结果,显卡的编号分别是 0。–name DeepSeek-R1-1 是给容器起一个名字,叫任何名字都可以,只要你喜欢,建议取个有意义的名字,比如 DeepSeek-R1-1。最后是下载下来的容器镜像的名称。
如果一切正常,启动后,就会返回容器的 ID。接着我们通过 docker ps 命令,就可以查询到容器的信息。
root@aitest:~# docker run -dp 8880:11434 --gpus device=0 --name DeepSeek-R1-1 docker.1ms.run/ollama/ollama:latest
7049f65fd9d34392c26355174f4701c1b2c5aa718cdc168d19a982f0519d7635
root@aitest:~# docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
7049f65fd9d3 docker.1ms.run/ollama/ollama:0.5.11 "/bin/ollama serve" 10 seconds ago Up 9 seconds 0.0.0.0:8880->11434/tcp, [::]:8880->11434/tcp DeepSeek-R1-1
这一步,经常会报如下的错误:
root@aitest:~# docker run -dp 8880:11434 --gpus device=0 --name DeepSeek-R1-1 docker.1ms.run/ollama/ollama:0.5.11
bd74646a26c6a539aa7660eb664caa585dba188720f0975701b73d87263b6cdf
docker: Error response from daemon: could not select device driver "" with capabilities: [[gpu]].
这是因为服务器的 NVIDIA Container Toolkit 没有装,需要执行如下命令安装一下:
Windows 安装 nvidia-container-toolkit
在 Windows 上安装 NVIDIA Container Toolkit 的步骤如下:
准备工作
- 安装 Docker Desktop:从官网下载并安装 Docker Desktop for Windows。
- 检查并安装 Hyper-V:
- 在“开始”菜单中搜索并打开“启用或关闭 Windows 功能”。
- 勾选“Hyper-V”和“Windows 虚拟机平台”,然后重启电脑。
- 安装并配置正确的 NVIDIA 驱动程序:
- 在“开始”菜单中搜索并打开“命令提示符”,输入并运行以下命令以检查是否已安装正确的 NVIDIA 驱动程序: nvidia-smi
- 如果未安装驱动程序或版本过旧,可前往 NVIDIA 官方网站下载并安装最新的驱动程序。
- 启用虚拟化功能:如果未启用虚拟化功能,可以在 BIOS 中启用,或者在 Windows 中以管理员身份运行 PowerShell 并执行以下命令:
dism.exe /online /enable-feature /featurename:Microsoft-Windows-Subsystem-Linux /all /norestart dism.exe /online /enable-feature /featurename:VirtualMachinePlatform /all /norestart
- 安装 WSL 2:
- 查看 WSL 版本: wsl -l -v
- 如果需要安装 WSL 2,可运行以下命令: wsl --install
- 安装 Ubuntu 20.04: wsl --install -d Ubuntu-20.04
- 安装完成后,设置用户名和密码。
安装 NVIDIA Container Toolkit
- 在 WSL 2 的 Ubuntu 环境中打开终端,然后执行以下命令以添加 NVIDIA Container Toolkit 的仓库密钥和源列表:
curl -fsSL https://nvidia.github.io/libnvidia-container/gpgkey | sudo gpg --dearmor -o /usr/share/keyrings/nvidia-container-toolkit-keyring.gpg \ && curl -s -L https://nvidia.github.io/libnvidia-container/stable/deb/nvidia-container-toolkit.list | \ sed 's#deb https://#deb [signed-by=/usr/share/keyrings/nvidia-container-toolkit-keyring.gpg] https://#g' | \ sudo tee /etc/apt/sources.list.d/nvidia-container-toolkit.list
- 可选步骤:如果需要使用实验性功能,可以运行以下命令: sed -i -e '/experimental/ s/^#//g' /etc/apt/sources.list.d/nvidia-container-toolkit.list
- 更新包列表并安装 NVIDIA Container Toolkit:
如果出现错误提示,可尝试重启电脑。sudo apt-get update sudo apt-get install -y nvidia-container-toolkit
配置 Docker
- 配置 Docker 守护程序以使用 NVIDIA Container Runtime: sudo nvidia-ctk runtime configure --runtime=docker
- 重启 Docker 服务: sudo systemctl restart docker
Docker Desktop 设置:
- 进入 Settings > General,确保 Use the WSL 2 based engine 已启用。
- 进入 Settings > Resources > WSL Integration,启用与你的 WSL 2 发行版的集成(例如 Ubuntu)。
验证安装
- 验证 NVIDIA Container Toolkit 是否正确安装: nvidia-container-cli --version
- 测试是否能够使用 GPU 运行容器: docker run --rm --gpus all nvidia/cuda:12.0-base nvidia-smi 如果成功显示 GPU 信息,则说明 NVIDIA Container Toolkit 已正确安装。
Linux 安装 nvidia-container-toolkit
以Ubuntu为例,执行以下命令:
distribution=$(. /etc/os-release;echo $ID$VERSION_ID)
curl -s -L https://nvidia.github.io/nvidia-docker/gpgkey | sudo apt-key add -
curl -s -L https://nvidia.github.io/nvidia-docker/$distribution/nvidia-docker.list | sudo tee /etc/apt/sources.list.d/nvidia-docker.list
apt-get update
apt-get install -y nvidia-docker2
systemctl restart docker
安装完成后,执行 docker info 命令,确保 docker 守护进程已经正确配置 GPU 支持。命令和输出如下:
root@aitest:~# docker info | grep -i nvidia
Runtimes: io.containerd.runc.v2 nvidia runc
此时再重新 docker run,就可以将 Ollama 容器拉起了。
部署与测试 DeepSeek-R1
Ollama 官方提供了一个可视化的模型仓库,便于我们了解 Ollama 已经支持了哪些模型,以及下载模型。仓库的地址是:ollama.com/library
介绍两种 Ollama 拉起 DeepSeek-R1 的方案。
在容器内下载模型和拉起
需要进入到 Ollama 容器的内部,去执行模型运行命令。进入容器的命令为:
docker exec -it <你的容器名称或 ID> /bin/bash
然后执行模型运行命令:
ollama run deepseek-r1:32b
由于本地没有模型,所以,会先在模型仓库下载模型,我们需要耐心等待模型下载完毕。
下载完成后,就会加载模型,直到出现 success,模型就加载好了。
将模型文件挂载进容器(推荐)
执行如下命令,直接将模型下载到本地:
ollama pull deepseek-r1:32b
启动容器,将模型挂载进去,需要注意 Ollama 在 windows的目录是/Users/[replace your username]/.ollama,如果是 ubuntu 服务器上的默认是模型文件存放目录 /usr/share/ollama/.ollama/models,但是在容器中的目录是 /root/.ollama/models,挂载时要写成服务器目录: 容器目录的格式,注意不要写反了。
docker run -dp 8880:11434 --runtime=nvidia --gpus device=0 --name DeepSeek-R1-1 -v /Users/username/.ollama/models:/root/.ollama/models
:/root/.ollama/models docker.1ms.run/ollama/ollama:0.5.11
最后查看容器中的模型是否已经运行:
root@aitest:~# docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
9a44f04e78f7 docker.1ms.run/ollama/ollama:0.5.11 "/bin/ollama serve" 6 minutes ago Up 6 minutes 0.0.0.0:8880->11434/tcp, [::]:8880->11434/tcp DeepSeek-R1-1
root@aitest:~# docker exec -it 9a44f04e78f7 ollama list
NAME ID SIZE MODIFIED
deepseek-r1:32b 38056bbcbb2d 19 GB About an hour ago
模型为什么不思考?
在上文中,我们问 “hello”,DeepSeek 并没有思考。但是我们用官方版本的去问同样的问题,会发现,官方版的无论简单问题还是复杂问题都会思考:
这是什么原因呢?我们使用如下命令看一下 deepseek-r1:32b 的聊天模板。
ollama show --modelfile deepseek-r1:32b
可以看到在 <| Assistant| > 后面直接就是跟的 Content,没有可以强调 的事情,因此就相当于把是否 think 的主动权交给大模型了。这个其实是对的,如果每一个问题无论简单与否都要思考,会劳民伤财,费时费钱。但这个事情反过来看,如果大模型的能力不能达到极致,在遇到我们想让它思考的问题时,它不思考,体验性又不好。所以这也是官方版本做了强制 think 的原因吧。
如何像官方一样强制 think 呢?其实很简单,按官方教程描述,只需要加一个 think 标签即可。
操作步骤是这样的。首先将聊天模板保存下来:
ollama show --modelfile deepseek-r1:32b > Modelfile
之后,在 <| Assistant| > 之后加上 \n,如下图所示:
之后基于 Modelfile 创建一个新的模型:
ollama create deepseek-r1-think:32b -f ./Modelfile
这时,再按照前面讲的将模型文件挂载进容器的启动方式,启动容器。
进入容器后,执行一下 ollama list,可以看到现在有两个模型了。
使用如下命令,将模型 deepseek-r1-think:32b 运行起来,然后测试:
ollama run deepseek-r1-think:32b
API 访问
由于 Ollama 的 API 兼容了 OpenAI 的数据结构,因此测试非常简单,直接 curl 一下即可:
Windows下可以用postman或者Apifox
Ubuntu环境下的 curl 命令:
curl http://localhost:8880/v1/chat/completions \
-H "Content-Type: application/json" \
-d '{
"model": "deepseek-r1-think:32b",
"messages": [
{"role": "user", "content": "hello"}
]
}'
Ollama 实例的负载均衡的配置
网关选型
根据我们的部署架构图,接下来就是 API 网关的选型了。在传统的业务中,我们经常使用 Nginx 等作为流量转发和负载均衡网关。这对于单次 HTTP 连接时间短,单次请求数据量小的场景来说没有问题。但在 AI 时代,网关后端对接的不再是传统的 HTTP Server,而是大模型。
如果只是简单测试一下负载均衡效果,那使用 Nginx 完全 OK。但如果是真正的实现生产级别的应用,那么我们就需要针对 AI 时代的新挑战而使用 AI 网关,比如 apache-apisix、Kong、Higress 等,关于这几个网关的选择,可以自行网上搜索比较下,各有优缺点,选择合适的即可。
AI 时代给网关带来的挑战
AI 时代给网关带来了哪些新挑战呢?
- 第一,服务连续性。LLM 应用通常需要较长的内容生成时间,用户体验很大程度上依赖于对话的连续性。因此,如何在后端系统更新时保持服务不中断成为了一个关键问题。
- 第二,资源安全。与传统应用相比,LLM 服务处理单个请求时需要消耗大量计算资源。这种特性使得服务特别容易受到攻击,攻击者可以用很小的成本发起请求,却会给服务器带来巨大负担。如何保障后端系统的稳定性变得尤为重要。
- 第三,商业模式保护。很多 AGI 企业会提供免费调用额度来吸引用户,但这也带来了风险,部分黑灰产会利用这些免费额度封装成收费 API 牟利。如何防范这类商业损失是一个现实问题。
- 第四,内容安全。不同于传统 Web 应用的简单信息匹配,LLM 应用通过 AI 推理来生成内容。如何确保这些自动生成的内容安全合规,需要特别关注。
- 第五,多模型管理。当我们需要接入多个大模型时,如何统一管理不同厂商的 API,降低开发和维护成本也是一个重要课题。
在网关的流量层面,AI 时代还面临着三大新需求,分别是长连接、高延时和大带宽,我整理了一张表格描述每个需求的特点。
需求 | 描述 |
---|---|
长连接 | 长连接允许客户端在连接建立后,持续保持与后端服务器的连接,直到客户端显式地断开连接。这种连接方式允许客户端在连接建立后,持续发送请求,而不需要每次请求都建立新的连接。 |
高延时 | 高延时是指客户端与后端服务器之间的网络延迟时间较长。这种延迟时间通常在 100ms 到 500ms 之间。高延时对客户端的用户体验影响较大,用户在等待时间过长时,可能会放弃使用服务。因此,需要设计网关以减少客户端等待时间。 |
大带宽 | 大带宽是指网关处理请求的带宽能力非常大。这种带宽能力通常在 10Gbps 到 100Gbps 之间。大带宽对网关的性能和吞吐量有 significant 的影响。因此,需要设计网 |
分布式部署演示
这边为了方便,使用apache-apisix作为网关,然后使用2台intel版本的mac mini作为演示。前端应用采用LobeChat作为演示。
安装apache-apisix
以docker部署作为演示,因为只是演示,这个可以部署在上面2台mac mini上,也可以单独使用一台机器。
docker-compose.yaml
services:
etcd:
image: bitnami/etcd:3.5.11
restart: always
volumes:
- etcd_data:/bitnami/etcd
environment:
ETCD_ENABLE_V2: "true"
ALLOW_NONE_AUTHENTICATION: "yes"
ETCD_ADVERTISE_CLIENT_URLS: "http://etcd:2379"
ETCD_LISTEN_CLIENT_URLS: "http://0.0.0.0:2379"
ports:
- "2379:2379/tcp"
networks:
apisix:
apisix:
image: apache/apisix:3.11.0-debian
restart: always
volumes:
- ./apisix_conf/config.yaml:/usr/local/apisix/conf/config.yaml:ro
ports:
- "9080:9080" # 代理端口
- "9180:9180" # 管理 API 端口
environment:
APISIX_DEPLOYMENT: "traditional"
APISIX_NGINX_WORKER_PROCESSES: "1"
depends_on:
- etcd
networks:
apisix:
apisix-dashboard:
image: apache/apisix-dashboard:3.0.0-alpine
restart: always
ports:
- "9000:9000" # Dashboard 端口
environment:
APISIX_DEPLOYMENT: "traditional"
APISIX_ADMIN_API_URL: "http://apisix:9180/apisix/admin"
APISIX_ADMIN_API_KEY: "edd1c9f034335f136f87ad84b625c8f1"
volumes:
- ./apisix-dashboard-conf/conf.yaml:/usr/local/apisix-dashboard/conf/conf.yaml:ro
depends_on:
- apisix
networks:
apisix:
volumes:
etcd_data:
driver: local
networks:
apisix:
driver: bridge
apisix_conf/config.yaml
#
# Licensed to the Apache Software Foundation (ASF) under one or more
# contributor license agreements. See the NOTICE file distributed with
# this work for additional information regarding copyright ownership.
# The ASF licenses this file to You under the Apache License, Version 2.0
# (the "License"); you may not use this file except in compliance with
# the License. You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
apisix:
node_listen: 9080 # APISIX listening port
enable_ipv6: false
enable_control: true
control:
ip: "0.0.0.0"
port: 9092
deployment:
admin:
allow_admin: # https://nginx.org/en/docs/http/ngx_http_access_module.html#allow
- 0.0.0.0/0 # We need to restrict ip access rules for security. 0.0.0.0/0 is for test.
admin_key:
- name: "admin"
key: edd1c9f034335f136f87ad84b625c8f1
role: admin # admin: manage all configuration data
- name: "viewer"
key: 4054f7cf07e344346cd3f287985e76a2
role: viewer
etcd:
host: # it's possible to define multiple etcd hosts addresses of the same etcd cluster.
- "http://etcd:2379" # multiple etcd address
prefix: "/apisix" # apisix configurations prefix
timeout: 30 # 30 seconds
plugin_attr:
prometheus:
export_addr:
ip: "0.0.0.0"
port: 9091
apisix-dashboard-conf/conf.yaml
#
# Licensed to the Apache Software Foundation (ASF) under one or more
# contributor license agreements. See the NOTICE file distributed with
# this work for additional information regarding copyright ownership.
# The ASF licenses this file to You under the Apache License, Version 2.0
# (the "License"); you may not use this file except in compliance with
# the License. You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
conf:
listen:
host: 0.0.0.0 # `manager api` listening ip or host name
port: 9000 # `manager api` listening port
etcd:
endpoints: # supports defining multiple etcd host addresses for an etcd cluster
- etcd:2379
# etcd basic auth info
# username: "root" # ignore etcd username if not enable etcd auth
# password: "123456" # ignore etcd password if not enable etcd auth
log:
error_log:
level: warn # supports levels, lower to higher: debug, info, warn, error, panic, fatal
file_path:
logs/error.log # supports relative path, absolute path, standard output
# such as: logs/error.log, /tmp/logs/error.log, /dev/stdout, /dev/stderr
authentication:
secret:
secret # secret for jwt token generation.
# NOTE: Highly recommended to modify this value to protect `manager api`.
# if it's default value, when `manager api` start, it will generate a random string to replace it.
expire_time: 3600 # jwt token expire time, in second
users:
- username: admin # username and password for login `manager api`
password: admin
- username: user
password: user
plugin_attr:
prometheus:
export_addr:
ip: "0.0.0.0"
port: 9091
安装ollama
参考前面ollama docker部署的步骤
安装LobeChat
LobeChat是一个开源的AI聊天机器人,它基于OpenAI的GPT模型,使用Python和Flask构建。
docker-compose.yaml
services:
lobechat:
image: lobehub/lobe-chat
container_name: lobechat
ports:
- "3210:3210"
environment:
- OPENAI_API_KEY=your_openai_api_key # 替换为你的 OpenAI API Key
- OPENAI_PROXY_URL=https://api.openai.com/v1 # 如果需要代理,可以修改为代理地址
volumes:
- ./data:/app/data # 持久化数据
restart: always
networks:
lobechat:
volumes:
lobechat_data:
networks:
lobechat:
driver: bridge