第 2 章 RAGDockerFastAPI

第 2 章:第一周:用 Docker Compose 搭起 RAG 的基础设施

第 2 章:第一周:用 Docker Compose 搭起 RAG 的基础设施

这一章不写一行检索逻辑。课程第一周的目标只有一个:把后面六周要用的底座立起来,并且证明它是活的。RAG 系统出问题,多数时候不是 prompt 写得不好,而是某个容器根本没起来、端口被占、数据库连不上。等你第 3 周开始调 BM25 打分、第 4 周调 hybrid search 的 RRF 融合权重时,如果底层不可信,你连"到底是检索错了还是服务挂了"都分不清。所以先把五个服务跑起来、把健康检查跑绿,再谈检索。

五个服务与一张端口表

课程把整套栈定义在仓库根目录的 compose.yml 里。所有容器接入同一个 Docker Network rag-network,数据落在 persistent volume 上,所以 docker compose down 不会丢掉 PostgreSQL 里的论文元数据,只有 down -v 才会连 volume 一起清掉。

Week 1 的五个服务与 Docker 网络拓扑:FastAPI、PostgreSQL 16、OpenSearch 2.19、Airflow 3.0、Ollama 各自暴露端口,通过 rag-network 互通并挂载持久化 volume

服务 版本 端口 职责
FastAPI 0.115+ 8000 REST API,带 async 支持和自动文档
PostgreSQL 16 5432 论文元数据与正文的主数据库
OpenSearch 2.19 9200 / 5601 Hybrid search 引擎(BM25 + Vector)与 Dashboards
Apache Airflow 3.0 8080 Workflow 编排,跑 DAG,用 PostgreSQL 做 backend
Ollama — 11434 本地 LLM server

注意每个服务为什么在这里。FastAPI 是唯一对外的 API 入口;PostgreSQL 存元数据,同时也被 Airflow 当 metadata database 复用,所以 DAG 的调度数据和业务数据在同一个实例里、不同库表;OpenSearch 第 1 周只是"装着",第 3 周才开始建 index、写 mapping;Ollama 第 1 周连模型都可以不装,服务本身能起来就行。

第 1 周只有 hello_world_dag.py 一个 DAG,第 2 周才会加入 arxiv_paper_ingestion.py。后面几周还会往这个 compose 里继续塞服务——第 6 周加 Redis 和本地 Langfuse,第 5 周加跑在宿主机 7861 的 Gradio 界面。所以下面那张访问入口表会随着周数变长,第 1 周你用不到全部。

从零到 docker compose up --build -d

前置条件四项:Docker Desktop(带 Docker Compose)、Python 3.12+、uv 包管理器,以及官方 README 要求的 8GB+ RAM 和 20GB+ 空闲磁盘——这是五个容器同时跑起来的下限。uv 的安装方式官方指向 uv Install Guide。

# 1. Clone and setup
git clone 
cd arxiv-paper-curator

# 2. Configure environment (IMPORTANT!)
cp .env.example .env
# The .env file contains all necessary configuration for OpenSearch, 
# arXiv API, and service connections. Defaults work out of the box.
# You need to add Jina embeddings free api key and langfuse keys (check the blogs)

# 3. Install dependencies
uv sync

# 4. Start all services
docker compose up --build -d

# 5. Verify everything works
curl http://localhost:8000/api/v1/health

--build 这个参数不能省。Airflow 和 FastAPI 都是仓库里的自定义镜像(airflow/Dockerfile 是其中一个),改了依赖或代码而不加 --build,你跑的还是旧镜像。-d 让容器回到后台,终端不被日志占住。

.env 这一步经常被跳过,因为默认值就能跑。第 1 周不需要改任何东西,但要知道后面哪些 key 什么时候变成必需:JINA_API_KEY 从第 4 周(embedding)开始必需,LANGFUSE__PUBLIC_KEY / LANGFUSE__SECRET_KEY 第 6 周可选用,TELEGRAM__BOT_TOKEN 第 7 周必需。完整变量清单看 .env.example。

如果你要对着某一周的 release tag 复现,README 给的流程多了一步 down -v,作用是先清掉可能残留的旧 volume,避免旧 schema 干扰:

# Clone a specific week's code
git clone --branch  https://github.com/jamwithai/arxiv-paper-curator
cd arxiv-paper-curator
uv sync
docker compose down -v
docker compose up --build -d

# Replace  with: week1.0, week2.0, etc.

健康检查与访问入口

五个容器全部 Started 不等于服务可用。OpenSearch 尤其明显:进程在,但 cluster 还没 ready。课程的验证方式是打 API 的健康检查端点:

