在 Docker 中使用 uv
入门
Tip
查看 uv-docker-example
项目,了解在 Docker 中使用 uv 构建应用程序的最佳实践示例。
uv 提供了 distroless Docker 镜像和基于流行基础镜像的衍生镜像,前者可用于将 uv 二进制文件复制到您自己的镜像构建中,后者可用于在容器中使用 uv。distroless 镜像仅包含 uv 二进制文件。相比之下,衍生镜像则包含预安装了 uv 的操作系统。
例如,要使用基于 Debian 的镜像在容器中运行 uv:
可用镜像
以下是可用的无发行版镜像:
- ghcr.io/astral-sh/uv:latest
- ghcr.io/astral-sh/uv:{major}.{minor}.{patch}
,例如 ghcr.io/astral-sh/uv:0.7.4
- ghcr.io/astral-sh/uv:{major}.{minor}
,例如 ghcr.io/astral-sh/uv:0.7
(最新的补丁版本)
以下是可用的衍生镜像:
- 基于
alpine:3.21
:ghcr.io/astral-sh/uv:alpine
ghcr.io/astral-sh/uv:alpine3.21
- 基于
debian:bookworm-slim
:ghcr.io/astral-sh/uv:debian-slim
ghcr.io/astral-sh/uv:bookworm-slim
- 基于
buildpack-deps:bookworm
:ghcr.io/astral-sh/uv:debian
ghcr.io/astral-sh/uv:bookworm
- 基于
python3.x-alpine
:ghcr.io/astral-sh/uv:python3.14-rc-alpine
ghcr.io/astral-sh/uv:python3.13-alpine
ghcr.io/astral-sh/uv:python3.12-alpine
ghcr.io/astral-sh/uv:python3.11-alpine
ghcr.io/astral-sh/uv:python3.10-alpine
ghcr.io/astral-sh/uv:python3.9-alpine
ghcr.io/astral-sh/uv:python3.8-alpine
- 基于
python3.x-bookworm
:ghcr.io/astral-sh/uv:python3.14-rc-bookworm
ghcr.io/astral-sh/uv:python3.13-bookworm
ghcr.io/astral-sh/uv:python3.12-bookworm
ghcr.io/astral-sh/uv:python3.11-bookworm
ghcr.io/astral-sh/uv:python3.10-bookworm
ghcr.io/astral-sh/uv:python3.9-bookworm
ghcr.io/astral-sh/uv:python3.8-bookworm
- 基于
python3.x-slim-bookworm
:ghcr.io/astral-sh/uv:python3.14-rc-bookworm-slim
ghcr.io/astral-sh/uv:python3.13-bookworm-slim
ghcr.io/astral-sh/uv:python3.12-bookworm-slim
ghcr.io/astral-sh/uv:python3.11-bookworm-slim
ghcr.io/astral-sh/uv:python3.10-bookworm-slim
ghcr.io/astral-sh/uv:python3.9-bookworm-slim
ghcr.io/astral-sh/uv:python3.8-bookworm-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.7.4-alpine
。
更多详细信息,请参阅 GitHub 容器 页面。
安装 uv
可以使用上述已预装 uv 的镜像之一,或者从官方无发行版(distroless)的 Docker 镜像中复制二进制文件来安装 uv:
或者,使用安装程序:
FROM python:3.12-slim-bookworm
# 安装程序需要 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 版本,例如:
提示
虽然上面的 Dockerfile 示例指定了特定的标签,但也可以指定特定的 SHA256。在需要可复现构建的环境中,指定特定的 SHA256 被视为最佳实践,因为标签可以在不同的提交 SHA 之间移动。
或者,使用安装程序:
安装项目
如果你使用 uv 来管理项目,可以将其复制到镜像中并进行安装:
将项目同步到新环境中,确保锁定文件是最新的
WORKDIR /app RUN uv sync --locked
!!! important
最佳实践是将 `.venv` 添加到仓库中的 [`.dockerignore` 文件](https://docs.docker.com/build/concepts/context/#dockerignore-files) 中,以防止其被包含在镜像构建中。项目虚拟环境依赖于本地平台,应在镜像中从头创建。
然后,要默认启动应用程序:
```dockerfile title="Dockerfile"
# 假设项目提供了 `my_app` 命令
CMD ["uv", "run", "my_app"]
Tip
最佳实践是使用 中间层 来分离依赖项和项目本身的安装,以缩短 Docker 镜像构建时间。
在 uv-docker-example
项目 中查看完整示例。
使用环境
项目安装完成后,你可以通过将项目虚拟环境的二进制目录置于路径开头来 激活 项目虚拟环境:
或者,对于任何需要该环境的命令,你可以使用 uv run
:
Tip
或者,在同步之前设置
UV_PROJECT_ENVIRONMENT
设置,以便安装到系统 Python 环境中,完全跳过环境激活。
使用已安装的工具
要使用已安装的工具,请确保工具二进制目录在路径中:
$ docker run -it $(docker build -q .) /bin/bash -c "cowsay -t hello"
_____
| hello |
=====
\
\
^__^
(oo)\_______
(__)\ )\/\
||----w |
|| ||
注意
可以通过在容器中运行 uv tool dir --bin
命令来确定工具二进制目录的位置。
或者,也可以将其设置为一个固定位置:
在 ARM musl 镜像中安装 Python
虽然如果镜像中没有兼容的 Python 版本,uv 会尝试安装兼容的 Python 版本,但 uv 目前还不支持在 ARM 架构的 musl Linux 上安装 Python。例如,如果你在 ARM 机器上使用 Alpine Linux 基础镜像,可能需要使用系统包管理器添加它:
在容器中进行开发
在开发时,将项目目录挂载到容器中很有用。通过这种设置,对项目的更改可以立即反映在容器化服务中,而无需重新构建镜像。但是,不要在挂载中包含项目虚拟环境(.venv
),因为虚拟环境是特定于平台的,应该保留为镜像构建的虚拟环境。
使用 docker run
挂载项目
将项目(位于工作目录中)绑定挂载到 /app
,同时使用匿名卷保留 .venv
目录:
Tip
包含 --rm
标志是为了确保容器退出时,容器和匿名卷能被清理。
在uv-docker-example
项目中查看完整示例。
结合 docker compose
配置 watch
在使用 Docker Compose 时,有更复杂的工具可用于容器开发。
watch
选项比绑定挂载更具粒度控制,并且支持在文件发生变化时触发对容器化服务的更新。
注意
此功能需要 Compose 2.22.0,它与 Docker Desktop 4.24 捆绑在一起。
在你的
Docker Compose 文件
中配置 watch
,以挂载项目目录但不同步项目虚拟环境,并在配置发生变化时重建镜像:
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
标志:
或者,你可以设置 UV_COMPILE_BYTECODE
环境变量,以确保 Dockerfile 中的所有命令都编译字节码:
缓存
可以使用缓存挂载来提高跨构建的性能:
更改默认的 UV_LINK_MODE
可以消除关于无法使用硬链接的警告,因为缓存和同步目标位于不同的文件系统上。
如果你不挂载缓存,可以通过使用 --no-cache
标志或设置 UV_NO_CACHE
来减小镜像大小。
注意
可以通过在容器中运行 uv cache dir
命令来确定缓存目录的位置。
或者,也可以将缓存设置为固定位置:
中间层
如果你使用 uv 来管理项目,可以通过 --no-install
选项将传递依赖项的安装移动到单独的层中,从而缩短构建时间。
uv sync --no-install-project
将安装项目的依赖项,但不会安装项目本身。由于项目经常变化,但其依赖项通常是静态的,因此这可以节省大量时间。
# 安装 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
将项目复制到镜像中
ADD . /app
同步项目
RUN --mount=type=cache,target=/root/.cache/uv \ uv sync --locked
请注意,需要 `pyproject.toml` 来确定项目根目录和名称,但直到最后执行 `uv sync` 命令时,项目_内容_才会被复制到镜像中。
!!! tip
如果你正在使用[工作区](../../concepts/projects/workspaces.md),那么使用 `--no-install-workspace` 标志,它会排除项目以及任何工作区成员。
如果你想在同步时排除特定的包,使用 `--no-install-package <name>`。
### 非可编辑安装
默认情况下,uv 以可编辑模式安装项目和工作区成员,这样对源代码的更改会立即反映在环境中。
`uv sync` 和 `uv run` 都接受 `--no-editable` 标志,该标志指示 uv 以非可编辑模式安装项目,消除对源代码的任何依赖。
在多阶段 Docker 镜像的场景中,`--no-editable` 可用于在某一阶段将项目包含在已同步的虚拟环境中,然后仅将虚拟环境(而非源代码)复制到最终镜像中。
例如:
```dockerfile title="Dockerfile"
# 安装 uv
FROM python:3.12-slim AS builder
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 --no-editable
# 将项目复制到中间镜像中
ADD . /app
同步项目
RUN --mount=type=cache,target=/root/.cache/uv \ uv sync --locked --no-editable
FROM python:3.12-slim
复制环境,但不复制源代码
COPY --from=builder --chown=app:app /app/.venv /app/.venv
运行应用程序
CMD ["/app/.venv/bin/hello"]
### 临时使用 uv
如果最终镜像中不需要 uv,可以在每次调用时挂载二进制文件:
```dockerfile title="Dockerfile"
RUN --mount=from=ghcr.io/astral-sh/uv,source=/uv,target=/bin/uv \
uv sync
使用 pip 接口
安装包
在这种情况下,可以安全地使用系统 Python 环境,因为容器已经是隔离的。
可以使用 --system
标志在系统环境中安装:
要默认使用系统 Python 环境,可以设置 UV_SYSTEM_PYTHON
变量:
或者,可以创建并激活一个虚拟环境:
RUN uv venv /opt/venv
# 自动使用虚拟环境
ENV VIRTUAL_ENV=/opt/venv
# 将环境中的可执行文件路径置于 PATH 前端
ENV PATH="/opt/venv/bin:$PATH"
使用虚拟环境时,uv 调用中应省略 --system
标志:
安装需求文件
要安装需求文件,需将其复制到容器中:
安装项目
在安装项目及其依赖项时,最佳实践是将依赖项的复制与其余源代码的复制分开。这样一来,项目的依赖项(不常变动)就可以与项目本身(变动频繁)分开缓存。
COPY pyproject.toml .
RUN uv pip install -r pyproject.toml
COPY . .
RUN uv pip install -e .
验证镜像来源
Docker 镜像在构建过程中会进行签名,以证明其来源。这些证明可用于验证镜像是否由官方渠道生成。
例如,你可以使用 GitHub CLI 工具 gh
验证证明:
$ 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
的(多平台)清单验证证明文件:
$ 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.7.4
,或者(更好的是)特定镜像摘要,例如 ghcr.io/astral-sh/uv:0.5.27@sha256:5adf09a5a526f380237408032a9308000d14d5947eafa687ad6c6a2476787b4f
。