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

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も追加!)

そうだね。言語設計の初期段階でこれを正しくやることが大事だよ。バージョン2.0まで先延ばしにしない方がいい。実行可能なスクリプトにパッケージやモジュールのメタデータを入れる前に、二度考えよう。それを決めたら、さらに三度考えてみて。言語によっては(Common Lispみたいに)うまくいくこともあるけど、他の言語(Pythonみたいに)ではそうじゃないこともあるからね。

依存関係で問題があったことはないよ。どうして混乱してるの?プロジェクトごとにrequirements.txtとvenvがあるんだから、これ以上簡単にはならないよ。

flaskコンテナでuvを使ってるんだけど、ビルド時間の差がほんとにすごい。速度だけじゃなくて、予測可能性が高くなるのがいいよね。無駄な「なんでpipがこのバージョンをインストールしたの?」みたいな瞬間がない。pyproject.tomlを書いて、uv lockでフリーズさせて、終わり。 >Dockerでは、pyproject.tomlとuv.lock*をそのままCOPYすればいい。で、uv sync --frozen --no-install-projectを実行すれば、自分のアプリをスキップしてインストール層がキャッシュ可能になる。ほんとに知ってる人は、1つのパッケージが変わっただけで全レイヤーを再構築するのがどれだけ面倒か分かってる。 >UV_PROJECT_ENVIRONMENT=/home/python/.localでvenvをバイパスできる。つまり、ベースイメージを事前にウォームアップしたり、ビルド間で共有できる。インフラコストが静かに節約できる。UV_COMPILE_BYTECODE=1に切り替えれば、ビルド時に.pycが得られる。 >可変環境を排除して、再現性を尊重させる。ビルドが壊れたら、それはロックファイルのせい。責任が明確になる。

自分が作ったソフトウェアをpypiで使ってもらってるんだけど、個人的にuvに切り替えて速度向上の恩恵を受けたい。でも、pipと全く同じように動くって保証がないと不安だし、同時に実行できるかも知りたい。ユーザーに「pip install xxxを実行して」って指示するなら、エラーが出た時に自分もそれを見てデバッグできるか確認したい。

pipと全く同じようには動かないよ。大きな違いについてはここに書いてあるよ: https://docs.astral.sh/uv/pip/compatibility/ いくつかはuvが標準に従っているけど、pipはまだレガシーな動作から移行中だったり、デザイン上の選択でuvが決めたこともある。標準があいまいだから、特定のツールの選択だったり、uvが何らかの理由で標準に従わないことにしたりしてるんだ。

PythonのツールがPython以外の言語で書かれるのには完全に反対だ。C拡張があるのは理解してるし、ほとんどの場合PythonはCPythonと同義だと思ってる。2つの言語で十分で、誰も求めていない3つ目は必要ない。Rustには何も反対しないけど、新しいツールが欲しいならどうぞ。既存のツールを再作成したいなら、それもいい。でも、理由もなく既存のエコシステムに入り込むのには反対だ。人気のPythonパッケージPendulumは、3.13のサポートが7ヶ月もなかった。PythonコミュニティにRustを十分に理解している人がいなかったからだろうと想像してる。Pendulumのネイティブ部分がCで書かれていたら、自分で直してたよ。https://github.com/python-pendulum/pendulum/issues/844 理想的な世界では、Rust(またはC以外の言語)で書かれた高速な日付時刻が欲しいなら、FFIを介してどの言語でも使える適切なライブラリを作るべきだ。今のところ、このRustの話はあまり良い印象を持ってなくて、Linuxコミュニティが抵抗感を持つのも理解できる。

PythonのツールがPython以外の言語で書かれるのには完全に反対だ。 あなたがPylintの実行が終わるのを待っている間、私は日差しを楽しんでるよ。

Rust(またはPython/C以外の言語)をPythonツールに使うことに対するあなたの反対理由は何なの?具体的な理由を言ってないよね。

RustはPythonやCにはない機能セットを提供してる。もしRustがその仕事に適しているなら、そのコードはRustで書かれるべきだと思う。サポートは実装言語よりもインセンティブ構造に関係してる。

俺は、ツールがどの言語で書かれてるかなんて気にしないよ。確かに、これらのツールを使うユーザーがPython開発者なら、理論的には貢献できるって気持ちはわかるけど、ツールがちゃんと機能してれば「Pythonじゃないから」ってのはどうでもいい。むしろ、Python環境のセットアップに関する問題があって、そのツールがPythonで書かれてたら、逆に壊れちゃうかもしれないと思う。

理論的には君の言ってることには賛成だけど、実際にはPythonで書かれたパッケージマネージャーがuvほど良いものに出会ったことがないんだ。速度のことは言ってないけどね。uvはPythonで書かれてもいいと思うけど、実際にはそうじゃない。