curl http://localhost:8000/api/v1/health

FastAPI 那边还有一个 Week 1 就存在的 /health 端点,make health 则把所有服务的状态一起查一遍。三个入口分工明确:/api/v1/health 走完整 API 路径,验证进程、路由和下游连接;/health 是最轻量的存活探针;make health 是排障时的批量入口。

Service URL Purpose
API Documentation http://localhost:8000/docs Interactive API testing
Gradio RAG Interface http://localhost:7861 User-friendly chat interface
Langfuse Dashboard http://localhost:3000 RAG pipeline monitoring & tracing
Airflow Dashboard http://localhost:8080 Workflow management
OpenSearch Dashboards http://localhost:5601 Hybrid search engine UI

最后两项是第 5、6 周才出现的,第 1 周打开会连不上,这是预期行为。本周真正会用的是 8000/docs(浏览器里直接调 API,比 curl 省事)和 5601(确认 OpenSearch 活着)。

配套的学习材料是 Week 1 notebook,用 uv 跑起来:

# Launch the Week 1 notebook
uv run jupyter notebook notebooks/week1/week1_setup.ipynb

notebook 里有自动化的前置条件检查和逐步验证流程。官方 README 给出的时间口径是:安装下载 2-3 小时,notebook 走完约 1 小时,合计 2-4 小时;这些时间绝大部分花在拉镜像和装 Docker Desktop 上。

uv、Makefile 与代码质量工具

依赖管理统一交给 uv。日常只有两个动词:uv sync 把 pyproject.toml 里声明的环境同步到本地,uv run <cmd> 在这个环境里执行命令,不用手动 source .venv/bin/activate。notebook、pytest、Gradio launcher 的启动方式因此是同一套:

uv sync
uv run jupyter notebook notebooks/week1/week1_setup.ipynb
uv run pytest
uv run python gradio_launcher.py

README 把开发工具列成一行:UV, Ruff, MyPy, Pytest, Docker Compose。前四个各管一件事:uv 管环境,Ruff 管格式与 lint,MyPy 管类型,Pytest 管测试。它们不该是"有空再配"的东西,因为第 4 周以后你会同时改 chunking、embedding 和 search 三处代码,类型错误靠肉眼抓不住。

命令行入口是 Makefile,README 明确推荐优先用它:

# View all available commands
make help

# Quick workflow
make start         # Start all services
make health        # Check all services health
make test          # Run tests
make stop          # Stop services
Command Description
make start Start all services
make stop Stop all services
make restart Restart all services
make status Show service status
make logs Show service logs
make health Check all services health
make setup Install Python dependencies
make format Format code
make lint Lint and type check
make test Run tests
make test-cov Run tests with coverage
make clean Clean up everything

make format / make lint 对应 Ruff 和 MyPy,make test / make test-cov 对应 Pytest。不想用 Makefile 就直接敲底层命令,效果一样:

# If you prefer using commands directly
docker compose up --build -d    # Start services
docker compose ps               # Check status
docker compose logs            # View logs
uv run pytest                 # Run tests

Ollama:装哪个本地模型

Ollama 在第 1 周是可选的。README 说得很清楚:No models are required for Week 1 - service health check works without them. 11434 端口能响应就算通过。但既然容器已经在跑,顺手拉一个小模型验证链路是划算的,第 5 周的生成端就靠它。

装模型有两条路,Makefile 是推荐路径:

# Using Makefile (recommended)
make ollama-pull MODEL=llama3.2:1b
make ollama-test MODEL=llama3.2:1b

# Direct HTTP calls for learning
curl -X POST http://localhost:11434/api/pull -d '{"name":"llama3.2:1b"}'
curl -X POST http://localhost:11434/api/generate -d '{"model":"llama3.2:1b","prompt":"Hello","stream":false}'

第二条路直接打 Ollama 的 HTTP API,课程把它单独列出来就是为了让你看清 Makefile 背后到底发生了什么:/api/pull 拉模型,/api/generate 推理且 stream: false 表示一次性返回。理解这一层,第 5 周做 SSE 流式响应时你就知道该把哪个字段改成 true。

官方给出的推荐模型与体积口径:

模型 体积 定位
llama3.2:1b 1.2GB 快,适合测试
llama3.2:3b 2.0GB 速度与质量折中
llama3.1:8b 4.7GB 质量更好,更慢

选型上不要一开始就上 8b。你手上是 8GB 内存起的 Docker Desktop,Ollama 还要跟 OpenSearch、Airflow 抢内存,先把 llama3.2:1b 跑通整条链路,需要评估生成质量时再换大模型。notebook 把 Ollama 测试拆成四个单元格(Test 3A 查模型、3B 简单推理、3C 性能、3D 命令与笔记),目的是让你在没装模型时也能正常往后走,不卡在一个必红的单元格上。

