ChatGPT先生と挑むFastAPI #9|FastAPIアプリをDocker化する

記事内に広告が含まれています。
タイトル

今回は、これまで開発してきたFastAPIアプリケーションをDocker化します。

Dockerfileを使用してアプリケーションの実行環境を定義し、Docker ComposeでFastAPIとPostgreSQLを組み合わせて起動できる構成を作成します。

コンテナ間の接続や環境変数の設定についても学び、Docker環境でAPIやデータベースが正常に動作することを確認します。

ChatGPT先生
ChatGPT先生

それではSTEP9を始めましょう!

  1. 今回学ぶこと
  2. 学習開始
    1. Docker化の目的と構成を理解する
      1. ① なぜDocker化するのか?
      2. ② 今回作成する構成
    2. 既存のアプリ構成と設定を確認する
      1. ① 現在の構成について
      2. ② 現在のDocker構成で重要な点
      3. ③ DB接続設定について
    3. Dockerfileを作成する
      1. ① Dockerfileの仕組み
      2. ② Dockerfileを作成する
      3. ③ Dockerfileの各命令の役割
    4. .dockerignoreを設定する
      1. ① .dockerignoreとは?
      2. ② .dockerignoreを作成する
    5. Docker ComposeでアプリとDBを構成する
      1. ① 今回の構成方針
      2. ② compose.run.yamlを作成する
      3. ③ 今回の設定で押さえておきたい仕組み
    6. Dockerイメージをビルドして起動する
      1. ① Dockerイメージをビルドする
      2. ② FastAPIとPostgreSQLのコンテナを起動する
      3. ③ コンテナの状態を確認する
      4. ④ FastAPIの起動ログを確認する
    7. APIとデータベースの動作確認
      1. ① GET APIの動作確認
      2. ② Alembicでデータベースにテーブルを作成する
      3. ③ PostgreSQLのテーブルを確認する
      4. ④ CRUDの動作確認
      5. ⑤ 商品を登録する(POST)
      6. ⑥ 登録した商品を取得する(GET)
      7. ⑦ PostgreSQLのデータを直接確認する
    8. Docker環境でpytestを実行する
      1. ① テスト設定の確認結果
      2. ② compose.run.yamlにテスト専用DBを追加する
      3. ③ テスト専用DBを起動する
      4. ④ テスト専用DBの状態を確認する
      5. ⑤ pytestを実行する
    9. 自力課題
      1. 課題内容
      2. 完了条件
      3. 実装結果
      4. ChatGPT先生のレビュー結果
  3. まとめ
■■■スポンサーリンク■■■

今回学ぶこと

今回のステップで学ぶ内容を一覧で出してもらいました。

ChatGPT先生
ChatGPT先生

STEP9では以下の内容を学習します。

  1. Docker化の目的と構成を理解する
  2. 既存のアプリ構成と設定を確認する
  3. Dockerfileを作成する
  4. .dockerignoreを設定する
  5. Docker ComposeでアプリとDBを構成する
  6. Dockerイメージをビルドして起動する
  7. APIとデータベースの動作確認をする
  8. Docker環境でpytestを実行する
  9. 自力課題

学習開始

それでは学習をはじめます。

Docker化の目的と構成を理解する

① なぜDocker化するのか?

ChatGPT先生
ChatGPT先生

STEP 1では、Dev Containersを使用して開発環境を構築しました。

既にDockerを使用しているのに、なぜ今回改めてDocker化するのでしょうか?

それは、開発環境を用意することと、完成したアプリケーションの実行環境を用意することでは目的が異なるからです。

比較STEP 1STEP 9
目的開発環境を統一するアプリの実行環境を再現する
主な用途コード編集・デバッグ・開発アプリの起動・実行・配布
ツールVS Codeなど開発ツールも利用実行に必要なものを中心に構成
ソースコードホストから共有する構成が一般的イメージに組み込む

Dev ContainersでもFastAPIは実行できます。しかし、VS Codeなどの開発環境を前提にせず、アプリケーションを独立して起動・配布できる構成を作るのが今回の目的です。

