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


関連記事

Stripe Connectで支払方法をクローンする

1 はじめに2 プラットフォームの顧客側の実装2.1 プラットフォームの顧客登録2.2 支払方法の登録3 支払方法クローンの実装3.1 顧客と支払方法のクローン3.1.1 支払方法のクローン3.1.2 ...

aws

AWS BatchはSaaSの非同期処理に向いているか?

1 はじめに2 AWS Batchの要素2.1 コンピューティング環境2.2 ジョブキュー2.3 ジョブ定義2.4 ジョブ3 AWS Batchの構築3.1 事前準備3.2 コンピューティング環境の作 ...

Vue.js+TypeScriptな環境整備

1 はじめに2 vue-cliのインストール3 プロジェクトの作成3.1 機能の選択3.2 シンタックスの選択3.3 CSSプリプロセッサの設定3.4 Unit test3.5 E2E test3.6 ...

FastAPIのPath Operationをasync defにするときはブロッキングに気をつけよう!

1 はじめに2 Path Operationと並行処理3 挙動の確認4 対応方法4.1 asyncioを使って自力で非同期化する4.2 async対応済みのパッケージを使う4.3 async defを ...

laravel logo

LaravelのFacade(ファサード)とは? 何気なく使用していた裏側の仕組みを解説!

1 はじめに1.1 Facadeを使用しているクラス2 Facadeの仕組み3 Facadeの作成3.1 サンプルコードに必要な実装3.2 Serviceの作成3.3 Facadeクラスの作成3.4 ...

フォロー

blog-page_side_responsive

2025年4月
 12345
6789101112
13141516171819
20212223242526
27282930  

アプリ情報

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