2.7.1 在 Docker 中使用

原文链接: https://docs.astral.sh/uv/guides/integration/docker/

2.7.1 在 Docker 中使用

快速开始

提示

在 Docker 中使用 uv 构建应用的最佳实践示例,请查看 uv-docker-example 项目。

uv 同时提供无发行版(distroless)Docker 镜像和基于常见基础镜像派生的镜像。前者适合在构建自己的镜像时复制 uv 二进制文件,后者适合在容器中使用 uv。无发行版镜像除 uv 二进制文件之外不包含任何内容;相对地,派生镜像则包含预装 uv 的操作系统。

例如,使用基于 Debian 的镜像在容器中运行 uv:

1
$ docker run --rm -it ghcr.io/astral-sh/uv:debian uv --help

可用镜像

提供以下无发行版镜像:

  • ghcr.io/astral-sh/uv:latest
  • ghcr.io/astral-sh/uv:{major}.{minor}.{patch},例如 ghcr.io/astral-sh/uv:0.12.19
  • ghcr.io/astral-sh/uv:{major}.{minor},例如 ghcr.io/astral-sh/uv:0.12(最新的补丁版本)

以及以下派生镜像:

  • 基于 alpine:3.23:
    • ghcr.io/astral-sh/uv:alpine
    • ghcr.io/astral-sh/uv:alpine3.23
  • 基于 alpine:3.22:
    • ghcr.io/astral-sh/uv:alpine3.22
  • 基于 debian:trixie-slim:
    • ghcr.io/astral-sh/uv:debian-slim
    • ghcr.io/astral-sh/uv:trixie-slim
  • 基于 buildpack-deps:trixie:
    • ghcr.io/astral-sh/uv:debian
    • ghcr.io/astral-sh/uv:trixie
  • 基于 dhi.io/alpine-base:3.23:
    • ghcr.io/astral-sh/uv:alpine-dhi
    • ghcr.io/astral-sh/uv:alpine3.23-dhi
  • 基于 dhi.io/debian-base:trixie-debian13:
    • ghcr.io/astral-sh/uv:debian-dhi
    • ghcr.io/astral-sh/uv:trixie-dhi
  • 基于 dhi/python:3.x:
    • ghcr.io/astral-sh/uv:python3.14-dhi
    • ghcr.io/astral-sh/uv:python3.13-dhi
    • ghcr.io/astral-sh/uv:python3.12-dhi
    • ghcr.io/astral-sh/uv:python3.11-dhi
    • ghcr.io/astral-sh/uv:python3.10-dhi
  • 基于 python3.x-alpine:
    • ghcr.io/astral-sh/uv:python3.15-rc-alpine
    • ghcr.io/astral-sh/uv:python3.15-rc-alpine3.23
    • ghcr.io/astral-sh/uv:python3.14-alpine
    • ghcr.io/astral-sh/uv:python3.14-alpine3.23
    • ghcr.io/astral-sh/uv:python3.13-alpine
    • ghcr.io/astral-sh/uv:python3.13-alpine3.23
    • ghcr.io/astral-sh/uv:python3.12-alpine
    • ghcr.io/astral-sh/uv:python3.12-alpine3.23
    • ghcr.io/astral-sh/uv:python3.11-alpine
    • ghcr.io/astral-sh/uv:python3.11-alpine3.23
    • ghcr.io/astral-sh/uv:python3.10-alpine
    • ghcr.io/astral-sh/uv:python3.10-alpine3.23
    • ghcr.io/astral-sh/uv:python3.9-alpine
    • ghcr.io/astral-sh/uv:python3.9-alpine3.22
  • 基于 python3.x-trixie:
    • ghcr.io/astral-sh/uv:python3.15-rc-trixie
    • ghcr.io/astral-sh/uv:python3.14-trixie
    • ghcr.io/astral-sh/uv:python3.13-trixie
    • ghcr.io/astral-sh/uv:python3.12-trixie
    • ghcr.io/astral-sh/uv:python3.11-trixie
    • ghcr.io/astral-sh/uv:python3.10-trixie
    • ghcr.io/astral-sh/uv:python3.9-trixie
  • 基于 python3.x-slim-trixie:
    • ghcr.io/astral-sh/uv:python3.15-rc-trixie-slim
    • ghcr.io/astral-sh/uv:python3.14-trixie-slim
    • ghcr.io/astral-sh/uv:python3.13-trixie-slim
    • ghcr.io/astral-sh/uv:python3.12-trixie-slim
    • ghcr.io/astral-sh/uv:python3.11-trixie-slim
    • ghcr.io/astral-sh/uv:python3.10-trixie-slim
    • ghcr.io/astral-sh/uv:python3.9-trixie-slim

