RAG(Retrieval-Augmented Generation,检索增强生成)通过把外部知识检索与大语言模型的生成能力结合,有效缓解了模型幻觉和知识过时的问题。但一套完整的 RAG 系统通常由嵌入模型服务、向量数据库、重排序组件和 LLM 推理后端等多个部分组成,这些组件的语言栈、依赖版本各不相同,手动部署极易出现环境不一致的麻烦。Docker 通过容器化技术把每个组件封装成标准化的运行单元,让整套 RAG 系统可以一条命令启动,这正是它在 RAG 场景下被广泛采用的原因。本文将从架构拆解、组件封装、编排实践到性能优化,系统地讲解 Docker 在 RAG 系统中的使用方法。

一、RAG 系统为什么离不开容器化
先看一个典型的 RAG 请求链路:用户提问后,系统先把问题转成向量,再去向量数据库中检索最相似的文档片段,然后把检索结果和原始问题拼接成提示词,交给大模型生成最终答案。这条链路上任何一个组件出问题,整个系统就会失效。
传统部署方式下,嵌入模型可能依赖特定版本的 PyTorch,向量数据库 Milvus 依赖特定的 etcd 和 MinIO,LLM 推理服务又可能需要 CUDA 和特定驱动版本。开发机上跑得好好的服务,迁移到生产环境后报错连篇,这类问题在 RAG 项目中尤其常见,因为它的组件数量远多于普通的 Web 应用。
容器化恰好解决了三个核心痛点:第一,环境一致性,镜像把依赖完整打包,开发、测试、生产环境完全相同;第二,组件隔离,每个服务独立运行,互不干扰依赖;第三,弹性伸缩,当检索请求量增大时,可以针对向量数据库或嵌入服务单独扩容,而不必整体重新部署。
二、用 Docker 封装 RAG 的核心组件
1. 部署向量数据库
向量数据库是 RAG 的存储与检索单元。以 Chroma 和 Milvus 为代表,它们都提供了官方镜像。对于中小规模项目,Chroma 单容器即可运行;对于亿级向量的场景,Milvus 是更稳妥的选择,它可以通过 standalone 模式用一个容器快速启动,也可以通过分布式模式拆分多个服务。
下面是一个 Chroma 的容器启动命令,通过卷挂载保证数据持久化,重启容器后已入库的向量不会丢失:
docker run -d \ --name chroma \ -p 8000:8000 \ -v chroma-data:/data \ chromadb/chroma
如果选择 Milvus,官方提供了独立的部署脚本,其核心是利用 etcd、MinIO 和 Milvus 三个容器协同工作,生产环境建议直接使用 docker-compose 管理,避免手动维护容器间的启动顺序。
2. 部署嵌入模型服务
嵌入服务负责把文本转换为向量。常用的方案是用 sentence-transformers 加载 BGE 或 m3e 等中文嵌入模型,再用 FastAPI 暴露接口。将这个服务容器化时,建议使用多阶段构建来减小镜像体积,先在一个完整的基础镜像里安装依赖,再只把运行所需的产物复制到精简的最终镜像中。
一个简化的 Dockerfile 示例如下:
FROM python:3.11-slim AS builder WORKDIR /app COPY requirements.txt . RUN pip install --prefix=/install -r requirements.txt FROM python:3.11-slim WORKDIR /app COPY --from=builder /install /usr/local COPY app.py . EXPOSE 8100 CMD ["uvicorn", "app:app", "--host", "0.0.0.0", "--port", "8100"]
这种写法能把镜像从几个 GB 压缩到几百 MB,拉取和启动速度都会显著提升。需要注意的是,模型权重文件不要打进镜像,而是在容器首次启动时从对象存储下载,或者通过只读卷挂载进来,这样升级代码时无需重新传输庞大的模型文件。
3. 部署 LLM 推理后端
本地化推理场景下,Ollama 是最简单的容器化方案,它官方镜像自带模型管理能力,启动后执行 exec 命令即可拉取模型:
docker run -d --name ollama \ --gpus all \ -v ollama-models:/root/.ollama \ -p 11434:11434 \ ollama/ollama docker exec -it ollama ollama pull qwen2.5:7b
这里有两个关键点:一是 --gpus all 参数启用 GPU 支持,前提是宿主机已安装 nvidia-container-toolkit;二是模型目录必须做卷挂载,否则容器重建后所有已下载的模型都会丢失,重新下载几个 GB 的权重文件非常耗时。
三、用 docker-compose 编排完整 RAG 系统
单独管理多个容器的启动顺序和网络配置非常繁琐,docker-compose 通过一个声明式配置文件解决这一切。下面是一套包含向量库、嵌入服务、推理后端和 RAG 应用层的完整编排示例:
version: "3.9"
services:
ollama:
image: ollama/ollama
volumes:
- ollama-models:/root/.ollama
deploy:
resources:
reservations:
devices:
- driver: nvidia
count: all
capabilities: [gpu]
embedding:
build: ./embedding-service
volumes:
- ./models:/models:ro
expose:
- "8100"
chroma:
image: chromadb/chroma
volumes:
- chroma-data:/data
ports:
- "8000:8000"
rag-api:
build: ./rag-app
ports:
- "8080:8080"
environment:
- OLLAMA_HOST=http://ollama:11434
- EMBEDDING_URL=http://embedding:8100
- CHROMA_URL=http://chroma:8000
depends_on:
- ollama
- embedding
- chroma
volumes:
ollama-models:
chroma-data:
这份配置有几个值得注意的细节。首先,服务之间通过 compose 内置的网络直接用服务名互相访问,例如 RAG 应用里配置的 OLLAMA_HOST=http://ollama:11434,不需要写死 IP 地址。其次,embedding 服务只用了 expose 而不是 ports,意味着它不对外暴露端口,只能在内部网络中被访问,这是常见的安全实践。
另外要注意 depends_on 只保证容器的启动顺序,不保证服务已经就绪。向量数据库或数据库类组件可能需要额外的初始化时间,建议在应用代码中加入带重试的健康检查逻辑,或者使用 compose 的 healthcheck 配置配合 condition: service_healthy 来确保依赖真正可用后再启动 RAG 应用。
四、生产环境的优化与注意事项
1. 数据持久化与备份
RAG 系统中最宝贵的资产是向量库中的索引数据和已下载的模型权重。所有相关目录都应该通过命名卷或绑定挂载持久化到容器之外。命名卷由 Docker 统一管理,性能和可移植性较好;绑定挂载直接映射宿主机目录,便于直接查看和备份文件,但路径耦合较高。无论选择哪种方式,都要定期对向量库数据进行快照备份,重建索引的成本远高于备份数据的成本。
2. 资源限制与性能调优
LLM 推理是典型的资源大户,如果不对容器做资源限制,一个失控的推理请求可能拖垮宿主机上的所有服务。可以通过 compose 的资源限制为每个服务设定内存和 CPU 上限。同时要注意,GPU 显存无法像内存那样灵活超分,Ollama 与其他 GPU 服务共存时,要根据模型大小合理评估显存占用,避免出现显存溢出导致的静默失败。
3. 镜像与网络优化
国内环境下拉取镜像缓慢是常见问题,可以配置镜像加速器地址,或者搭建私有 Registry 缓存常用镜像。对于需要经常更新的应用层镜像,建议利用镜像分层缓存特性,把变化频率低的依赖安装步骤放在 Dockerfile 前部,把频繁变动的业务代码放在最后,这样每次构建只需重新执行代码复制之后的步骤,大幅缩短 CI 时间。
此外,嵌入服务和向量库之间的通信走内部网络即可,不需要经过外部端口映射,减少一层转发开销;对于高并发检索场景,可以考虑给 Chroma 或 Milvus 配置连接池,并在应用层做批量嵌入,把多次单条请求合并为一次批量调用,这通常能带来数倍的性能提升。
五、总结
Docker 把 RAG 系统中异构的组件统一到标准化的容器体系中,从向量数据库、嵌入服务到 LLM 推理后端,每个部分都可以独立构建、独立升级、独立扩展。配合 docker-compose 的声明式编排,整套系统可以被完整地版本化到代码仓库中,团队成员只需一条 docker compose up 命令就能拉起完全一致的环境。
落地时建议遵循几个原则:模型权重与数据一律外置挂载,镜像只包含代码与依赖;核心服务配置健康检查,避免启动顺序陷阱;对推理服务严格做资源限制,防止相互影响。把这些细节处理好,容器化的 RAG 系统在开发效率和生产稳定性上都会有明显收益。