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


関連記事

Go言語

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

1 はじめに2 ログインAPIの作成3 ログインのセッション管理4 ミドルウェア4.1 gin.Default()4.2 Logger4.3 Recovery4.4 sessions5 独自ミドルウェ ...

laravel logo

Laravelのブラウザテスト「Dusk」で非同期で重たい処理のテストを実装してみよう

1 はじめに2 JavaScriptの式で待機する2.1 テスト対象となるコード2.2 Duskのテストコード3 DOM要素の表示を待つ3.1 テスト対象となるコード3.2 Duskテストコードの実装 ...

aws

Amazon API Gatewayざっくり概要

1 はじめに2 Amazon API Gatewayとは?2.1 なにができる?2.2 作成できるAPIの種類2.3 REST APIとHTTP APIの違い3 Amazon API Gatewayの ...

Rust入門してみた (基本構文編)

1 はじめに2 Rustとは?3 Rustの特徴的な基本構文3.1 変数と定数3.2 所有権3.3 所有権の借用3.4 関数3.5 エラーハンドリング3.5.1 回復不能なエラー(panic!)3.5 ...

laravel logo

Laravelでメールを送る

1 はじめに1.1 準備2 実装2.1 Mailableクラスの作成2.2 テンプレートの作成2.3 Mailableクラスの修正2.4 コントローラの作成2.5 コンフィグの修正3 さいごに4 おす ...

フォロー

blog-page_side_responsive

2025年4月
 12345
6789101112
13141516171819
20212223242526
27282930  

アプリ情報

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