Docker + uv + FastAPI 環境構築に挑戦中!試行錯誤の記録

Docker + uv + FastAPI 環境構築に挑戦中!試行錯誤の記録

こんにちは!最近、新しい技術の組み合わせを試すことにハマっています。
今回は、「Docker」と「uv」を使って「FastAPI」アプリケーションの環境を構築できるのか?という疑問を探るべく、色々と試している過程を記録に残します。

まだ最適な答えは見つかっていませんが、「どこまで調べたか」「次に何をするか」をメモしておくことで、未来の自分や、同じような疑問を持つ方の助けになれば嬉しいです。

挑戦のテーマ

今回挑戦しているテーマは、以下の3つの技術を組み合わせることです。

  • Docker: コンテナ型仮想化プラットフォーム
  • uv: Pythonの高速パッケージ管理ツール
  • FastAPI: PythonのモダンなWebフレームワーク

最終的な目標は、

  1. uv を使って FastAPI アプリケーションのプロジェクトを作成・管理する。
  2. そのプロジェクトを Docker コンテナとしてビルド・実行できるようにする。
  3. (将来的には)作成したDockerコンテナをGoogle Cloud Runなどにデプロイする。

という流れです。

特に、「uvで作ったプロジェクトって、Dockerでそのまま使えるの?」「Dockerfileはどう書けばいいの?」という点が、今回の調査の大きなポイントです。

「uv」と「Docker」について (おさらいと現状の理解)

uv

以前の記事でも紹介しましたが、uv はRust製の超高速なPythonパッケージ管理ツールです。
プロジェクトの初期化 (`uv init`) やライブラリのインストール (`uv add`)、仮想環境の自動作成 (`uv run`) などが非常にスムーズで、個人的に気に入って使っています。

Docker

Docker については、正直まだ勉強不足です…!
私の今の理解では、「コンテナ」という隔離された環境を作成し、その中でアプリケーションを動かすためのツール、という感じです。

コンテナを使うことで、

  • 開発環境と本番環境の差異をなくせる
  • アプリケーションとその依存関係をまとめて配布できる
  • サーバーへのデプロイが容易になる

といったメリットがある、ということは理解しています。
Cloud Runのようなサービスを使えば、コンテナをそのままデプロイできるのも魅力的ですよね。
使いこなせれば、開発がもっと楽しくなりそうです!

試行錯誤の記録:uv プロジェクトを Docker 化する

さて、ここからは実際に試してみた内容を記録していきます。

1. Dockerfile の調査

まず、「uv を使った Python プロジェクトを Docker 化するための Dockerfile はどう書けばいいのか?」を調べました。
AI (今回はClaude) に相談してみたところ、以下のような Dockerfile を提案されました。

Claude提案のDockerfile (初期案):


# Base Python image
FROM python:3.11-slim

# Set working directory
WORKDIR /app

# Install uv
RUN pip install uv

# Copy only the dependency management files first
COPY pyproject.toml poetry.lock* ./ # poetry.lock は不要かも?

# Install dependencies using uv
# pyproject.toml があれば uv pip compile を実行して依存関係をインストール
RUN uv pip install --system --no-cache \
    $(if [ -f pyproject.toml ]; then uv pip compile pyproject.toml; fi)

# Copy the rest of the application code
COPY . .

# Command to run the application
CMD ["python", "your_main_script.py"]

なるほど。流れとしては、

  1. ベースとなるPythonイメージを指定 (python:3.11-slim)
  2. 作業ディレクトリを設定 (/app)
  3. pip を使って uv をインストール
  4. pyproject.toml をコピー
  5. uv pip compile で依存関係を解決し、uv pip install でインストール
  6. 残りのアプリケーションコードをコピー
  7. 実行コマンドを指定

という感じのようです。

2. Dockerfile の修正と動作確認

提案された Dockerfile を元に、FastAPI アプリケーション用に修正し、実際にビルドして動かしてみました。
(いくつかのネットワークツールも追加しています)

修正後のDockerfile:


# Base Python image
FROM python:3.11-slim

# Set working directory
WORKDIR /app

