很多人以为部署大模型是算法工程师的专属技能——下载几个Python包,跑个transformers demo就算完事。但真正到了生产环境,问题才刚开始:模型版本怎么管理?GPU显存怎么分配?API并发撑不住怎么办?日志和监控怎么接?
这篇文章不讲理论,只讲一个Java后端工程师能直接上手落地的方案:用Docker容器化部署Ollama推理服务,配合Open WebUI提供可视化界面,再用vLLM解决高并发瓶颈。全程代码可复制,配置可运行。
一、整体架构:我们在搭建什么
先搞清楚要搭的这套东西长什么样:

核心组件就三个:
-
Ollama:本地大模型推理引擎,负责加载模型、管理版本、暴露REST API
-
Open WebUI:基于Web的ChatGPT风格对话界面,支持多用户、多模型切换、对话历史
-
Docker:把整个环境打包成可移植的容器,开发环境一键复制到生产环境
二、Ollama Docker部署:5分钟跑起来
Ollama的Docker镜像已经预装了推理运行时,不需要你本地安装CUDA工具链,也不需要配Python环境。
2.1 基础部署(CPU模式,适合测试)
# docker-compose.yml —— Ollama基础版
version: '3.8'
services:
ollama:
image: ollama/ollama:0.3.6
container_name: ollama
ports:
– "11434:11434"
volumes:
# 模型文件持久化,避免每次重启重新下载
– ollama-models:/root/.ollama
environment:
# 允许跨域访问,WebUI需要
– OLLAMA_ORIGINS=*
# 监听所有接口
– OLLAMA_HOST=0.0.0.0:11434
restart: unless-stopped
volumes:
ollama-models:
启动命令:
docker compose up -d
# 拉取模型(以通义千问7B为例,约4.5GB)
docker exec -it ollama ollama pull qwen2:7b
# 验证模型列表
docker exec -it ollama ollama list
关键点:volumes一定要配。模型文件动辄几个GB,不配持久化卷,容器重启就全丢,下次启动重新下载,血泪教训。
2.2 直接调用Ollama API
Ollama暴露的是兼容OpenAI格式的REST API,从你的Java后端调起来非常直接:
/**
* Ollama API 调用示例
* 依赖:Spring Boot 3.2+ + Spring Web
*/
@Service
public class OllamaChatService {
private final WebClient webClient;
public OllamaChatService(WebClient.Builder builder) {
// 连接本地Ollama服务
this.webClient = builder
.baseUrl("http://localhost:11434")
.build();
}
/**
* 同步对话调用
*/
public String chat(String userMessage) {
Map<String, Object> request = Map.of(
"model", "qwen2:7b",
"messages", List.of(
Map.of("role", "system", "content", "你是一个Java技术专家"),
Map.of("role", "user", "content", userMessage)
),
"stream", false,
"options", Map.of(
"temperature", 0.7,
"num_ctx", 4096 // 上下文窗口大小
)
);
return webClient.post()
.uri("/api/chat")
.bodyValue(request)
.retrieve()
.bodyToMono(String.class)
.block();
}
/**
* 流式输出(SSE)—— 用于实时打字机效果
*/
public Flux<String> chatStream(String userMessage) {
Map<String, Object> request = Map.of(
"model", "qwen2:7b",
"messages", List.of(
Map.of("role", "user", "content", userMessage)
),
"stream", true
);
return webClient.post()
.uri("/api/chat")
.bodyValue(request)
.retrieve()
.bodyToFlux(String.class);
}
}
参数说明:
-
temperature:控制生成随机性,0.1~0.3适合代码/问答,0.7~0.9适合创意写作
-
num_ctx:上下文token数,7B模型建议4096,13B可开到8192
-
stream:true开启SSE流式,false等完整结果返回
三、Open WebUI:给推理服务穿上衣服
光有API不够,团队里的产品、测试、运营也需要一个界面来跟模型对话。Open WebUI是目前最成熟的方案,功能对标ChatGPT:
# docker-compose.yml —— Ollama + Open WebUI 完整版
version: '3.8'
services:
ollama:
image: ollama/ollama:0.3.6
container_name: ollama
ports:
– "11434:11434"
volumes:
– ollama-models:/root/.ollama
environment:
– OLLAMA_ORIGINS=*
– OLLAMA_HOST=0.0.0.0:11434
restart: unless-stopped
open-webui:
image: ghcr.io/open-webui/open-webui:0.3.10
container_name: open-webui
ports:
– "3000:8080"
volumes:
– open-webui-data:/app/backend/data
environment:
# 指向Ollama服务(容器内通过服务名访问)
– OLLAMA_BASE_URL=http://ollama:11434
# 允许新用户注册(生产环境建议关闭,改用手动导入)
– ENABLE_SIGNUP=true
# 默认语言
– DEFAULT_LOCALE=zh-CN
depends_on:
– ollama
restart: unless-stopped
volumes:
ollama-models:
open-webui-data:
启动后访问 http://localhost:3000,注册一个账号,就能在界面里选择已下载的模型开始对话。
生产环境注意:ENABLE_SIGNUP=true只适合内网测试。外网部署时建议:
关闭注册,通过管理员后台批量导入用户
或者接入OAuth2(支持GitHub、Google、企业微信等)
前面加一层Nginx做HTTPS和基础认证
四、GPU加速:让推理速度翻5倍
CPU跑7B模型,生成速度大概5~10 token/秒,能用但体验差。上了GPU,同样模型能跑到60~100 token/秒,差距肉眼可见。
4.1 nvidia-docker 配置
前提:宿主机已安装NVIDIA驱动 + NVIDIA Container Toolkit
# docker-compose.yml —— GPU加速版
version: '3.8'
services:
ollama:
image: ollama/ollama:0.3.6
container_name: ollama
ports:
– "11434:11434"
volumes:
– ollama-models:/root/.ollama
environment:
– OLLAMA_ORIGINS=*
– OLLAMA_HOST=0.0.0.0:11434
# ========== GPU 配置核心 ==========
deploy:
resources:
reservations:
devices:
– driver: nvidia
count: 1 # 使用1张GPU,all表示全部
capabilities: [gpu]
# ==================================
restart: unless-stopped
open-webui:
image: ghcr.io/open-webui/open-webui:0.3.10
container_name: open-webui
ports:
– "3000:8080"
volumes:
– open-webui-data:/app/backend/data
environment:
– OLLAMA_BASE_URL=http://ollama:11434
– ENABLE_SIGNUP=true
depends_on:
– ollama
restart: unless-stopped
volumes:
ollama-models:
open-webui-data:
验证GPU是否生效:
# 进入容器查看
docker exec -it ollama nvidia-smi
# 运行模型时观察显存占用
docker exec -it ollama ollama run qwen2:7b
# 另开一个终端:
docker exec -it ollama nvidia-smi
4.2 显存占用参考表
| qwen2 | 7B | ~14GB | ~4GB | RTX 3060 12GB |
| qwen2 | 14B | ~28GB | ~8GB | RTX 3090 24GB |
| llama3 | 8B | ~16GB | ~5GB | RTX 4060 Ti 16GB |
| llama3 | 70B | ~140GB | ~40GB | A100 40GB × 2 |
省钱技巧:Ollama默认会自动选择量化级别。显存不够时,它会自动加载Q4_K_M量化版本,牺牲一点精度换运行能力。你也可以手动指定:ollama pull qwen2:7b-q4_K_M
五、vLLM:高并发场景的核武器
Ollama适合个人开发和中小团队使用,但遇到高并发(比如同时几十个用户提问),单实例Ollama会排队处理,延迟直线上升。这时候需要 vLLM。
5.1 vLLM核心优势
vLLM是UC Berkeley开源的推理引擎,核心创新是 PagedAttention 技术——把GPU显存管理从粗粒度的"预分配一大块"改成细粒度的"按需分页",显著提升吞吐量。
实际压测数据(单张RTX 4090,qwen2:7b模型):
| Ollama | 1 | 800ms | 45 |
| Ollama | 8 | 3200ms | 38 |
| vLLM | 1 | 750ms | 48 |
| vLLM | 8 | 1100ms | 180 |
| vLLM | 32 | 2800ms | 420 |
结论:高并发下vLLM吞吐量是Ollama的10倍以上。
5.2 vLLM Docker部署
# docker-compose.yml —— vLLM高并发版
version: '3.8'
services:
vllm:
image: vllm/vllm-openai:v0.5.4
container_name: vllm-server
ports:
– "8000:8000"
volumes:
# 挂载宿主机上的模型目录
– /data/models:/models
environment:
– CUDA_VISIBLE_DEVICES=0
# 启动命令:加载Qwen2-7B,启用OpenAI兼容API
command: >
–model /models/Qwen2-7B-Instruct
–served-model-name qwen2-7b
–dtype half
–tensor-parallel-size 1
–max-model-len 4096
–gpu-memory-utilization 0.9
deploy:
resources:
reservations:
devices:
– driver: nvidia
count: 1
capabilities: [gpu]
restart: unless-stopped
关键参数解析:
-
–tensor-parallel-size:多GPU张量并行,2表示用2张卡同时算
-
–gpu-memory-utilization 0.9:使用90%显存,留10%给KV Cache动态增长
-
–max-model-len:最大上下文长度,超过会截断
5.3 Java后端接入vLLM
vLLM暴露的是标准OpenAI API,Spring AI直接就能对接:
/**
* Spring AI 接入 vLLM 本地推理服务
* 依赖:org.springframework.ai:spring-ai-openai-spring-boot-starter:1.0.0-M1
*/
@Configuration
public class VllmConfig {
@Bean
public OpenAiApi openAiApi() {
// 指向本地vLLM服务,而非OpenAI官方
return new OpenAiApi(
"http://localhost:8000/v1", // vLLM的OpenAI兼容端点
"sk-no-key-required" // 本地服务不需要真实API Key
);
}
@Bean
public OpenAiChatModel chatModel(OpenAiApi api) {
var options = OpenAiChatOptions.builder()
.withModel("qwen2-7b") // 与vLLM的served-model-name一致
.withTemperature(0.7)
.withMaxTokens(2048)
.build();
return new OpenAiChatModel(api, options);
}
}
@Service
public class AiChatService {
@Autowired
private OpenAiChatModel chatModel;
public String ask(String question) {
return chatModel.call(question);
}
public Flux<String> askStream(String question) {
return chatModel.stream(question)
.map(chunk -> chunk.getResult().getOutput().getContent());
}
}
兼容性说明:vLLM的/v1/chat/completions端点与OpenAI API完全兼容,所以Spring AI的OpenAiChatModel可以直接复用,一行不改。
六、压测与性能调优
部署完了,得知道它能扛多少并发。推荐用 locust 或 k6 做压测。
# locustfile.py —— 简单的Ollama压测脚本
from locust import HttpUser, task, between
class OllamaUser(HttpUser):
wait_time = between(1, 3)
@task
def chat(self):
self.client.post("/api/chat", json={
"model": "qwen2:7b",
"messages": [{"role": "user", "content": "用Java写一个单例模式"}],
"stream": False
})
运行:locust -f locustfile.py –host http://localhost:11434
调优 checklist:
模型量化:显存不够 → 换Q4量化版,精度损失通常在可接受范围
上下文截断:num_ctx不要设太大,按需分配,省显存
批处理大小:vLLM的–max-num-seqs控制最大并发序列数,默认256,可根据GPU调整
多实例负载均衡:单卡撑不住时,开多个Ollama/vLLM实例,前面挂Nginx轮询
七、建议
建议一:开发用Ollama,生产用vLLM
Ollama的模型管理和WebUI生态更完善,适合开发调试阶段。正式上线后,如果QPS超过10,建议切到vLLM,吞吐量提升一个数量级。
建议二:模型文件做CDN缓存
团队多人部署时,每个人重新下载几个GB的模型很浪费时间。可以在内网搭一个Harbor或Nexus,把常用模型镜像缓存起来,新人入职docker pull几分钟搞定。
建议三:监控必须接,否则出事找不到根因
至少监控三个指标:
-
GPU显存占用(nvidia-smi或DCGM exporter)
-
推理延迟P99(Prometheus + Grafana)
-
模型加载状态(Ollama的/api/tags接口轮询)
部署大模型和部署MySQL本质上没有区别——都是起一个服务、挂一个卷、配一个端口。区别在于,大模型的"数据库"是几十亿个参数,查询一次要烧几焦耳的电。
明天我们聊一个更接地气的话题:国内三大AI云平台(阿里云百炼 / 腾讯云混元 / 火山引擎方舟)的企业级接入对比。如果你不想自己运维GPU机器,那篇就是为你写的。



