世界を動かす技術を、日本語で。

Docker化されたFlask / DjangoアプリでのPipからUvへの切り替え

概要

  • uv を使うことで、Pythonプロジェクトの 依存管理とビルド速度 が大幅に向上
  • pyproject.toml で依存関係を管理し、 requirements.txt を廃止
  • Docker環境 でのセットアップ手順とコマンド例を具体的に解説
  • 非rootユーザー かつ venv未使用 の安全かつシンプルな構成
  • uvコマンド による依存追加・更新・削除の運用方法

Docker環境でuvを使ったPython依存管理高速化

  • uv を導入することで、従来のpipやvenvよりも 10倍以上のビルド速度向上 を体感
  • venv(仮想環境)を使わず非rootユーザー (例: pythonユーザー)で運用
  • pyproject.toml で依存関係を宣言し、 requirements.txt は不要
  • uvのlockファイル (uv.lock)は依存ツリーを正確に反映、pip freezeよりも優れた管理
  • Gitの差分 も最小限で移行可能、既存プロジェクトにも簡単に適用

pyproject.tomlとrequirements.txtの移行

  • pyproject.toml にトップレベル依存関係を記述
  • requirements.txt を削除し、 pyproject.tomluv.lock のみで管理
  • 例:
    • pyproject.toml
      • [project]
      • dependencies = [ "redis==5.2.1", ]
    • requirements.txt
      • redis==5.2.1(削除)

Dockerfileの変更ポイント

  • uvバイナリ(uv, uvx) を公式リリースから取得し、 /usr/local/bin/ に配置
  • pyproject.tomluv.lock をプロジェクトルートにコピー
  • 環境変数 設定
    • UV_COMPILE_BYTECODE=1: ビルド時にバイトコードを生成し、起動時の負荷軽減
    • UV_PROJECT_ENVIRONMENT="/home/python/.local": venvを作成せず、ローカルパスに直接インストール
  • 依存インストールコマンド をbin/uv-install等のシェルスクリプトに分離し、Dockerfileから実行

uvによる依存インストールスクリプト例

  • uv.lockがなければ自動生成、既存のlockファイルがあれば --frozen で厳密に再現
  • --no-install-project でプロジェクト自体のパッケージ化をスキップ(必要に応じて削除可)
  • 例:
    #!/usr/bin/env bash
    set -o errexit
    set -o pipefail
    if ! test -f uv.lock || ! uv lock --check 2>/dev/null; then
      uv lock
    fi
    uv sync --frozen --no-install-project
    

依存パッケージの追加・更新・削除運用

  • ショートカットスクリプトで各種コマンドを簡単実行
    • ./run deps:install
      • 新しいイメージをビルドし、uv.lockをボリューム経由で更新
    • ./run deps:install --no-build
      • ビルド省略、uv.lockのみ更新
    • ./run uv [...]
      • 任意のuvコマンドを直接実行
  • 依存追加例:
    • uv add mypackage --no-sync(pyproject.tomlとlockのみ更新、インストールなし)
    • その後 ./run deps:install で実際にインストール・反映
  • 依存削除例:
    • uv remove mypackage --no-sync
  • アップデート・バージョン指定も柔軟に対応
  • ./run uv:outdated で依存のアップデート状況を一覧表示

運用のポイントとまとめ

  • uvsync コマンドで依存を厳密に再現、バージョンのズレを防止
  • pyproject.tomluv.lock のみで依存関係を一元管理
  • Docker と組み合わせることで、 再現性・セキュリティ・高速化 を両立
  • pip互換のサブコマンド も用意されているが、uv独自のワークフロー推奨
  • 動画解説Git差分 も参考に、既存プロジェクトへの移行も容易

uv への移行や運用で困ったことがあれば、ぜひフィードバックを。 メール通知も低頻度でスパムなし、1クリックで解除可能。

Hackerたちの意見

uvは、pyenvやvirtualenv、pipを直接置き換えるワークフローをサポートしていることに注意する価値があるよ。lockfileやpyproject.tomlへの変更を強制しないからね。uv python pinを使うと、現在のディレクトリに.python-versionファイルが作成されるんだ。uv virtualenvは、.python-versionファイルに指定されたPythonのバージョンをダウンロードして、現在のディレクトリにそのバージョンのPythonを使った.venvという名前のvirtualenvを作成するよ(pyenvのinstallみたいにね)。uv pip install -r requirements.txtは、.venv/bin/pip install -r requirements.txtと同じように動作するんだ。uv runは、virtualenv内でコマンドを実行し、.envファイルに指定された環境変数も公開するよ(ただし、優先順位の問題には注意してね:https://github.com/astral-sh/uv/issues/9465)。

uvとその柔軟性は本当に素晴らしいよ。pipが10分かかるところを、uvは20〜30秒で処理できるからね。

でも、pyproject.tomlにPythonのバージョンを保存しているんじゃないの?それならPythonのバージョンファイルは必要なの?

+1、これがuvを使い始めた理由そのものだよ。めちゃくちゃ便利。ただ、なぜかuv pipがすごく遅いんだよね。理由はわからないけど、うちの組織が変なネットワークのことをやってるのかも。

常に最新のロックファイルを持つようにしよう。もし! test -f uv.lock || ! uv lock --check 2>/dev/nullが真なら、uv lockを実行する。

ロックファイルを持つ意味がなくなっちゃうんじゃない?存在しないか無効なら、ロックファイルに何か壊滅的なことが起こったってことだし、プロジェクトに詳しい人が対処すべきだよね。そうでなければ、ロックファイルを持つ意味がないじゃん。CIがロックファイルを静かに置き換えちゃって、混乱を招く可能性があるよ。

Pythonの世界では、ロックファイルは「インストールプロセスの奇妙なステップ」として扱われることが多く、バージョン管理にはコミットされていないことが多いね。

でも、どんな対策が考えられるの?ロックファイルが全くないなら、それは初回の実行か、後でgitのアップストリームから上書きされることになるだろうし。壊れているなら、誰かがパッケージのインストールをミスった可能性が高いし、新しいロックファイルを作成するのが一番理にかなっていると思う。こういう稀なエッジケースを扱う感じもするけど、かなりシンプルな方法だと思うよ。

実は、これはuv syncが提供する--lockedオプションでカバーされています。uv sync --lockedを実行すると、ロックファイルが存在しないか、古い場合は成功しません。追記:あなたのコメントを少し誤解していました。ロックファイルがない場合や、指定した依存関係と一致しないロックファイルがある場合は、人間が介入すべきだと強く同意します。だから、ビルドの際には必ず--lockedオプションを使うことをお勧めします。

これはプロセスの大きなバグだね。これを言うためにコメントを見に来たよ。彼らはこう言ってるけど、君が指摘した通り、正反対のことをしてる: > 「--frozenフラグはロックファイルが更新されないことを保証します。」 それがまさに私たちが望んでいることだよ。ロックファイルには、インストールするすべての依存関係の正確なバージョンの完全なリストがあるべきだから。

2025年になっても、Pythonのパッケージングと依存関係の管理はまだ混乱してるね。

みんながまだuvを使ってないから、こんなに混乱してるだけだと思う。

そう、めちゃくちゃだよ(新しく:Rustも追加!)

Hacker Newsで議論の続きを見る