② 今回作成する構成

環境イメージ
今回構築するローカル実行環境のイメージ(ChatGPTで生成)
ChatGPT先生
ChatGPT先生

FastAPIとPostgreSQLは、それぞれ独立したコンテナとして実行します。

Docker Composeは複数のコンテナ、ネットワーク、データ保存用のボリュームなどをまとめて定義・管理する仕組みです。

これにより、開発用のVS Code環境とは独立して、FastAPIとDBをまとめて起動できるようにします。

既存のアプリ構成と設定を確認する

ChatGPT先生
ChatGPT先生

今回は、既存のFastAPIプロジェクトを確認し、Docker化に必要な情報を整理します。

STEP 6〜8でPostgreSQL・SQLAlchemy・環境変数・pytestを導入しているため、既存の構成をできるだけ維持しながらDocker化する方針です。

① 現在の構成について

ChatGPT先生
ChatGPT先生

現在の構成は次のようになっています。

確認項目現在の状態STEP 9での対応
Python3.12同じバージョンを使用
FastAPIapp/main.pyapp.main:appを起動対象にする
PostgreSQL17既存のDB設定を活用
DB接続app/config.pyで環境変数から取得既存の仕組みを利用
依存ライブラリrequirements.txt実行環境に必要なものをインストール
マイグレーションAlembic既存の設定を利用
テストpytest+テスト専用DB既存のテストを実行

現在の構成を大きく変更する必要はなさそうです。

ただし、実行用イメージでは開発・テスト専用のライブラリを含めるかどうかを検討します。

② 現在のDocker構成で重要な点

ChatGPT先生
ChatGPT先生

既存のcompose.yamlでは、appサービスに以下の設定があります。

volumes:
  - .:/workspace

command: sleep infinity

これは、ローカルのソースコードをコンテナと共有し、開発用コンテナを起動したままにする構成です。

FastAPIを自動起動する設定にはなっていません。

今回のDocker化では、ソースコードをイメージに組み込み、コンテナ起動時にFastAPIが実行されるようにします。

また、既存のdevcontainer.jsonがcompose.yamlのappサービスを参照している点にも注意が必要です。

開発用の構成を維持し、アプリ実行用の構成を別途用意する方針にします。

これにより、既存のDev Containersへの影響を抑えます。

③ DB接続設定について

ChatGPT先生
ChatGPT先生

.env.exampleには以下の接続先が定義されています。

DATABASE_URL=postgresql+psycopg://fastapi:your_password@db:5432/fastapi_learning

ここでのdbは、Docker Composeで定義されたPostgreSQLのサービス名です。

FastAPIコンテナから同じComposeネットワーク上のDBへ接続する構成であれば、この考え方をそのまま利用できます。

また、既存のPostgreSQLにはデータ保存用のボリュームが設定されているため、実行用構成でもデータの永続化を考慮します。

Dockerfileを作成する

ChatGPT先生
ChatGPT先生

今回は、FastAPIアプリケーションを実行するためのDockerfileを作成します。

STEP 1の開発用Dockerfileはそのまま残し、アプリ実行用のDockerfileを新しく作成します。

① Dockerfileの仕組み

ChatGPT先生
ChatGPT先生

Dockerfileは、Dockerイメージを作成するための設計書です。

開発用Dockerfileとの主な違いは、コンテナの起動方法にあります。

現在のcompose.yamlでは、以下のように設定されています。

command: sleep infinity

これはコンテナを起動したままにするための設定で、FastAPI自体は起動しません。

今回作成するDockerfileでは、コンテナ起動時にUvicornを実行し、FastAPIを自動で起動するようにします。

DockerfileのCMD命令で、コンテナ起動時のデフォルトコマンドを定義できます。

② Dockerfileを作成する

ChatGPT先生
ChatGPT先生

VS Codeでプロジェクト直下の/workspaceに、Dockerfileというファイルを新規作成してください。

既存の.devcontainer/Dockerfileとは別ファイルです。

# Python 3.12の軽量イメージを使用する
FROM python:3.12-slim

