LangChain 和 LlamaIndex 是目前搭建大模型应用最常用的两个框架,前者擅长链式调用与 Agent 编排,后者专注于文档索引和检索增强。但这两个框架都有一个共同特点:依赖多、体积大、环境敏感。本地跑得好好的脚本,一旦搬进容器就报错,不是缺编译工具链,就是向量库连不上,或者每次启动都重新下载 Embedding 模型。容器化是解决环境一致性的正解,但配置不当反而会带来更麻烦的问题。这篇文章就来系统讲讲怎么把这两个框架干净利落地装进 Docker 里。

为什么容器化 LangChain 应用并不简单
直接写一个 FROM python:3.11 然后 pip install langchain,确实能跑,但代价是镜像轻松超过 2GB。LangChain 的可选依赖体系是罪魁祸首之一:核心包很小,但一旦引入 langchain-community、llama-index 以及各种向量库客户端,依赖树会迅速膨胀。此外,很多依赖(比如 chromadb 的底层 onnxruntime,或者 sentence-transformers 依赖的 torch)在安装时需要下载预编译 wheel,网络稍慢就会构建失败。
第二个难点是模型的持久化。LlamaIndex 默认使用 HuggingFace 的 Embedding 模型,首次运行时会把模型下载到 ~/.cache/huggingface。如果这个目录没有挂载卷,容器每次重建都会重新下载几百 MB 的模型文件,既慢又浪费带宽。第三个难点是网络配置:容器内的应用访问宿主机或其他容器的向量数据库、LLM API 时,localhost 的语义会发生变化,这是新手最容易踩的坑。
所以一个合格的容器化方案要同时解决四件事:镜像体积可控、依赖版本锁定、模型与数据持久化、服务间网络互通。下面逐个拆解。
编写基础 Dockerfile:多阶段构建与依赖锁定
推荐的做法是先用一个完整的基础镜像安装依赖,再把 site-packages 拷贝到精简的运行镜像中,也就是多阶段构建。这样可以把 gcc 等编译工具留在构建阶段,运行镜像里只保留最终的依赖产物。先看依赖锁定,务必使用 pip-compile 或 pip freeze 生成锁文件,避免每次构建拉到不同版本:
pip freeze > requirements.txt
接下来是 Dockerfile 主体,以一个同时使用 LangChain 和 LlamaIndex 的 RAG 应用为例:
# 构建阶段:安装依赖 FROM python:3.11-slim AS builder WORKDIR /app # 先拷贝依赖文件,利用 Docker 层缓存 COPY requirements.txt . RUN pip install --no-cache-dir --prefix=/install -r requirements.txt # 运行阶段:精简镜像 FROM python:3.11-slim WORKDIR /app # 从构建阶段拷贝依赖 COPY --from=builder /install /usr/local # 拷贝应用代码 COPY ./app ./app # 指定模型缓存目录并挂载卷 ENV HF_HOME=/data/huggingface ENV TRANSFORMERS_CACHE=/data/huggingface EXPOSE 8000 CMD ["uvicorn", "app.main:app", "--host", "0.0.0.0", "--port", "8000"]
这个写法有几个细节值得注意。第一,COPY requirements.txt 放在拷贝源码之前,这样只要依赖没变,pip install 那一层就直接走缓存,日常迭代速度会快很多。第二,用 --prefix=/install 把依赖装到独立目录,方便整体拷贝。第三,通过 HF_HOME 环境变量把 HuggingFace 模型缓存指向 /data,后面挂载一个卷就能实现模型持久化,二次启动不用重新下载。
如果项目用到 torch,还可以考虑安装 CPU 精简版,体积能从 2GB 降到 200MB 左右:
pip install torch --index-url https://download.pytorch.org/whl/cpu
当然,如果容器里要跑 GPU 推理,则需要以 nvidia/cuda 系列镜像为基底,并用 --gpus all 启动,这是另一套方案了。对大多数调用远程 API(如 OpenAI、DeepSeek、通义千问)的应用来说,CPU 版本完全够用,没必要为了一个 Embedding 模型背上几个 GB 的 CUDA 运行时。
用 docker compose 编排应用与向量数据库
真实的 RAG 应用很少单独跑,通常还需要 Chroma、Milvus、PostgreSQL(配合 pgvector)这类依赖服务。用 docker compose 统一编排是最省心的方式。下面是一个把 FastAPI 应用、Chroma 向量库和 Redis 组合起来的示例:
version: "3.8"
services:
rag-app:
build: .
ports:
- "8000:8000"
environment:
- OPENAI_API_KEY=${OPENAI_API_KEY}
- CHROMA_HOST=chroma
- CHROMA_PORT=8001
- REDIS_URL=redis://redis:6379/0
volumes:
- model-cache:/data/huggingface
depends_on:
- chroma
- redis
restart: unless-stopped
chroma:
image: chromadb/chroma
ports:
- "8001:8001"
volumes:
- chroma-data:/chroma/chroma
redis:
image: redis:7-alpine
volumes:
model-cache:
chroma-data:这里的关键点在于服务发现。容器内的应用访问 Chroma 时,地址写的是 http://chroma:8001 而不是 localhost。compose 会自动创建一个网络,服务名就是主机名。对应到 LlamaIndex 代码里,初始化 Chroma 客户端大概是这样:
import chromadb
from llama_index.vector_stores.chroma import ChromaVectorStore
from llama_index.core import StorageContext
client = chromadb.HttpClient(
host="chroma", # compose 网络中的服务名
port=8001
)
vector_store = ChromaVectorStore(chroma_collection=client.get_or_create_collection("docs"))
storage_context = StorageContext.from_defaults(vector_store=vector_store)另一个要点是卷的划分。model-cache 卷持久化 Embedding 模型,chroma-data 卷持久化向量数据。这样即使 docker compose down 再 up,已索引的文档和已下载的模型都还在。API 密钥则通过环境变量注入,配合项目根目录的 .env 文件管理,切记不要把密钥写进镜像或提交到代码仓库。
如果应用还需要访问宿主机上的服务(比如本地跑的 Ollama),在 Linux 下可以用特殊域名 host.docker.internal(需在 compose 中为服务加 extra_hosts: ["host.docker.internal:host-gateway"]),或者直接让应用容器加入 host 网络。Windows 和 macOS 的 Docker Desktop 默认就支持这个域名。
镜像体积优化与常见问题排查
即便采用了多阶段构建,LangChain 加 LlamaIndex 的镜像通常仍有 1GB 以上,其中大头往往是 torch、onnxruntime 和 numpy 的科学计算栈。进一步压缩可以尝试几个手段:一是按需安装 LangChain 的集成包,比如只装 langchain-openai 而不是整个 langchain-community;二是用 pip install --no-deps 处理个别依赖过重的包,但要手动补齐缺失依赖,维护成本较高;三是终极方案换用 Alpine 基底,不过很多科学计算包没有 musl 的 wheel,会触发源码编译,反而更慢更折腾,一般不建议。
排查问题时,几个命令非常实用。docker image history 镜像名 能看到每一层占了多少空间,找出体积大户;docker compose logs -f rag-app 实时看应用日志;如果怀疑依赖缺失,可以 docker run -it 镜像名 pip list 进容器检查实际安装的包。遇到 Chroma 连接超时,先确认应用和 Chroma 是否在同一个 compose 网络里,再检查端口映射和防火墙配置。
还有一个高频报错是首次启动时下载 HuggingFace 模型超时。解决办法有两个方向:一是构建镜像时就把模型下载进镜像(适合模型固定、内网部署的场景),在 Dockerfile 中加一步预下载;二是利用卷缓存配合代理环境变量,例如设置 HF_ENDPOINT 指向镜像站。前者启动快但镜像大,后者灵活但首次启动慢,按部署环境取舍即可。
小结
容器化 LangChain 和 LlamaIndex 应用,核心思路是分层解决:用多阶段构建和依赖锁定控制镜像体积与可复现性,用环境变量和 compose 网络解耦服务配置,用持久化卷保护模型缓存与向量数据。把这套配置固化成模板后,无论是部署到单机还是迁移到 Kubernetes,基本只需要调整编排层,应用代码和 Dockerfile 都可以原样复用。建议先把最小可运行版本跑通,再逐步把 Chroma、Redis、监控探针这些周边服务加进编排,避免一开始就追求大而全导致排障困难。
LangChainLlamaIndexDocker容器化修改时间:2026-09-16 06:28:41