uv 在 Docker 中做多阶段构建:如何优化构建速度与镜像体积
uv 在 Docker 中做多阶段构建如何优化构建速度与镜像体积【免费下载链接】uvAn extremely fast Python package and project manager, written in Rust.项目地址: https://gitcode.com/GitHub_Trending/uv/uv当你用 uv 管理 Python 项目项目里有pyproject.toml和uv.lock并在 Docker 中构建应用镜像时最朴素的做法是把源码COPY进镜像然后uv sync。问题是项目代码每次构建都变化而它的依赖很少变同时默认的 editable 安装让.venv依赖源码导致最终镜像必须带着整个源码目录。这篇文章基于 uv 官方文档 docs/guides/integration/docker.md给出一条可照做的路径用缓存挂载和中间层缩短构建时间用多阶段构建加--no-editable让最终镜像只包含虚拟环境从而减小体积。准备条件.dockerignore 与基础镜像两个前提来自文档的明确要求把.venv加入.dockerignore。项目虚拟环境依赖本地平台必须留在构建上下文之外在镜像里从零创建。基础镜像自带 Python 时用python:3.12-slim这类镜像并通过 distroless 官方镜像拷入 uv 二进制。uv 提供两种官方镜像distroless 镜像只含 uv 二进制用于拷入自己的构建和基于 alpine/debian 等衍生镜像uv 已预装可直接当基础镜像用。本文主线用前者。未优化前的基线写法单阶段仅作对照# Copy the project into the image COPY . /app # Disable development dependencies ENV UV_NO_DEV1 # Sync the project into a new environment, asserting the lockfile is up to date WORKDIR /app RUN uv sync --locked注意其中的UV_NO_DEV1生产镜像中禁用开发依赖优化后的构建同样应保留这一项。在构建阶段安装 uv固定版本从官方 distroless 镜像拷入二进制是最简单的安装方式FROM python:3.12-slim-trixie COPY --fromghcr.io/astral-sh/uv:latest /uv /uvx /bin/文档建议固定到具体 uv 版本例如COPY --fromghcr.io/astral-sh/uv:0.12.13 /uv /uvx /bin/对于要求可复现构建的环境文档进一步推荐固定 SHA256因为 tag 可以指向不同的 commit# e.g., using a hash from a previous release COPY --fromghcr.io/astral-sh/uvsha256:2381d6aa60c326b71fd40023f921a0a3b8f91b14d5db6b90402e65a635053709 /uv /uvx /bin/构建速度与镜像体积的完整主路径两阶段 Dockerfile下面这份 Dockerfile 是文档中“多阶段 --no-editable”示例的完整保留它同时用了缓存挂载和依赖/项目分层是本文的主路径# Install uv FROM python:3.12-slim AS builder COPY --fromghcr.io/astral-sh/uv:latest /uv /uvx /bin/ # Use the system Python across both stages ENV UV_PYTHON_DOWNLOADS0 # Change the working directory to the app directory WORKDIR /app # Install dependencies RUN --mounttypecache,target/root/.cache/uv \ --mounttypebind,sourceuv.lock,targetuv.lock \ --mounttypebind,sourcepyproject.toml,targetpyproject.toml \ uv sync --locked --no-install-project --no-editable # Copy the project into the intermediate image COPY . /app # Sync the project RUN --mounttypecache,target/root/.cache/uv \ uv sync --locked --no-editable FROM python:3.12-slim # Copy the environment, but not the source code COPY --frombuilder /app/.venv /app/.venv # Run the application CMD [/app/.venv/bin/hello]逐段说明它为什么同时解决“速度”和“体积”第一段RUN只绑定挂载uv.lock和pyproject.toml用--no-install-project只装依赖、不装项目本身。依赖一般稳定、项目代码频繁变化把依赖安装单独成层后改代码不会触发依赖重装。pyproject.toml在这里的作用是供 uv 识别项目根目录和项目名此时镜像里还没有项目内容。第二段COPY . /app之后再uv sync --locked把项目本身同步进环境。--no-editable是体积优化的关键。uv 默认以 editable 模式安装项目源码改动立即生效此时.venv依赖源码目录加上--no-editable后项目以非 editable 模式安装.venv自包含最终阶段只需COPY --frombuilder /app/.venv /app/.venv源码不进入最终镜像。ENV UV_PYTHON_DOWNLOADS0表示两个阶段都使用基础镜像自带的系统 Python保证 builder 阶段创建的.venv在最终镜像里路径和解释器都一致。--mounttypecache,target/root/.cache/uv把 uv 缓存挂为构建缓存跨构建复用下载与安装结果。缓存目录位置可在容器内用uv cache dir查看也可以用ENV UV_CACHE_DIR/opt/uv-cache/固定。最后一行CMD [/app/.venv/bin/hello]中的hello是文档示例里的入口命令替换为你项目实际提供的应用入口uv run场景下文档的写法是CMD [uv, run, my_app]其中my_app同样是示例命令名。如果你还想在同步时排除个别包文档给出了--no-install-package name参数。可选分支workspace 项目的分层如果项目是 uv workspace上面的两步 sync 需要两处改动文档明确说明第一次 sync 用--frozen而不是--locked——因为还没有拷贝各 workspace 成员的pyproject.tomluv 无法校验锁文件是否最新用--no-install-workspace排除项目本身以及所有 workspace 成员。# Install uv FROM python:3.12-slim COPY --fromghcr.io/astral-sh/uv:latest /uv /uvx /bin/ WORKDIR /app RUN --mounttypecache,target/root/.cache/uv \ --mounttypebind,sourceuv.lock,targetuv.lock \ --mounttypebind,sourcepyproject.toml,targetpyproject.toml \ uv sync --frozen --no-install-workspace COPY . /app RUN --mounttypecache,target/root/.cache/uv \ uv sync --locked所有成员拷贝完成后的第二次 sync 仍可用--locked校验锁文件覆盖全部 workspace 成员。缓存相关的补充配置UV_LINK_MODEcopy缓存挂载和同步目标位于不同文件系统时uv 会提示无法链接文件设置该变量可以消除这类告警。如果不想挂载缓存则可以用--no-cache标志或UV_NO_CACHE让镜像不携带 uv 缓存直接减小体积。UV_PYTHON_CACHE_DIR托管 Python 的安装默认在安装前不缓存设置该变量如ENV UV_PYTHON_CACHE_DIR/root/.cache/uv/python并配合缓存挂载后uv python install也能跨构建复用。临时挂载 uv如果最终镜像完全不需要 uv可以用 build 挂载代替COPY --from二进制只在单条RUN中可用RUN --mountfromghcr.io/astral-sh/uv,source/uv,target/bin/uv \ uv sync可选权衡字节码编译针对生产镜像文档把字节码编译列为一项可选项并明确它是一笔权衡通常能改善启动时间代价是更长的安装时间和更大的镜像体积。如果启动时间是你的目标用标志位RUN uv python install --compile-bytecode RUN uv sync --compile-bytecode或者用环境变量让 Dockerfile 内所有命令都编译字节码ENV UV_COMPILE_BYTECODE1注意文档给出的边界uv python install只会为托管的 Python 版本编译标准库非托管版本例如官方python基础镜像的标准库是否预编译由发行方决定官方python镜像没有编译过的标准库。验证构建结果构建阶段自带锁文件校验uv sync --locked会断言uv.lock与项目元数据一致不一致时 uv 直接报错、不会尝试更新锁文件。本地可以先用uv lock --check预检见 docs/concepts/projects/sync.md。构建失败且报锁文件过期时先在项目里更新uv.lock再重新构建。运行验证构建成功后直接运行镜像入口命令应正常启动。文档中验证构建产物的用法是把构建结果交给docker run例如$ docker run -it $(docker build -q .) /bin/bash -c cowsay -t hello其中docker build -q输出镜像 ID 并作为docker run的参数对你自己的镜像等价做法是构建后用docker run执行其CMD上面的/app/.venv/bin/你的入口确认应用启动且依赖可导入。体积判断文档没有给出固定的体积数值最终镜像小是结构性的——最终阶段只从 builder 复制了/app/.venv不包含源码和 uv 二进制。边界与参考workspace 之外的单项目场景按主路径即可--frozen/--no-install-workspace只用于 workspace。字节码编译会增大镜像只在启动时间敏感时启用。本仓库根目录的 Dockerfile 是 uv 自身构建的多阶段实例build 阶段用 cargo 交叉编译出uv/uvx最终阶段FROM scratch只拷入两个二进制第 70–73 行与本文“最终阶段只拷必要产物”的思路一致可作为阅读参考。如果还需要验证官方镜像的来源文档提供了gh attestation verify与cosign的校验方式建议针对具体版本 tag 或 digest 而不是latest执行。【免费下载链接】uvAn extremely fast Python package and project manager, written in Rust.项目地址: https://gitcode.com/GitHub_Trending/uv/uv创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考