# システムパッケージとネットワークツールのインストール
RUN apt-get update && apt-get install -y \
    curl \
    iproute2 \
    iputils-ping \
    net-tools \
    && apt-get clean \
    && rm -rf /var/lib/apt/lists/*

# Install uv
RUN pip install uv

# Copy only the dependency management files first
COPY pyproject.toml ./

# Install dependencies using uv directly
# FastAPIと関連ライブラリを直接インストール
RUN uv pip install --system fastapi uvicorn python-multipart jinja2 aiofiles

# Copy the rest of the application code
COPY . .

# Expose the port the app runs on
EXPOSE 8000

# Command to run the application with uvicorn
CMD ["uvicorn", "main:app", "--host", "0.0.0.0", "--port", "8000"]

変更点:

  • ネットワークツール (`curl`, `iproute2` など) を追加。
  • 依存関係のインストールを、uv pip compile を経由せず、uv pip install --system fastapi uvicorn ... で直接行うように変更。(こちらの方がシンプルで分かりやすいと感じました)
  • FastAPIを実行するために EXPOSE 8000 を追加。
  • CMDuvicorn を使ってFastAPIアプリを起動するように変更。

この Dockerfile と、簡単なFastAPIアプリケーション (main.py) を用意して、docker builddocker run を実行したところ…

動きました! 🎉

コンテナ内の FastAPI サーバーに、別のターミナルやブラウザからアクセスできることを確認できました。

ちなみに、その時に使った main.py はこんな感じです。

main.py (動作確認用):


from fastapi import FastAPI, Request
from fastapi.staticfiles import StaticFiles
from fastapi.templating import Jinja2Templates
from fastapi.responses import HTMLResponse, JSONResponse
from fastapi.middleware.cors import CORSMiddleware
import uvicorn

app = FastAPI(title="UV Docker Test API")

# CORSミドルウェアを追加 - 開発中のローカルフロントエンドを許可
app.add_middleware(
    CORSMiddleware,
    allow_origins=["http://localhost:3000", "http://127.0.0.1:3000", "http://frontend:3000"], # 必要に応じてフロントエンドのオリジンを追加
    allow_credentials=True,
    allow_methods=["*"], # すべてのメソッドを許可 (開発中)
    allow_headers=["*"], # すべてのヘッダーを許可 (開発中)
)

# 静的ファイルのマウント (今回は未使用)
# app.mount("/static", StaticFiles(directory="static"), name="static")

# templatesディレクトリの設定 (今回は未使用)
# templates = Jinja2Templates(directory="templates")

@app.get("/", response_class=HTMLResponse)
async def root(request: Request):
    """
    簡単なHTMLレスポンスを返す
    """
    return """
    <html>
        <head>
            <title>UV Docker Test</title>
        </head>
        <body>
            <h1>Hello from FastAPI in Docker with UV!</h1>
            <p>API endpoint: <a href="/api/hello">/api/hello</a></p>
        </body>
    </html>
    """

@app.get("/api/hello")
async def hello():
    """
    JSONレスポンスを返すシンプルなAPI
    """
    return {"message": "Hello from uv-docker-test API!"}

# uvicornで実行する場合 (DockerfileのCMDで実行される)
# if __name__ == "__main__":
#     uvicorn.run("main:app", host="0.0.0.0", port=8000, reload=True) # reload=True は開発時のみ

CORS (Cross-Origin Resource Sharing) の設定も追加しています。
allow_origins で許可するオリジン (アクセス元のドメイン) を指定します。
["*"] とすれば全てのオリジンを許可できますが、セキュリティリスクが高まるので注意が必要です。

4. とりあえずここまで!

今回の試行錯誤で、

「uv で開発環境を作ったPythonプロジェクトを、Dockerコンテナとして動かすことは可能である」

ということが分かりました!

Dockerfile で uv をインストールし、uv pip install で依存関係をインストールすれば、問題なく動作するようです。
FastAPI に関しても、基本的な動作確認はできました。

ただ、まだ調査不足な点も多いです。

  • PostgreSQL との連携はどうするか? (Docker Compose を使う? Google Cloud SQL を使う?)
  • Dockerfile の最適化 (ビルド時間の短縮、イメージサイズの削減など)
  • uv のキャッシュを Docker ビルドで効率的に利用する方法はあるか?
  • 本番環境向けの Dockerfile はどう書くべきか?

これらの点については、今後さらに調査・学習を進めていく必要があります。
「uv で開発環境作ったけど Docker でどうやって包めばええねん!」と思った時のための、第一歩の記録として、この記事が役立てば幸いです。

Comments

No comments yet. Why don’t you start the discussion?

    コメントを残す

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