官方使用
FastAPI 官方 Dockerfile 中用了两次:
python
RUN --mount=type=cache,target=/root/.cache/uv \
--mount=type=bind,source=uv.lock,target=uv.lock \
--mount=type=bind,source=pyproject.toml,target=pyproject.toml \
uv sync --frozen --no-install-project # ✅ 第一次
RUN --mount=type=cache,target=/root/.cache/uv \
uv sync # ✅ 第二次
这是 uv 官方推荐的优化构建流程,目的是 最大程度复用缓存,加快构建速度,同时支持源码变化重装依赖。
🔍 第一次 uv sync --frozen --no-install-project
📌 作用是:
• ✅ 只安装依赖(不安装你项目代码)
• ✅ 使用 lock 文件精确控制版本
• ✅ 构建中间层缓存(intermediate layer)
✔️ 只要你没改 pyproject.toml 或 uv.lock,这一层永远不需要重建!
这是 Docker 构建中非常重要的一环 ------ 利用文件不变来缓存 layer,加快构建速度。
🔄 第二次 uv sync
📌 作用是:
• ✅ 安装你项目的源码(也就是 app/ 本身)
• 因为第一次用了 --no-install-project,项目代码没有装进去
🚨 如果你更新了项目源码,但没改 pyproject.toml,就只会触发第二次构建。
你可以类比为:
python
# 第一次,只安装依赖,不装项目代码
pip install --no-deps --require-hashes -r requirements.txt
# 第二次,把你的项目当成 package 装进环境里
pip install .
# 是 Python 项目中非常重要的一步,表示 将你当前目录(.)下的项目当作一个 Package 安装到当前 Python 环境中。
这样你每次写代码改的是 .py 文件而不是依赖,Docker 就不会重新构建冗余层 ✅
第一次 uv sync --frozen --no-install-project | 第二次 uv sync |
---|---|
安装依赖项(构建缓存层) | 安装项目本身 |
不会安装你 app/ 源码 | 会装源码到虚拟环境 |
对应 lock 文件(更可控) | 快速重建项目层 |
有利于构建性能 | 有利于热更新和本地测试 |
可以,但你就会失去 中间层缓存优化的好处,每次改 .py 都会重新装全部依赖 ❌
✅ 为什么 FastAPI 官方不写 COPY?
因为他们使用的是 BuildKit 的多阶段构建 + bind mount 优化方案:
• 第一次 RUN uv sync --frozen --no-install-project 使用挂载的 pyproject.toml 和 uv.lock 安装依赖(但不安装项目)
• 最终会在后面阶段,再使用 COPY . . 把项目代码拷贝进来
步骤 | 是否合理 | 说明 |
---|---|---|
设置基础镜像 | ✅ | python:3.12-slim 节省体积 |
使用 uv 构建 | ✅ | 快速高效、现代依赖工具 |
缓存依赖安装层 | ✅ | 加速构建、复用缓存 |
分阶段 COPY + uv sync | ✅ | 减少变更触发重装 |
PYTHONPATH=. | ✅ | 适配 from app.xxx 导入 |
CMD 使用 uvicorn | ✅ | 适合线上部署、ASGI 原生 |
开箱即用的Dockfile
python
FROM python:3.12-slim AS builder
WORKDIR /app
# 设置时区为 UTC+8
ENV TZ=Asia/Shanghai
RUN ln -snf /usr/share/zoneinfo/$TZ /etc/localtime && echo $TZ > /etc/timezone
COPY --from=ghcr.io/astral-sh/uv:0.5.11 /uv /uvx /bin/
ENV PATH="/app/.venv/bin:$PATH"
ENV UV_COMPILE_BYTECODE=1
ENV UV_LINK_MODE=copy
# 复制项目文件到容器中
COPY . /app/
RUN --mount=type=cache,target=/root/.cache/uv \
--mount=type=bind,source=uv.lock,target=uv.lock \
--mount=type=bind,source=pyproject.toml,target=pyproject.toml \
uv sync --frozen --no-install-project
ENV PYTHONPATH=.
CMD ["uvicorn", "app.main:app", "--host", "0.0.0.0", "--port", "8000", "--log-level", "warning"]