很多人以为部署大模型是算法工程师的专属技能——下载几个Python包,跑个transformersdemo就算完事。但真正到了生产环境,问题才刚开始:模型版本怎么管理?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可开到8192stream: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-smi4.2 显存占用参考表
| 模型 | 参数量 | FP16显存 | 4-bit量化 | 建议GPU |
|---|---|---|---|---|
| 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模型):
| 方案 | 并发数 | 平均延迟 | 吞吐量(token/s) |
|---|---|---|---|
| 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机器,那篇就是为你写的。