Day 61 | Docker部署AI推理服务:Ollama + Open WebUI生产实践

发布时间:2026/8/28 18:54:31
Day 61 | Docker部署AI推理服务:Ollama + Open WebUI生产实践 很多人以为部署大模型是算法工程师的专属技能——下载几个Python包跑个transformersdemo就算完事。但真正到了生产环境问题才刚开始模型版本怎么管理GPU显存怎么分配API并发撑不住怎么办日志和监控怎么接这篇文章不讲理论只讲一个Java后端工程师能直接上手落地的方案用Docker容器化部署Ollama推理服务配合Open WebUI提供可视化界面再用vLLM解决高并发瓶颈。全程代码可复制配置可运行。一、整体架构我们在搭建什么先搞清楚要搭的这套东西长什么样核心组件就三个Ollama本地大模型推理引擎负责加载模型、管理版本、暴露REST APIOpen 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_HOST0.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 APIOllama暴露的是兼容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) { MapString, 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 FluxString chatStream(String userMessage) { MapString, 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模型建议409613B可开到8192streamtrue开启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_HOST0.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_URLhttp://ollama:11434 # 允许新用户注册生产环境建议关闭改用手动导入 - ENABLE_SIGNUPtrue # 默认语言 - DEFAULT_LOCALEzh-CN depends_on: - ollama restart: unless-stopped ​ volumes: ollama-models: open-webui-data:启动后访问http://localhost:3000注册一个账号就能在界面里选择已下载的模型开始对话。生产环境注意ENABLE_SIGNUPtrue只适合内网测试。外网部署时建议关闭注册通过管理员后台批量导入用户或者接入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_HOST0.0.0.0:11434 # GPU 配置核心 deploy: resources: reservations: devices: - driver: nvidia count: 1 # 使用1张GPUall表示全部 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_URLhttp://ollama:11434 - ENABLE_SIGNUPtrue 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量化建议GPUqwen27B~14GB~4GBRTX 3060 12GBqwen214B~28GB~8GBRTX 3090 24GBllama38B~16GB~5GBRTX 4060 Ti 16GBllama370B~140GB~40GBA100 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 4090qwen2:7b模型方案并发数平均延迟吞吐量(token/s)Ollama1800ms45Ollama83200ms38vLLM1750ms48vLLM81100ms180vLLM322800ms420结论高并发下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_DEVICES0 # 启动命令加载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后端接入vLLMvLLM暴露的是标准OpenAI APISpring 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 FluxString 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生产用vLLMOllama的模型管理和WebUI生态更完善适合开发调试阶段。正式上线后如果QPS超过10建议切到vLLM吞吐量提升一个数量级。建议二模型文件做CDN缓存团队多人部署时每个人重新下载几个GB的模型很浪费时间。可以在内网搭一个Harbor或Nexus把常用模型镜像缓存起来新人入职docker pull几分钟搞定。建议三监控必须接否则出事找不到根因至少监控三个指标GPU显存占用nvidia-smi或DCGM exporter推理延迟P99Prometheus Grafana模型加载状态Ollama的/api/tags接口轮询部署大模型和部署MySQL本质上没有区别——都是起一个服务、挂一个卷、配一个端口。区别在于大模型的数据库是几十亿个参数查询一次要烧几焦耳的电。明天我们聊一个更接地气的话题国内三大AI云平台阿里云百炼 / 腾讯云混元 / 火山引擎方舟的企业级接入对比。如果你不想自己运维GPU机器那篇就是为你写的。