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-config と gcc が、ランタイムで動作させるために default-libmysqlclient-dev をインストールしています。

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

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

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

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

このイメージのポイント

  • ビルド時のみ必要な pkg-config と gcc はbuilderステージでのみのインストール、ランタイムのみで必要な default-libmysqlclient-dev は最終ステージでのみインストールしています。
  • 1回目の uv sync では pyproject.toml と uv.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


関連記事

EloquentのtoArray()とtoJson()をカスタマイズ

はじめに LaravelのEloquentには色々便利な機能がありますが、今回はモデルのシリアライズに関する機能を紹介したいと思います。 $visibleプロパティと$hiddenプロパティ Lara ...

Stripe Connectを使ってCheckoutを利用した継続課金を実装

はじめに 前回は、Stripe Connectを使った継続課金の実装について説明しました。今回は、Checkoutを使った場合の継続課金の実装について説明します。 なお、継続課金に使用する商品について ...

rails

RailsでAjax処理で画面を更新する

はじめに RailsでAjax処理で画面を更新する方法を簡単に紹介します。 View(遷移元)の設定 link_toやform_forなどで remote: true を指定します。 (下記の例ではu ...

Go言語

GoフレームワークGinでミドルウェアを使ってログインAPIを実装

はじめに 今後 Go を使用した案件をやる機会があり、現在 Go の勉強中なのですが、今回は案件でも使用するフレームワーク Gin を使用してログインAPIを実装したいと思います。Gin はメモリ利用 ...

rails

Rails5で出力される「alias_method_chain is deprecated.」について

はじめに 直近のプロジェクトではRails5を使っているのですが、利用しているgem関連で下記のような警告が大量に出力されるので、少し調べてみました。 (「./bin/rails c」や「./bin/ ...

フォロー

blog-page_side_responsive

2025年4月
日 月 火 水 木 金 土
 12345
6789101112
13141516171819
20212223242526
27282930  

アプリ情報

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