こんにちは!最近、新しい技術の組み合わせを試すことにハマっています。
今回は、「Docker」と「uv」を使って「FastAPI」アプリケーションの環境を構築できるのか?という疑問を探るべく、色々と試している過程を記録に残します。
まだ最適な答えは見つかっていませんが、「どこまで調べたか」「次に何をするか」をメモしておくことで、未来の自分や、同じような疑問を持つ方の助けになれば嬉しいです。
挑戦のテーマ
今回挑戦しているテーマは、以下の3つの技術を組み合わせることです。
- Docker: コンテナ型仮想化プラットフォーム
- uv: Pythonの高速パッケージ管理ツール
- FastAPI: PythonのモダンなWebフレームワーク
最終的な目標は、
- uv を使って FastAPI アプリケーションのプロジェクトを作成・管理する。
- そのプロジェクトを Docker コンテナとしてビルド・実行できるようにする。
- (将来的には)作成した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"]
なるほど。流れとしては、
- ベースとなるPythonイメージを指定 (
python:3.11-slim
) - 作業ディレクトリを設定 (
/app
) pip
を使ってuv
をインストールpyproject.toml
をコピーuv pip compile
で依存関係を解決し、uv pip install
でインストール- 残りのアプリケーションコードをコピー
- 実行コマンドを指定
という感じのようです。
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
を追加。 CMD
をuvicorn
を使ってFastAPIアプリを起動するように変更。
この Dockerfile と、簡単なFastAPIアプリケーション (main.py
) を用意して、docker build
と docker 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 でどうやって包めばええねん!」と思った時のための、第一歩の記録として、この記事が役立てば幸いです。