Airflow 的简单认证与密码文件

Airflow 的 Web UI 在 http://localhost:8080,用户名和密码是容器初始化时自动生成的,不是固定的 admin/admin。README 特意用一行 NOTE 指出去哪找:

airflow/simple_auth_manager_passwords.json.generated

这个 .generated 后缀本身就是设计信号:文件由容器启动时生成、不进版本控制,你 clone 下来第一次 up 完才会出现在 airflow/ 目录里。找不到它通常意味着 Airflow 容器还没初始化完,等一会儿或看 docker compose logs 里的初始化输出。

Airflow 容器的几个配置点解释了它为什么能在 macOS、Linux、WSL 和 Ubuntu 上跑同一份 compose:

AIRFLOW__DATABASE__SQL_ALCHEMY_CONN=postgresql+psycopg2://rag_user:rag_password@postgres:5432/rag_db
AIRFLOW__CORE__EXECUTOR=LocalExecutor
POSTGRES_DATABASE_URL=postgresql+psycopg2://rag_user:rag_password@postgres:5432/rag_db
PYTHONPATH=/opt/airflow/src

连接串里的 host 是 postgres 而不是 localhost——容器之间走 rag-network 的服务名解析,这是新手最常写错的一处。LocalExecutor 表示任务在本地并行,适合单机;PYTHONPATH=/opt/airflow/src 让 DAG 能 import 到宿主机挂进来的 src/ 代码,所以 DAG 里的业务逻辑和你本地开发的是同一份。

容器本身跑在 airflow 用户(50000:0)下,log 用 named volume 而不是 bind mount。README 把这两条归在"避免权限问题"和"避免 bind mount 冲突"下,是跨平台部署踩出来的经验。

airflow/
├── README.md                           # This file
├── Dockerfile                          # Custom Airflow container with dependencies
├── requirements-airflow.txt            # Python dependencies for DAGs
└── dags/
    ├── hello_world_dag.py             # Week 1 health check DAG
    ├── arxiv_paper_ingestion.py       # Week 2 production ingestion DAG
    └── arxiv_ingestion/
        └── tasks.py                   # Production pipeline tasks with async processing

有一处文档口径要留心:根 README 和 Week 1 材料写的是 Apache Airflow 3.0,airflow/README.md 里的容器说明写的是 Python 3.12 搭配 Apache Airflow 2.10.3,两处对不上,以 airflow/Dockerfile 的实际基础镜像为准。这种小矛盾在迭代中的开源课程里很常见,也提醒你别把 README 当版本事实的唯一来源。

排障:内存、端口与冷启动

按课程统计,新手卡住的地方基本是三类。

服务起不来。 官方口径是等 2-3 分钟,然后看日志:

docker compose logs [service-name]

第一次 up --build 要拉 OpenSearch 这类大镜像、build Airflow 镜像、初始化数据库,2-3 分钟内某些容器还在 starting 是正常的,别急着 down 重来。判断"真的卡住"要看日志有没有推进,而不是 ps 里的状态字符串。

端口冲突。 README 点名的四个端口是 8000、8080、5432、9200,分别对应 FastAPI、Airflow、PostgreSQL、OpenSearch。宿主机上如果已有本地 Postgres 占着 5432,或别的项目占着 8000,容器会起来但端口映射失败。9200 被点名而 5601 没有,是因为数据端口不通时 Dashboards 必然也不可用,先查 9200 就够了。

内存不够。 解法是给 Docker Desktop 调大内存分配,而不是去砍服务。五个容器里 OpenSearch 是内存大户,Airflow 次之。调完还紧张就删掉 Ollama 的模型,这是最不影响本周进度的减负方式。

彻底重置用一条命令,它会连 volume 一起清掉,数据库里的数据也会没:

docker compose down --volumes && docker compose up --build -d

钱的部分不用担心:课程完全免费,本地开发 $0,只有可选的外部 LLM 服务会产生大约 $2-5 的开销。你真正需要投入的是 2-4 小时和 20GB 磁盘。

等你把 curl http://localhost:8000/api/v1/health 跑绿,第 1 周就结束了。第 2 周会在这套底座上加第一个真正的业务 DAG:用 arXiv API 抓论文、用 Docling 解析 PDF、把元数据和正文写进 PostgreSQL——那时你会庆幸现在把 Airflow 的权限、volume 和连接串都理顺了。

参考链接