# コンテナ内の作業ディレクトリを設定する
WORKDIR /app

# Pythonの依存ライブラリをインストールする
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt

# FastAPIのソースコードをイメージに組み込む
COPY app/ ./app/

# Alembicのマイグレーションに必要なファイルを組み込む
COPY alembic.ini .
COPY migrations/ ./migrations/

# STEP 8で作成したテストコードを組み込む
COPY tests/ ./tests/

# アプリケーションが使用するポートを明示する
EXPOSE 8000

# コンテナ起動時にFastAPIを実行する
CMD ["uvicorn", "app.main:app", "--host", "0.0.0.0", "--port", "8000"]

今回はSTEP 8のテストをコンテナ内でも実行できるように、既存のrequirements.txtとtests/をそのまま含めています。本番配布を目的とする場合は、テスト・開発専用の依存関係を分離してイメージを小さくすることも検討します。

また、.envはイメージに組み込みません。環境変数は後ほどDocker Composeから渡す構成にします。

③ Dockerfileの各命令の役割

ChatGPT先生
ChatGPT先生

今回使用したDockerfileの命令を整理します。

命令役割
FROMベースとなるDockerイメージを指定する
WORKDIRコンテナ内の作業ディレクトリを指定する
COPYビルド時にファイルをイメージへコピーする
RUNイメージのビルド時にコマンドを実行する
EXPOSEアプリが使用するポートを示す
CMDコンテナ起動時のデフォルトコマンドを指定する

ここで特に押さえておきたいのは、RUNとCMDの違いです。

  • RUN:イメージのビルド時に実行する。今回はライブラリのインストール。
  • CMD:コンテナ起動時に実行するコマンドを指定する。今回はFastAPIの起動。

また、EXPOSE 8000はポートの使用を宣言するもので、これだけでWindows側にポートが公開されるわけではありません。ポート公開はDocker Compose側で設定します。

FastAPIの起動コマンドについて

uvicorn app.main:app --host 0.0.0.0 --port 8000

app.main:appは、app/main.py内のapp = FastAPI()を指します。

--host 0.0.0.0は、コンテナ内の全ネットワークインターフェースで接続を受け付けるための設定です。

--port 8000で待ち受けポートを指定しています。

.dockerignoreを設定する

ChatGPT先生
ChatGPT先生

Dockerイメージのビルドに不要なファイルを除外するため、.dockerignoreを作成します。

① .dockerignoreとは?

ChatGPT先生
ChatGPT先生

.dockerignoreは、Dockerイメージをビルドする際に、不要なファイルをビルドコンテキストから除外するための設定ファイルです。

ビルドコンテキストとは、Dockerがイメージを作成する際に参照できるファイルの集合です。

今回のDockerfileでは、以下のようにソースコードをコピーしています。

COPY app/ ./app/

.dockerignoreで除外されたファイルは、COPYの対象にできなくなります。

これにより、不要なファイルをビルダーへ送ることを防ぎ、ビルドの効率化や秘密情報の混入防止につながります。

なお、.gitignoreはGitの管理対象を制御するファイルであり、.dockerignoreとは役割が異なります。

② .dockerignoreを作成する

ChatGPT先生
ChatGPT先生

VS Codeでプロジェクト直下の /workspace に、.dockerignoreを新規作成してください。

# Git関連
.git/

# Pythonの仮想環境
.venv/
venv/