与无发行版镜像一样,每个派生镜像也会以 uv 版本标签发布,形式为 ghcr.io/astral-sh/uv:{major}.{minor}.{patch}-{base} 和 ghcr.io/astral-sh/uv:{major}.{minor}-{base},例如 ghcr.io/astral-sh/uv:0.12.19-alpine。

此外,从 0.8 开始,每个派生镜像还会把 UV_TOOL_BIN_DIR 设置为 /usr/local/bin,以便 uv tool install 在默认用户下正常工作。

更多细节请参阅 GitHub Container 页面。

安装 uv

使用上面预装 uv 的镜像之一,或者从官方无发行版 Docker 镜像中复制二进制文件来安装 uv:

1
2
3
# Dockerfile
FROM python:3.12-slim-trixie
COPY --from=ghcr.io/astral-sh/uv:latest /uv /uvx /bin/

或者使用安装器:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
# Dockerfile
FROM python:3.12-slim-trixie

# 安装器需要 curl(以及证书)来下载发布归档
RUN apt-get update && apt-get install -y --no-install-recommends curl ca-certificates

# 下载最新的安装器
ADD https://astral.sh/uv/install.sh /uv-installer.sh

# 运行安装器并删除它
RUN sh /uv-installer.sh && rm /uv-installer.sh

# 确保安装的二进制文件在 `PATH` 中
ENV PATH="/root/.local/bin/:$PATH"

注意这要求 curl 可用。

无论采用哪种方式,最佳实践都是固定到特定的 uv 版本,例如:

1
COPY --from=ghcr.io/astral-sh/uv:0.12.19 /uv /uvx /bin/

提示

虽然上面的 Dockerfile 示例固定到特定标签,但也可以固定特定的 SHA256。在要求可复现构建的环境中,固定特定 SHA256 被认为是最佳实践,因为标签可能被移动到不同的提交 SHA 上。

1
2
# 例如,使用此前某个版本的哈希
COPY --from=ghcr.io/astral-sh/uv@sha256:2381d6aa60c326b71fd40023f921a0a3b8f91b14d5db6b90402e65a635053709 /uv /uvx /bin/

或者使用安装器:

1
ADD https://astral.sh/uv/0.12.19/install.sh /uv-installer.sh

安装项目

如果你使用 uv 管理项目,可以把项目复制到镜像中并安装它:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
# Dockerfile
# 把项目复制到镜像中
COPY . /app

# 禁用开发依赖
ENV UV_NO_DEV=1

# 把项目同步到新环境,并断言锁文件是最新的
WORKDIR /app
RUN uv sync --locked

重要

最佳实践是把 .venv 加入仓库中的 .dockerignore 文件,以免它被包含进镜像构建。项目虚拟环境依赖本地平台,应在镜像中从头创建。

然后,让应用默认启动:

1
2
3
# Dockerfile
# 假设项目提供了 `my_app` 命令
CMD ["uv", "run", "my_app"]

提示

最佳实践是使用中间层把依赖安装与项目本身的安装分开,以缩短 Docker 镜像的构建时间。

完整示例请查看 uv-docker-example 项目。

使用环境

