uv on Docker をやっている
TL;DR
本記事に記載するコードは下記のリポジトリよりダウンロードいただけます。(⭐おまちしております)
コンテナの実行。
uv_on_docker_playground (main) » docker run -it uv_on_docker
Bytecode compiled 1 file in 59ms
Hello World
開発用コンテナとして利用する。
$ docker compose watch app
$ docker compose exec app bash
$ root@393b1996df7c:/app# uv run hello
Bytecode compiled 1 file in 39ms
# Hello World
前書き
以前、uv on Dockerをやっているという記事を投稿しました。
この時点では、「使ってみた程度」のレベルで投稿していたのですが、定期的にアクセスが来ています。今見ると色々修正したい部分があるので新しく書き直すことにしました。
※ 本記事ではuvとは何か、uvコマンドの使い方は解説しません。GitHubのドキュメント貼るので読んでみてください。
Dockerfile
次は、開発用コンテナの設定です。(のちに本番に有効な設定を紹介します)
FROM ghcr.io/astral-sh/uv:python3.12-bookworm-slim
WORKDIR /app
ENV UV_COMPILE_BYTECODE=1
ENV UV_LINK_MODE=copy
RUN \
uv sync --frozen --no-install-project --no-dev
ADD . /app
RUN \
uv sync --frozen
ENV PATH="/app/.venv/bin:$PATH"
ENTRYPOINT []
CMD ["uv", "run", "hello"]
Docker Image
DockerのImageですが有用なものが配布されています。distroless
, alpine
, debian
と複数の選択肢があります。今回はdebian
を利用しています。
# distroless
ghcr.io/astral-sh/uv:latest
ghcr.io/astral-sh/uv:{major}.{minor}.{patch}, e.g., ghcr.io/astral-sh/uv:0.6.14
ghcr.io/astral-sh/uv:{major}.{minor}, e.g., ghcr.io/astral-sh/uv:0.6 (the latest patch version)
# alpine:3.20:
ghcr.io/astral-sh/uv:alpine
ghcr.io/astral-sh/uv:alpine3.20
# Based on debian:bookworm-slim:
ghcr.io/astral-sh/uv:debian-slim
ghcr.io/astral-sh/uv:bookworm-slim
# Based on buildpack-deps:bookworm:
ghcr.io/astral-sh/uv:debian
ghcr.io/astral-sh/uv:bookworm
# Based on python3.x-alpine:
ghcr.io/astral-sh/uv:python3.13-alpine
ghcr.io/astral-sh/uv:python3.12-alpine
...
# Based on python3.x-bookworm:
ghcr.io/astral-sh/uv:python3.13-bookworm
ghcr.io/astral-sh/uv:python3.12-bookworm
...
# Based on python3.x-slim-bookworm:
ghcr.io/astral-sh/uv:python3.13-bookworm-slim
ghcr.io/astral-sh/uv:python3.12-bookworm-slim
...
全て確認する場合は、次のリンクをご参照ください。
キャッシュ
uv sync --no-install-project
は、プロジェクト自体ではなく、その依存関係のみをインストールします。プロジェクトと異なり、依存関係は一般的に静的なのでこの設定を利用しています。
RUN \
uv sync --frozen --no-install-project --no-dev
なお、本番環境などで実行するときは --no-editable
オプションが有用です。これを利用すると、あるステージで同期された仮想環境にプロジェクトを含め、その後最終イメージへはソースコードではなく仮想環境のみをコピーすることが可能です。
マルチステージビルドの事例
# Install uv
FROM python:3.12-slim AS builder
COPY /uv /uvx /bin/
# Change the working directory to the `app` directory
WORKDIR /app
# Install dependencies
RUN \
uv sync --frozen --no-install-project --no-editable
# Copy the project into the intermediate image
ADD . /app
# Sync the project
RUN \
uv sync --frozen --no-editable
FROM python:3.12-slim
# Copy the environment, but not the source code
COPY /app/.venv /app/.venv
# Run the application
CMD ["/app/.venv/bin/hello"]
ByteCode Compilation
uvでは sync
するときに Python Interpreterの中間表現にビルドするかを指定できます。ビルドする関係でインストール時の実行時間が伸びますが、パッケージの初回呼び出しを高速化可能です。
RUN uv sync --compile-bytecode
また次のように記述するとDockerfile
内で実行されるすべての命令がこの設定の影響を受け、結果として全体がコンパイル対象になります。
ENV UV_COMPILE_BYTECODE=1
compose.yml
services:
app:
build: .
tty: true
working_dir: /app
command: ["tail", "-f", "/dev/null"]
develop:
watch:
- action: sync
path: .
target: /app
ignore:
- .venv/
- action: rebuild
path: ./uv.lock
watch
の部分はお好みで。ファイル変更の監視して、差分を検知すると自動でsyncやrebuildが走ります。
pyproject.toml
[project]
name = "app"
version = "0.1.0"
description = "Add your description here"
readme = "README.md"
requires-python = ">=3.12"
dependencies = []
[build-system]
requires = ["hatchling"]
build-backend = "hatchling.build"
[project.scripts]
hello = "app:hello"
特に手を入れていない設定です。今回紹介したプロジェクトは以前のuv
のバージョンで初期化したため、hello.py
になっています。現在(>=v0.6.0
)では、uv init project
するとmain.py
が生成されます。
Prior to v0.6.0, uv created a file named hello.py instead of main.py.
Dependabot
Dependabotで簡単に依存関係の管理ができます。Dependabotはuvの依存関係まで含めたバージョン管理用のファイル uv.lock
をサポートしています。次のように.github/dependabot.yml
を設定すれば大丈夫です。
version: 2
updates:
- package-ecosystem: "github-actions"
directory: "/"
schedule:
interval: "monthly"
- package-ecosystem: "uv"
directory: "/"
schedule:
interval: "monthly"
allow:
- dependency-name: "pydantic"
ignore:
- dependency-name: "pydantic"
update-types:
- "version-update:semver-patch"
この機能は2025年3月13日にreleaseされたものです。
あとがき
uv使い始めた方の参考になれば幸いです。
なお、最近は nix develop
で開発することが多いです。Nixで開発しておけば、配布も楽なのでおすすめです。
{
description = "uv 環境の開発シェル (Python 3.13)";
inputs = {
nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable";
flake-utils.url = "github:numtide/flake-utils";
};
outputs = { self, nixpkgs, flake-utils }:
flake-utils.lib.eachDefaultSystem (system:
let
pkgs = import nixpkgs { inherit system; };
in {
devShell = pkgs.mkShell {
buildInputs = [
pkgs.python313
pkgs.uv
];
shellHook = ''
echo "uv version: $(uv --version)"
echo "python version: $(python --version)"
'';
};
});
}
参考文献
Discussion