Pythonは俺の好きな言語だけど、uvを完全に受け入れたよ。めちゃくちゃ簡単で速いから、他に近いものはないね。古いサーバーでEOLのディストリビューションを使ってて、壊れるのが怖くて誰も触れないような環境で最新のPythonが必要?uvだよ。小さなスクリプトのために依存関係が一つか二つ必要で、共有するためにパッケージ化するのが面倒?uvだね。とはいえ、拡張機能に関しては君の意見にちょっと同意する部分もある。数年前から進めてるサイドプロジェクトがあって、最初は純粋にPythonで作ったんだ。それを使ってPythonの遅い部分を学んで、どうやって回避するかを考えた。その後、もっと負荷のかかる部分をCで書き始めて、ctypesでインターフェースを作った。最終的にはPython APIを使って書き直したんだけど、Cで書いた部分が多すぎて、なんで全部Cで書かなかったんだろうって自問自答したら、「Cが得意じゃないから、壊す自信がない」って答えが返ってきた。だから今はRustで少しずつ書き直してる。主にRustを学ぶためにね。長くなったけど、外部ライブラリの機能がコアのPythonコードを超え始めたら、それは多分、全部別の言語で書くべきだってサインだと思う。

私はPythonのツールがPython以外の言語で書かれるのには完全に反対です。C拡張が存在するのは理解してるし、ほとんどの場合、PythonはCPythonと同義です。 >2つの言語で十分だと思う。誰も求めていない3つ目は必要ない。何のために十分なの?uvユーザーはそれに悩まされる必要はないよ。ほとんどのエコシステムはツールに対して言語のミックスを使ってる。ツールのユーザーが心配するような細かいことじゃない。 >理由もなく既存のエコシステムに入り込むのには反対です。それはもっと速いから。Pythonで書かれてないからね。ツールはユーザーのためのもの。ツールの言語はツールの開発者のためのもの。これらは同じ人である必要はない。重要なのは、そのツールがエコシステムの中で実際の問題を解決しているかどうか(解決してるよ)。人々はそれを好きなの?

この視点には感謝だけど、uvのようなツールをRustで作るのは良いアイデアだと思う。なぜなら、それはPythonの管理をするためのツールであって、Pythonコードの中から呼び出すためのツールじゃないから。Python管理ツールもPythonで書かれていると、鶏と卵の状況が生まれる。まず、Python管理ツールを始める前に、動作するPythonのインストールが必要になる。おそらくそれを使うのは、他の方法でPythonの管理をするよりも優れているからだよね。そうなると、どのPythonのバージョンや特定の実行ファイルをこの管理ツールが使っているのか、実際に実行しているコードは同じものを使っているのか、依存関係ツリーはどうなってるのか、管理ツールが実行している環境で必要なPythonパッケージをどう管理しているのか、実行しているコードが完全に独立したパッケージ環境を使っているかどうかをどうやって確認するのか、もしそうじゃなかったら、アプリが必要とするパッケージやバージョンと管理ツールが必要とするものの間に衝突があったらどうするのか、これらのことがうまく機能していない場合、どうやってデバッグして修正するのか、などの複雑な問題が出てくる。管理ツールがコンパイルされたバイナリとしてダウンロードして使えるものであれば、どの言語で書かれていても、これらの厄介な質問は解消される。そうすれば、そのツールは実際にシステム上のPythonの使用に関するすべてを管理してくれて、ツール自体を管理するための別のツールチェーンを使う必要もなくなるし、そのツールが実際に使いたかったツールと衝突する可能性があるかどうかを心配する必要もなくなる。

「2つの言語で十分だと思う。誰も求めていない3つ目は要らない。」 githubでruffとuvがもらったスターの数を見てみて。これは急成長だよ。だからruffで評価されて、uvに進んだ。これを「求められた」と呼べるね。 > 「理由もなく既存のエコシステムに入り込むのには反対だ。」 それは理由があるよ。今あるツールはひどくて、Python以外の何かを書いたことがあるなら使えないから。: それを言うのは、誰かがそれに気づかない唯一の理由は、もっと良いものを見たことがないからだと思う。まあ、CやC++の方がひどいツールもあるけど、Pythonは最悪のツールを持っている言語のトップだよ。

「理由もなく既存のエコシステムに入り込むのには反対だ。」 理由があるよ。今日存在するツールはひどくて、Python以外の何かを書いたことがあるなら使えないから。: それを言うのは、誰かがそれに気づかない唯一の理由は、もっと良いものを見たことがないからだと思う。まあ、CやC++の方がひどいツールもあるけど、Pythonは最悪のツールを持っている言語のトップだよ。

UVは本当に使える。ここ数年でPythonのパッケージ管理に起きた最高のことの一つだね。

PSA:pipuvに置き換えるのは注意してね、ドロップインの置き換えだと思ってるなら。デフォルトではuvpycファイルを生成しないから、サービスの起動がかなり遅くなるかもしれないよ。詳細は https://docs.astral.sh/uv/reference/settings/#pip_compile-by...

uvを試してみようと思ったのは、コンテナビルドが速くなるっていう約束だったからで、実際にそれを実現してくれたよ。普段はプラットフォームPythonを使ってたけど、それに反対されてたにもかかわらず、uvのおかげでやっとやめることができた。

uvでrequirements.txtを生成して、それをdockerと一緒に送れば、こんな面倒なことは必要ないよ。