项目安装完成后,你可以把项目虚拟环境的二进制目录放到路径最前面来激活它:

1
2
# Dockerfile
ENV PATH="/app/.venv/bin:$PATH"

或者,对任何需要该环境的命令都使用 uv run:

1
2
# Dockerfile
RUN uv run some_script.py

提示

另一种方式是,在同步之前设置 UV_PROJECT_ENVIRONMENT,把包安装到系统 Python 环境并完全跳过环境激活。

使用已安装的工具

要使用已安装的工具,请确保工具二进制目录在路径中:

1
2
3
# Dockerfile
ENV PATH=/root/.local/bin:$PATH
RUN uv tool install cowsay
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
$ docker run -it $(docker build -q .) /bin/bash -c "cowsay -t hello"
  _____
| hello |
  =====
     \
      \
        ^__^
        (oo)\_______
        (__)\       )\/\
            ||----w |
            ||     ||

注意

可以在容器中运行 uv tool dir --bin 命令来确定工具二进制目录的位置。

也可以把它设置为固定位置:

1
2
# Dockerfile
ENV UV_TOOL_BIN_DIR=/opt/uv-bin/

在容器中开发

开发时,把项目目录挂载到容器中很有用。这样,对项目的修改可以立即反映到容器化服务中,而无需重新构建镜像。不过,重要的是不要把项目虚拟环境(.venv)包含在挂载中,因为虚拟环境与平台相关,应当保留为镜像构建出的那一份。

用 docker run 挂载项目

把(工作目录中的)项目绑定挂载到 /app,同时用匿名卷保留 .venv 目录:

1
$ docker run --rm --volume .:/app --volume /app/.venv [...]

提示

加上 --rm 标志是为了确保容器退出时容器和匿名卷都被清理。

完整示例请查看 uv-docker-example 项目。

用 docker compose 配置 watch

使用 Docker compose 时,可以用更精细的工具进行容器开发。watch 选项能提供比绑定挂载更细粒度的控制,并支持在文件变化时触发容器化服务的更新。

注意

该功能需要 Compose 2.22.0,它随 Docker Desktop 4.24 一起提供。

在你的 Docker compose 文件中配置 watch,以在不挂载项目虚拟环境的情况下挂载项目目录,并在配置变化时重新构建镜像:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
# compose.yaml
services:
  example:
    build: .

    # ...

    develop:
      # 创建 `watch` 配置以更新应用
      #
      watch:
        # 把工作目录与容器中的 `/app` 目录同步
        - action: sync
          path: .
          target: /app
          # 排除项目虚拟环境
          ignore:
            - .venv/

        # 当 `pyproject.toml` 变化时重新构建镜像
        - action: rebuild
          path: ./pyproject.toml

然后运行 docker compose watch,以开发配置运行容器。

完整示例请查看 uv-docker-example 项目。

优化

编译字节码

对生产镜像来说,把 Python 源文件编译为字节码通常是有益的,因为它往往能改善启动时间(代价是安装时间和镜像体积增加)。

要启用字节码编译,请使用 --compile-bytecode 标志:

1
2
3
# Dockerfile
RUN uv python install --compile-bytecode
RUN uv sync --compile-bytecode

另一种方式是设置 UV_COMPILE_BYTECODE 环境变量,以确保 Dockerfile 中的所有命令都编译字节码:

1
2
# Dockerfile
ENV UV_COMPILE_BYTECODE=1

注意

在 uv python install 期间,uv 只会编译受管 Python 版本的标准库。非受管 Python 版本的发行方决定标准库是否预编译。例如,官方 python 镜像的标准库不会被编译。

缓存

可以使用缓存挂载来提升多次构建之间的性能:

1
2
3
4
5
# Dockerfile
ENV UV_LINK_MODE=copy

RUN --mount=type=cache,target=/root/.cache/uv \
    uv sync

修改 UV_LINK_MODE 可以消除因缓存与同步目标位于不同文件系统而无法链接文件的警告。

