🦄

uv on Docker環境でFastAPI + Lambda Web Adapter(LWA)アプリをマルチステージビルドする

に公開

本記事はPython Advent Calendar 2025の2日目の記事です。

はじめに

こんにちは、がんがんです。
以前の記事で FastAPI + Lambda Web Adapter(LWA) 用のコンテナを作成しました。

https://zenn.dev/gangannikki/articles/fastapi-on-docker

この記事ではパッケージの管理をrequirements.txt、pyenvで実施していました。
最近のPython開発ではuvをメインで利用しており、uv版のコンテナを作成を試みました。

uvを用いたPythonコンテナの作成方法は複数あります。今回紹介していない方法は以下の記事で詳しくまとめられていました。
複数の選択肢については以下の記事を合わせてご覧ください。

https://zenn.dev/mkj/articles/3aaa36d6f35c08

まとめ

最終的なDockerfile、pyproject.tomlはこちらです。

ARG APP_DIR="/api"
FROM ghcr.io/astral-sh/uv:0.9.11 AS uv

# Base image
FROM python:3.13.9-slim-bookworm AS base

WORKDIR /app
ENV TZ=Asia/Tokyo
ARG APP_DIR

COPY --from=public.ecr.aws/awsguru/aws-lambda-adapter:0.9.1 /lambda-adapter /opt/extensions/lambda-adapter

# Build stage
FROM base AS builder

ENV UV_COMPILE_BYTECODE=1 \
    UV_NO_INSTALLER_METADATA=1 \
    UV_LINK_MODE=copy

RUN --mount=from=uv,source=/uv,target=/bin/uv \
    --mount=type=cache,target=/root/.cache/uv \
    --mount=type=bind,source=${APP_DIR}/uv.lock,target=uv.lock \
    --mount=type=bind,source=${APP_DIR}/pyproject.toml,target=pyproject.toml \
    uv sync --frozen --no-dev

# Production stage
FROM base AS prod

COPY --from=builder /app/.venv/lib/python3.13/site-packages /usr/local/lib/python3.13/site-packages
COPY ${APP_DIR}/app/ .

# NOTE: FastAPI CLIではなくuvicornを利用しています 
ENTRYPOINT ["python", "-m", "uvicorn"]
CMD ["main:app", "--host", "0.0.0.0", "--port", "8080"]
[project]
name = "exp-fastapi-app-example"
version = "0.1.0"
description = "experiment app"
requires-python = ">=3.13.0"
dependencies = [
    "fastapi==0.121.0",
    "pydantic==2.12.3",
    "pydantic-settings==2.11.0",
    "uvicorn==0.38.0",
]

[tool.uv]
required-version = ">=0.9.0"

今回は以下のようなディレクトリ構成でFastAPI App.を構築しています。FastAPIの詳細については今回深く触れません。お好きな構成を採用してください。

.
├── api/
│   ├── app/
│   │   ├── __init__.py
│   │   ├── dependencies.py
│   │   ├── main.py
│   │   └── settings.py
│   ├── .env
│   ├── .env.example
│   ├── pyproject.toml
│   └── uv.lock
├── api.Dockerfile               # 本記事のメインファイル
├── api.Dockerfile.dockerignore
└── README.md

Dockerfileの書き方

1. uvを一時的に利用する

アプリケーションコンテナは可能な限り軽量に作成することが推奨されています。Dockerfileのベストプラクティスにもマルチステージビルドの利用不要なパッケージをインストールしないことが推奨されています。

https://docs.docker.com/build/building/best-practices/

https://docs.docker.jp/develop/develop-images/dockerfile_best-practices.html

uvはライブラリのインストールのみで利用するためデプロイステージでは不要となります。そこでDockerのbindマウントを用いてuvを一時的に利用します。

RUN --mount=from=uv,source=/uv,target=/bin/uv \
    --mount=type=cache,target=/root/.cache/uv \
    --mount=type=bind,source=${APP_DIR}/uv.lock,target=uv.lock \
    --mount=type=bind,source=${APP_DIR}/pyproject.toml,target=pyproject.toml \
    uv sync --frozen --no-dev



uvの一時的な利用は公式ドキュメントでも紹介されています。

https://docs.astral.sh/uv/guides/integration/docker/#using-uv-temporarily

2. pyproject.toml、uv.lockもbindマウントで利用する

uvと同じようにpyproject.toml、uv.lockもbindマウントで利用します。pyproject.toml、uv.lockもデプロイステージでは不要ですので可能であれば削りたいです。

2024年版のDockerfileの考え方&書き方の記事でも記載されていますがbindマウントはrequirements.txt方式や別言語でも有効なアプローチです。積極的に使っていきたいですね

https://future-architect.github.io/articles/20240726a/#レイヤー自体のキャッシュと、レイヤーをまたいだキャッシュ

3. LWA (Lambda Web Adapter)を利用する

LWAの利用は前回の記事と同じです。LWAの詳細は前回の記事を参照ください。

https://zenn.dev/gangannikki/articles/fastapi-on-docker#fastapi-%2B-lambda-web-adapterコンテナを作成する

まとめ

今回はuv + FastAPI + LWAを利用したPythonコンテナを作成しました。
uvやruffなどAstral製のものは使い勝手が良いものが多いです。uvは公式ドキュメントのサポートが手厚いことも魅力的です。
本記事では利用していませんがUsing uv in Dockerのページでuv + Dockerコンテナの作成方法がまとめられていました。

https://docs.astral.sh/uv/guides/integration/docker/

https://docs.astral.sh/uv/guides/integration/fastapi/

uvを利用するユーザーが増えている一方、技術記事などの情報があまり多くないというデメリットもあります。自分用のメモとして適宜残していきたいですね。

Discussion