BackEnd

uvの本番環境用dockerのマルチステージビルド

投稿日:

はじめに

uvで本番環境のdockerイメージをビルドするにはどのような方法が良いのか調査したいと思い、今回はFastAPIとMySQLを使ったサンプルアプリケーションを題材に調査してみました。

MySQLドライバには mysqlclient を使います。このドライバはネイティブライブラリに依存していて、例えばDebianではビルド時に pkg-config gcc 、実行時には default-libmysqlclient-dev が必要です。

このような場合、マルチステージビルドを使ったほうが、最終のイメージサイズを小さくすることができるので、今回はそれを実践してみたいと思います。

サンプルプロジェクトの作成

まずはuvを使ったサンプルプロジェクトを作成します。

pyproject.toml では、fastapiとmysqlclientに依存しています。dev dependancyとしてpytestに依存しています。

src/uv_docker/app.py を作成します。こちらはFastAPIのアプリケーションで、mysqlclientを使ってSELECTするサンプルとなっています。

シングルステージビルド

まずは、シングルステージのDockerfileを試してみます。

このイメージのポイント

  • プロダクション向けビルドで推奨されている ENV UV_COMPILE_BYTECODE=1 を指定しています。この環境変数を指定することで、ビルド時にバイトコード (.pycファイル) が生成されるため、起動時間が短縮される傾向にあるそうです。
  • mysqlclient パッケージをインストールするために pkg-configgcc が、ランタイムで動作させるために default-libmysqlclient-dev をインストールしています。

このイメージでも問題なく動作はしますが、 mysqlclient パッケージのインストールのためだけに必要なパッケージが入っているせいで、このイメージのサイズは「725MB」もあります。

また、ランタイムに本来不要なパッケージが含まれているというのも気になります。それでは、次の章でマルチステージ化してみましょう。

uv流のマルチステージビルド

uvのドキュメントに書かれているマルチステージビルド用のサンプルを参考に、先ほどのイメージをマルチステージ化してみました。

このイメージのポイント

  • ビルド時のみ必要な pkg-configgcc はbuilderステージでのみのインストール、ランタイムのみで必要な default-libmysqlclient-dev は最終ステージでのみインストールしています。
  • 1回目の uv sync では pyproject.tomluv.lock のみを参照し、 --no-install-project オプションを付けることで依存パッケージのみをインストールしています。
    これにより、ソースコードに変更が加えられたとしても、依存パッケージをインストールするための RUN uv sync はキャッシュが効き、ビルド時間を短縮することができます。
    さらに、 --mount=type=cache,target=/root/.cache/uv を指定することで、2回目の uv sync 時にキャッシュを共有するため、ビルドが速くなります。
  • 2回目の uv sync は、アプリケーションのソースコードがコピーされた状態で実行します。 1回目のキャッシュを使用しつつ実行されるため、依存パッケージのインストール時間はほとんどかかりません。
    また --no-editable を指定することで、プロジェクト自体も .venv 配下の site-packages 内にインストールされます。こうすることで、アプリケーションに必要なものはすべて .venv 配下に収まるようになります。
  • 最終ステージでは、 builderステージからは .venv 配下のコピーし、ランタイムで必要なパッケージをインストールし、FastAPIを起動します。

このイメージは「423MB」とかなり小さくなりました!

便利オプションの紹介(おまけ)

uv sync --no-editable はプロジェクト内に変更があっても.venv内に反映されないのですが、 --reinstall オプションを付けることで強制的に.venv内に再インストールされ、反映されるようになりました。

さいごに

uv流のDockerfileは、初め見た時はよく分からなかったのですが、一つ一つ紐解いていくと効率の良いDockerfileであることが分かり、同時にこのサンプルをベースに各ステージごとに適切な依存パッケージをインストールすることで、最終イメージを小さくすることができました。
uvを使う時はDockerfileにもひと工夫入れてみましょう!

おすすめ書籍

エキスパートPythonプログラミング 改訂4版 (アスキードワンゴ) Effective Python 第2版 ―Pythonプログラムを改良する90項目 動かして学ぶ!Python FastAPI開発入門

blog-page_footer_336




blog-page_footer_336




-BackEnd
-, ,

執筆者:

免責事項

このブログは、記事上部に記載のある投稿日時点の一般的な情報を提供するものであり、投資等の勧誘・法的・税務上の助言を提供するものではありません。仮想通貨の投資・損益計算は複雑であり、個々の取引状況や法律の変更によって異なる可能性があります。ブログに記載された情報は参考程度のものであり、特定の状況に基づいた行動の決定には専門家の助言を求めることをお勧めします。当ブログの情報に基づいた行動に関連して生じた損失やリスクについて、筆者は責任を負いかねます。最新の法律や税務情報を確認し、必要に応じて専門家に相談することをお勧めします。


comment

メールアドレスが公開されることはありません。 が付いている欄は必須項目です

CAPTCHA


関連記事

rails

ShrineでS3に画像をアップロードする

1 はじめに1.1 前提条件1.2 関連記事2 AWS側の準備2.1 S3バケットを作成する2.2 CORSを設定する2.3 アクセス用のユーザを作成する3 Rails側の設定3.1 Initiali ...

laravel logo

Laravelで非同期実行する

1 はじめに1.1 動作環境2 準備2.1 デーブルの作成2.2 .envの修正3 ジョブの作成4 ジョブのディスパッチ5 キューワーカーを起動6 より細かな制御6.1 特定のキューにディスパッチする ...

Go言語

go 1.24の tool directive でツールを管理する

1 はじめに1.1 これまでのツールの管理方法2 tool directiveを使ってツールを管理する2.1 ツールの追加2.2 ツールの削除3 go tool コマンドでツールを実行する4 さいごに ...

rails

Shrineを使って画像をアップロードする

1 はじめに2 Shrineとは2.1 簡単な説明2.2 作者2.3 特徴3 下準備3.1 Gemを追加3.2 初期設定3.3 テーブルを作成する4 実装4.1 Uploaderの実装4.2 Mode ...

Go言語

Go言語の基礎〜基本構文その2〜

1 はじめに2 配列3 スライス3.1 スライスの宣言3.2 len3.3 append3.4 range3.5 値の切り出し3.6 可変長引数4 map4.1 宣言と初期化4.2 マップの操作4.3 ...

フォロー

blog-page_side_responsive

2025年4月
 12345
6789101112
13141516171819
20212223242526
27282930  

アプリ情報

私たちは無料アプリもリリースしています、ぜひご覧ください。 下記のアイコンから無料でダウンロードできます。