# Pythonのキャッシュ
**/__pycache__/
**/*.py[cod]

# テスト・静的解析のキャッシュ
.pytest_cache/
.ruff_cache/
.coverage
htmlcov/

# VS Codeの設定
.vscode/

# 環境変数(秘密情報の混入防止)
.env
.env.*
!.env.example

# ログファイル
*.log

<今回の設定で重要な点>

1. .envを除外する

.envにはDBのパスワードなどが含まれます。

Dockerイメージに組み込む必要はないため、ビルドコンテキストから除外します。

なお、.dockerignoreだけで秘密情報の管理が完結するわけではありません。実行時の環境変数は、以降の学習で設定します。

2. tests/は除外しない

今回のDockerfileには、以下の命令があります。

COPY tests/ ./tests/

STEP 8で作成したテストをコンテナ内で実行するため、tests/は除外しません。

同様に、requirements.txt、app/、migrations/など、Dockerfileから参照するファイルも残します。

Docker ComposeでアプリとDBを構成する

ChatGPT先生
ChatGPT先生

先ほど作成したDockerfileを使い、FastAPIとPostgreSQLをまとめて起動できるようにします。

既存のDev Containersは引き続き使用するため、開発用のcompose.yamlは変更せず、アプリ実行用のComposeファイルを新しく作成します。

① 今回の構成方針

ChatGPT先生
ChatGPT先生

現在の開発環境と今回作成する実行環境を整理します。

ファイル用途
compose.yaml既存のDev Containers用
compose.run.yaml今回作成するアプリ実行用

実行用のcompose.run.yamlでは、以下の2つのサービスを定義します。

  • app:DockerfileからFastAPIの実行用イメージをビルドする。
  • db:PostgreSQLを起動し、データをボリュームに保存する。

開発用Composeとプロジェクト名を分けることで、コンテナやDBの保存領域を独立させます。

② compose.run.yamlを作成する

ChatGPT先生
ChatGPT先生

プロジェクト直下の/workspaceに、compose.run.yamlを新規作成してください。

# 開発用Composeとコンテナ・ボリュームを分離する
name: fastapi-learning-run

services:
  app:
    # 実行用Dockerfileからイメージをビルドする
    build:
      context: .
      dockerfile: Dockerfile

    # 実行時に環境変数を読み込む
    env_file:
      - .env

    # Windows側の8000番ポートからAPIにアクセスできるようにする
    ports:
      - "127.0.0.1:8000:8000"

    # PostgreSQLが接続可能になってから起動する
    depends_on:
      db:
        condition: service_healthy

  db:
    image: postgres:17

    restart: unless-stopped

    # PostgreSQLの初期設定
    environment:
      POSTGRES_DB: ${POSTGRES_DB}
      POSTGRES_USER: ${POSTGRES_USER}
      POSTGRES_PASSWORD: ${POSTGRES_PASSWORD}

    # DBのデータをコンテナ外のボリュームに保存する
    volumes:
      - postgres_data:/var/lib/postgresql/data

    # PostgreSQLが接続可能か確認する
    healthcheck:
      test:
        [
          "CMD-SHELL",
          "pg_isready -U ${POSTGRES_USER} -d ${POSTGRES_DB}"
        ]
      interval: 5s
      timeout: 5s
      retries: 10

volumes:
  postgres_data:

③ 今回の設定で押さえておきたい仕組み

ChatGPT先生
ChatGPT先生

build:既存のDockerfileを使用する

build:
  context: .
  dockerfile: Dockerfile

context: .は、プロジェクト直下をビルドコンテキストに指定しています。

dockerfile: Dockerfileでは、先ほど作成したアプリ実行用Dockerfileを指定しています。既存の開発用Dockerfileは使用しません。

env_file:実行時に環境変数を渡す

env_file:
  - .env

.envの内容をFastAPIコンテナの環境変数として渡します。

STEP 7で作成したSettingsは、引き続きDATABASE_URLなどの設定値を読み込めます。

Dockerイメージに.envを組み込む必要はありません。

depends_on:DBの起動を待つ

depends_on:
  db:
    condition: service_healthy

単にPostgreSQLコンテナが起動しているだけでは、接続できる状態とは限りません。

そこで、healthcheckでDBの状態を確認し、healthyと判定されてからFastAPIコンテナを起動します。

ただし、これは起動時の待機処理であり、起動後のDB切断に対する再接続処理ではありません。

データの永続化について

今回のComposeには、開発用とは別の名前のComposeプロジェクトを設定しました。

name: fastapi-learning-run

通常、Composeが管理するボリューム名にはプロジェクト名が付くため、開発用DBとは別の保存領域になります。

つまり、今回のPostgreSQLは新しい空のDBから始まります。

この後の動作確認では、既存のAlembicマイグレーションを適用してからCRUDを確認する予定です。

なお、DBのポートはWindows側に公開していません。FastAPIからはComposeの内部ネットワーク経由でdb:5432に接続します。

Dockerイメージをビルドして起動する

ChatGPT先生
ChatGPT先生

これまでに作成したDockerfileとDocker Composeを使って、実際にFastAPIを動かしてみましょう!

この項目では、次の順序で進めます。

  1. Dockerイメージをビルドする
  2. FastAPIとPostgreSQLのコンテナを起動する
  3. コンテナの状態とログを確認する

まずはビルドからです。結果を確認してから起動に進みます。

① Dockerイメージをビルドする

ChatGPT先生
ChatGPT先生

今回作成したcompose.run.yamlでは、次のようにDockerfileを指定しました。

build:
  context: .
  dockerfile: Dockerfile

この設定に基づいて、Docker ComposeがFastAPI用のDockerイメージを作成します。

Dev Container内ではなく、Windows側でFastAPIプロジェクトのルートディレクトリに移動して実行します。

実行環境:Windows側のPowerShell

docker compose -f compose.run.yaml build app

このコマンドの意味は次のとおりです。

指定意味
-f compose.run.yaml今回作成した実行用Composeファイルを指定
buildDockerイメージをビルド
appappサービスのみをビルド

ビルドが成功すると、DockerfileのRUNやCOPYなどが処理され、FastAPIを実行するためのイメージが作成されます。

この段階では、まだコンテナは起動しません。

実行結果↓

ビルドの結果

② FastAPIとPostgreSQLのコンテナを起動する

ChatGPT先生
ChatGPT先生

作成したイメージを使用して、FastAPIとPostgreSQLを起動します。

実行環境:Windows側のPowerShell(プロジェクト直下)

docker compose -f compose.run.yaml up -d
オプション役割
-f compose.run.yaml実行用Composeファイルを指定
upサービスのコンテナを作成・起動
-dバックグラウンドで起動

depends_onとhealthcheckの設定により、PostgreSQLがhealthyになってからFastAPIの起動処理が進みます。

注意: 開発用Uvicornが8000番ポートを使用している場合は、先に停止してください。

実行結果↓

コンテナ起動結果

③ コンテナの状態を確認する

ChatGPT先生
ChatGPT先生

実行環境:Windows PowerShell(プロジェクト直下)

docker compose -f compose.run.yaml ps

このコマンドで、各コンテナの状態や公開ポートを確認できます。

期待する結果は以下のとおりです。

サービス期待する状態
appUp
dbUp (healthy)

実行結果↓

コンテナ起動確認

④ FastAPIの起動ログを確認する

ChatGPT先生
ChatGPT先生

実行環境:Windows PowerShell(プロジェクト直下)

docker compose -f compose.run.yaml logs app

このコマンドで、appコンテナのログを表示します。

正常であれば、以下のようなログが出力されます。

INFO:     Started server process [1]
INFO:     Waiting for application startup.
INFO:     Application startup complete.
INFO:     Uvicorn running on http://0.0.0.0:8000

特にApplication startup complete.が表示されているか確認しましょう。

実行結果↓

FastAPI起動ログの確認

APIとデータベースの動作確認

ChatGPT先生
ChatGPT先生

ここからは、Docker化したFastAPIアプリが実際に動作するか確認します。

まずはデータベースを使用しないAPIから確認しましょう。

① GET APIの動作確認

ChatGPT先生
ChatGPT先生

Windowsのブラウザで以下のURLにアクセスしてください。

http://localhost:8000/

正常であれば、STEP 1で作成したAPIのレスポンスが返ってきます。

{
  "message": "Hello, FastAPI!"
}

実行結果↓

FastAPI動作確認結果

② Alembicでデータベースにテーブルを作成する

ChatGPT先生
ChatGPT先生

今回のPostgreSQLは、開発用とは別のボリュームを使用しているため、新しい空のDBから始まっています。

STEP 6で学習したAlembicを使い、既存のマイグレーションを適用しましょう。

実行環境:Windows PowerShell(プロジェクト直下)

docker compose -f compose.run.yaml exec app alembic upgrade head

このコマンドは、起動中のappコンテナ内でalembic upgrade headを実行します。

  • exec app:起動中のFastAPIコンテナ内でコマンドを実行する。
  • alembic upgrade head:未適用のマイグレーションを最新まで適用する。

今回は、Alembicが今回の実行用DBを参照する設定になっていることを前提としています。

実行結果↓

マイグレーション結果

③ PostgreSQLのテーブルを確認する

ChatGPT先生
ChatGPT先生

実際にテーブルが作成されたか確認しましょう。

実行環境:Windows PowerShell(プロジェクト直下)

docker compose -f compose.run.yaml exec db psql -U fastapi -d fastapi_learning -c "\dt"

※ .envでDBユーザー名やDB名を変更している場合は、それぞれの値に置き換えてください。

このコマンドは、実行用のPostgreSQLコンテナ内でpsqlを実行し、テーブル一覧を表示します。

正常であれば、以下の2つのテーブルが表示されるはずです。

テーブル役割
productsSTEP 6で作成した商品テーブル
alembic_versionAlembicのマイグレーション適用状況を管理するテーブル

実行結果↓

テーブルの確認結果

④ CRUDの動作確認

ChatGPT先生
ChatGPT先生

続いて、FastAPIからPostgreSQLにアクセスできるか確認しましょう。

まずは、商品一覧を取得するGET APIを実行します。

実行環境:Windowsのブラウザ

  1. http://localhost:8000/docs にアクセスする。
  2. GET /products/{product_id}のAPIを開く。
  3. 「Try it out」→product_id:1で「Execute」をクリックする。

新しいDBにはまだ商品を登録していないため、正常にDBへ問い合わせができれば、商品が見つからず 404 Not Found が返る想定です。

実行結果↓

GET API確認結果

⑤ 商品を登録する(POST)

ChatGPT先生
ChatGPT先生

次は、Docker環境のPostgreSQLに実際にデータを書き込めるか確認します。

実行環境:Swagger UI

  1. http://localhost:8000/docs を開く。
  2. POST /products を選択する。
  3. 「Try it out」をクリックする。
  4. リクエストボディに必要な商品情報を入力する。
  5. 「Execute」をクリックする。

今回は、DBへ商品を登録するAPIを使用してください。

確認する内容:

  • HTTPステータスコードが 201 Created になること。
  • 登録した商品情報がレスポンスに返ってくること。

実行結果↓

POST API確認結果

⑥ 登録した商品を取得する(GET)

ChatGPT先生
ChatGPT先生

続いて、先ほど登録した商品を取得し、DBに保存されていることを確認します。

実行環境:Swagger UI

  1. GET /products/{product_id} を開く。
  2. 「Try it out」をクリックする。
  3. product_idに1を指定する。
  4. 「Execute」をクリックする。

期待するレスポンスは以下です。

{
  "id": 1,
  "name": "鉛筆",
  "price": 160
}

HTTPステータスコードは200 OKを期待します。

実行結果↓

登録したデータの取得結果

⑦ PostgreSQLのデータを直接確認する

ChatGPT先生
ChatGPT先生

最後に、APIから登録した商品が実際にPostgreSQLに保存されていることを、SQLで確認しましょう。

実行環境:Windows PowerShell(プロジェクト直下)

docker compose -f compose.run.yaml exec db psql -U fastapi -d fastapi_learning -c "SELECT id, name, price FROM products WHERE id = 1;"

※ DBユーザー名・DB名を変更している場合は置き換えてください。

期待する結果は、IDが1、商品名が鉛筆、価格が160のレコードが取得されることです。

実行結果↓

DB直接確認の結果

Docker環境でpytestを実行する

ChatGPT先生
ChatGPT先生

STEP 8で作成したAPIテストをDocker環境で実行し、既存の機能が正常に動作することを確認します。

ただし、現在の実行用compose.run.yamlには、開発環境に存在したtest-dbサービスがありません。

テストを実行する前に、テスト用DBの接続設定を確認する必要があります。

① テスト設定の確認結果

ChatGPT先生
ChatGPT先生

conftest.pyでは、通常のDB接続設定から、テスト専用の接続先を生成しています。

TEST_DB_HOST = "test-db"
TEST_DB_NAME = "fastapi_step8_test"

また、以下の安全対策が実装されています。

  • テスト専用DB以外へのマイグレーションを防止する。
  • get_db()を差し替え、テスト用Sessionを使用する。
  • テスト終了後にトランザクションをロールバックする。

既存のテストコードを変更する必要はありません。

ただし、今回作成したcompose.run.yamlにはtest-dbサービスが存在しません。

そのため、実行用Composeにもテスト専用のPostgreSQLを追加します。

② compose.run.yamlにテスト専用DBを追加する

ChatGPT先生
ChatGPT先生

今回の変更では、開発用DBとテスト用DBを完全に分離します。

compose.run.yamlのservices内に、既存のdbと同じ階層で以下を追加してください。

  # pytest専用のPostgreSQL
  test-db:
    image: postgres:17

    # テスト専用DBの初期設定
    environment:
      POSTGRES_DB: fastapi_step8_test
      POSTGRES_USER: ${POSTGRES_USER}
      POSTGRES_PASSWORD: ${POSTGRES_PASSWORD}

    # 通常のDBとは別のボリュームを使用する
    volumes:
      - postgres_test_data:/var/lib/postgresql/data

    # テスト用DBが接続可能か確認する
    healthcheck:
      test:
        [
          "CMD-SHELL",
          "pg_isready -U ${POSTGRES_USER} -d fastapi_step8_test"
        ]
      interval: 5s
      timeout: 5s
      retries: 10

続いて、ファイル末尾のvolumesに、テスト用ボリュームを追加します。

volumes:
  postgres_data:
  postgres_test_data:

なぜこの構成にするのか?

通常のDBとテスト用DBを別々のコンテナ・ボリュームで管理するためです。

今回のappとtest-dbは同じComposeプロジェクト内のネットワークに接続されるため、既存のfixtureからtest-dbというサービス名で接続できます。

また、conftest.pyが接続先をテスト専用DBに変更するため、既存の.envを変更する必要もありません。

なお、通常のFastAPIの起動にはテスト用DBが必要ないため、appのdepends_onには追加しません。pytest実行前にテスト用DBを起動する構成とします。(appではtest-dbは使用しない)

③ テスト専用DBを起動する

ChatGPT先生
ChatGPT先生

compose.run.yamlに追加したtest-dbを起動しましょう。

今回は、通常のFastAPIとPostgreSQLを起動したまま、テスト専用DBを追加で起動します。

実行環境:Windows PowerShell(プロジェクト直下)

docker compose -f compose.run.yaml up -d test-db

このコマンドでは、test-dbサービスを指定しているため、Composeファイルに定義されている全サービスではなく、test-dbとその依存サービスだけが起動対象になります。

今回のtest-dbには依存サービスがないため、テスト専用DBだけを追加で起動します。

期待する結果

以下のように表示されれば、起動は成功です。

Container fastapi-learning-run-test-db-1 Started

ただし、StartedだけではPostgreSQLが接続可能な状態になったことまでは確認できません。

実行結果↓

test-db起動結果

④ テスト専用DBの状態を確認する

ChatGPT先生
ChatGPT先生

実行環境:Windows PowerShell(プロジェクト直下)

docker compose -f compose.run.yaml ps test-db

期待する状態は以下です。

Up (healthy)

healthyになっていれば、テスト専用DBが接続を受け付けられる状態になっています。

実行結果↓

コンテナの確認結果

⑤ pytestを実行する

ChatGPT先生
ChatGPT先生

STEP 8で作成したテストを、今回Docker化したFastAPIコンテナ内で実行しましょう。

実行環境:Windows PowerShell(プロジェクト直下)

docker compose -f compose.run.yaml exec app python -m pytest -v

このコマンドでは、起動中のappコンテナ内でpytestを実行します。

今回の構成では、conftest.pyがDBの接続先をtest-dbに切り替え、テスト用Sessionを使用します。

そのため、先ほど登録した「鉛筆」のデータがある通常のDBとは分離してテストできます。

期待する結果

STEP 8で作成したテストがすべて成功し、最後に以下のような結果が表示されることを期待します。

XX passed in X.XXs

実行結果↓

pytest実行結果

自力課題

ChatGPT先生
ChatGPT先生

ここからは、今回学習した内容を使って、自分でDocker Composeの設定を変更してみましょう。

課題内容

ChatGPT先生
ChatGPT先生

現在のFastAPIは、Windows側の8000番ポートで公開されています。

これを、Windows側の8001番ポートでアクセスできるように変更してください。

要件

  • compose.run.yamlを修正する。
  • コンテナ内部の待ち受けポートは8000のままにする。
  • Windows側からhttp://localhost:8001/でアクセスできるようにする。
  • Dockerfileは変更しない。
  • 既存のPostgreSQLのデータは削除しない。

8001番ポートがほかのアプリで使用されていないことを前提とします。

完了条件

ChatGPT先生
ChatGPT先生
  • compose.run.yamlを修正し、ホスト側の公開ポートを8001に変更できている
  • コンテナ内部の待ち受けポートは8000のままになっている
  • Dockerfileを変更していない
  • 設定変更を反映してコンテナを起動できている
  • http://localhost:8001/ にアクセスし、HTTP 200と期待するJSONレスポンスを確認できている
  • 既存のPostgreSQLのデータ(鉛筆)が削除されずに残っている

実装結果

修正したコード(抜粋)

    # Windows側の8001番ポートからAPIにアクセスできるようにする
    ports:
      - "127.0.0.1:8001:8000"

実行結果

実行結果
GETリクエストの結果

ChatGPT先生のレビュー結果

ChatGPT先生
ChatGPT先生

実装そのものはできています。スクリーンショットからも、8001番ポートへの変更とDBデータの維持を確認できました。

これで「自力課題」完了です。

まとめ

今回はFastAPIアプリを実行するためのDocker環境を作成しました。

開発用と実行用の環境の目的の違い

Dev Containers用の環境は、VS Codeから開発作業を行うことが目的なので、コンテナを起動したままにする構成になっています。

FastAPIアプリ実行用の環境は、アプリケーションを実行することが目的なので、Dockerfileにてコンテナ起動時にUvicornを実行してFastAPIも起動します。

DBの扱いについて

実行用環境でも、通常のアプリ用DBとは別にテスト専用DBを作成しました。これは、pytest実施時に既存のデータに影響を与えないようにするためです。

FastAPIからPostgreSQLへの接続先は.envで次のように指定しています。

DATABASE_URL=postgresql+psycopg://fastapi:your_password@db:5432/fastapi_learning

ここで、ホスト名にlocalhostではなくdbを指定しています。

localhostを指定すると「FastAPIコンテナ自身」を指してしまうため、別コンテナで動いているPostgreSQLには接続できません。開発用・実行用のどちらのDocker ComposeでもPostgreSQLのサービス名をdbとしているため、同じDB接続設定を利用できます。

DockerfileとDocker Composeの役割の違い(FastAPI固有の内容ではないが)
  • Dockerfile:FastAPIを実行するためのイメージの作り方や起動コマンドを定義する
  • Docker Compose:FastAPI・PostgreSQL・ポートや環境変数など、コンテナをどのような構成で起動するかを定義する

これまで業務では、環境構築についてはインフラエンジニアが別途いて、環境は準備してくれていたのでアプリ開発に集中できたという、ある意味恵まれた案件ばかりでした。

そのため、Dockerや今回学んだような設定方法については、何となく見ればわかると思っていましたが、自力で作成できるほど理解していなかったということが、今回の学習で改めて感じました。

環境周りは実務で触れる機会が少なかった分、敬遠しがちでしたが自力で環境構築をできるようにしたいです。

コメント

タイトルとURLをコピーしました