如果你不挂载缓存,可以通过 --no-cache 标志或设置 UV_NO_CACHE 来减小镜像体积。

默认情况下,受管 Python 安装不会被缓存。可以结合缓存挂载设置 UV_PYTHON_CACHE_DIR:

1
2
3
4
5
# Dockerfile
ENV UV_PYTHON_CACHE_DIR=/root/.cache/uv/python

RUN --mount=type=cache,target=/root/.cache/uv \
    uv python install

注意

可以在容器中运行 uv cache dir 命令来确定缓存目录的位置。

也可以把缓存设置为固定位置:

1
2
# Dockerfile
ENV UV_CACHE_DIR=/opt/uv-cache/

中间层

如果你使用 uv 管理项目,可以通过 --no-install 选项把传递依赖的安装移到单独的层中,从而缩短构建时间。

uv sync --no-install-project 会安装项目的依赖但不安装项目本身。由于项目频繁变化,而它的依赖通常相对固定,这可以节省大量时间。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
# Dockerfile
# 安装 uv
FROM python:3.12-slim
COPY --from=ghcr.io/astral-sh/uv:latest /uv /uvx /bin/

# 把工作目录切换到 `app` 目录
WORKDIR /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 --locked --no-install-project

# 把项目复制到镜像中
COPY . /app

# 同步项目
RUN --mount=type=cache,target=/root/.cache/uv \
    uv sync --locked

注意,识别项目根目录和名称需要 pyproject.toml,但项目的内容直到最后的 uv sync 命令才会被复制进镜像。

提示

如果你想在同步时额外排除特定包,请使用 --no-install-package <name>。

工作区中的中间层

如果你使用工作区,则需要做几处调整:

  • 初始同步时使用 --frozen 而不是 --locked。
  • 使用 --no-install-workspace 标志,它会同时排除项目和所有工作区成员。
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
# Dockerfile
# 安装 uv
FROM python:3.12-slim
COPY --from=ghcr.io/astral-sh/uv:latest /uv /uvx /bin/

WORKDIR /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-workspace

COPY . /app

RUN --mount=type=cache,target=/root/.cache/uv \
    uv sync --locked

缺少每个工作区成员的 pyproject.toml 时,uv 无法断言 uv.lock 文件是最新的,因此我们在初始同步时使用 --frozen 而不是 --locked 来跳过该检查。在所有工作区成员都被复制之后的第二次同步,仍然可以使用 --locked,并会校验锁文件对所有工作区成员都是正确的。

非可编辑安装

默认情况下,uv 以可编辑模式安装项目和工作区成员,这样源代码的修改会立即反映到环境中。

uv sync 和 uv run 都接受 --no-editable 标志,它让 uv 以非可编辑模式安装项目,从而去除对源代码的任何依赖。

在多阶段 Docker 镜像的场景中,可以用 --no-editable 把项目包含进某一阶段同步出的虚拟环境,然后只把该虚拟环境(而不是源代码)复制到最终镜像中。

例如:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
# Dockerfile
# 安装 uv
FROM python:3.12-slim AS builder
COPY --from=ghcr.io/astral-sh/uv:latest /uv /uvx /bin/

# 两个阶段都使用系统 Python
ENV UV_PYTHON_DOWNLOADS=0

# 把工作目录切换到 `app` 目录
WORKDIR /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 --locked --no-install-project --no-editable

# 把项目复制到中间镜像中
COPY . /app

# 同步项目
RUN --mount=type=cache,target=/root/.cache/uv \
    uv sync --locked --no-editable

FROM python:3.12-slim

# 复制环境,但不复制源代码
COPY --from=builder /app/.venv /app/.venv

# 运行应用
CMD ["/app/.venv/bin/hello"]

临时使用 uv

如果最终镜像不需要 uv,可以在每次调用时挂载该二进制文件:

1
2
3
# Dockerfile
RUN --mount=from=ghcr.io/astral-sh/uv,source=/uv,target=/bin/uv \
    uv sync

使用 pip 接口

安装包

在这种场景下使用系统 Python 环境是安全的,因为容器本身已经隔离。可以用 --system 标志安装到系统环境:

1
2
# Dockerfile
RUN uv pip install --system ruff

要默认使用系统 Python 环境,请设置 UV_SYSTEM_PYTHON 变量:

1
2
# Dockerfile
ENV UV_SYSTEM_PYTHON=1

另一种方式是创建并激活虚拟环境:

1
2
3
4
5
6
# Dockerfile
RUN uv venv /opt/venv
# 自动使用该虚拟环境
ENV VIRTUAL_ENV=/opt/venv
# 把环境中的入口点放到路径最前面
ENV PATH="/opt/venv/bin:$PATH"

使用虚拟环境时,uv 调用中应省略 --system 标志:

1
2
# Dockerfile
RUN uv pip install ruff

安装 requirements

要安装 requirements 文件,请把它们复制到容器中:

1
2
3
# Dockerfile
COPY requirements.txt .
RUN uv pip install -r requirements.txt

安装项目

在安装 requirements 的同时安装项目时,最佳实践是把 requirements 的复制与其余源代码分开。这样,项目依赖(不常变化)就能与项目本身(变化非常频繁)分开缓存。

1
2
3
4
5
# Dockerfile
COPY pyproject.toml .
RUN uv pip install -r pyproject.toml
COPY . .
RUN uv pip install -e .

验证镜像来源

Docker 镜像在构建过程中会被签名,以提供来源证明。这些证明可用于验证镜像来自官方渠道。

例如,你可以用 GitHub CLI 工具 gh验证证明:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
$ gh attestation verify --owner astral-sh oci://ghcr.io/astral-sh/uv:latest
Loaded digest sha256:xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx for oci://ghcr.io/astral-sh/uv:latest
Loaded 1 attestation from GitHub API

The following policy criteria will be enforced:
- OIDC Issuer must match:................... https://token.actions.githubusercontent.com
- Source Repository Owner URI must match:... https://github.com/astral-sh
- Predicate type must match:................ https://slsa.dev/provenance/v1
- Subject Alternative Name must match regex: (?i)^https://github.com/astral-sh/

✓ Verification succeeded!

sha256:xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx was attested by:
REPO          PREDICATE_TYPE                  WORKFLOW
astral-sh/uv  https://slsa.dev/provenance/v1  .github/workflows/build-docker.yml@refs/heads/main

这说明该特定 Docker 镜像由官方 uv GitHub 发布工作流构建,并且此后未被篡改。

GitHub 证明建立在 sigstore.dev 基础设施之上。因此你也可以使用 cosign 命令针对 uv 的(多平台)清单验证证明内容:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
$ REPO=astral-sh/uv
$ gh attestation download --repo $REPO oci://ghcr.io/${REPO}:latest
Wrote attestations to file sha256:xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx.jsonl.
Any previous content has been overwritten

The trusted metadata is now available at sha256:xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx.jsonl
$ docker buildx imagetools inspect ghcr.io/${REPO}:latest --format "{{json .Manifest}}" > manifest.json
$ cosign verify-blob-attestation \
    --new-bundle-format \
    --bundle "$(jq -r .digest manifest.json).jsonl"  \
    --certificate-oidc-issuer="https://token.actions.githubusercontent.com" \
    --certificate-identity-regexp="^https://github\.com/${REPO}/.*" \
    <(jq -j '.|del(.digest,.size)' manifest.json)
Verified OK

提示

这些示例使用 latest,但最佳实践是针对特定版本标签(例如 ghcr.io/astral-sh/uv:0.12.19)验证证明,或者(更好的做法)针对具体的镜像摘要,例如 ghcr.io/astral-sh/uv:0.5.27@sha256:5adf09a5a526f380237408032a9308000d14d5947eafa687ad6c6a2476787b4f。

最后修改 September 25, 2026: 更新 (221c74c33)