Docker 環境で Python FastAPI Webサービスを起動する

# Docker 環境で Python FastAPI Webサービスを起動する

こんにちは、@studio_meowtoon です。今回は、WSL Ubuntu の Docker 環境で、Python FastAPI Web アプリケーションをコンテナーとして起動する方法を紹介します。
![fastapi_on_docker.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3206449/3d64d60e-4024-7989-b440-91b82911bda5.png)

## 目的

Windows 11 の Linux でクラウド開発します。

こちらから記事の一覧がご覧いただけます。

https://qiita.com/studio_meowtoon/items/6119ab05d8b13527efd8

## 実現すること

ローカル環境の WSL Ubuntu の Docker 環境で、Dockerfile からビルドした Python FastAPI Web サービスのカスタムコンテナーイメージを起動します。

**Python ファイルのアプリをコンテナーとして起動**
```mermaid
flowchart LR
terminal --> uvicorn
subgraph Ubuntu
  subgraph Docker
    subgraph container[app-hello-fastapi]
      subgraph python
        uvicorn <--> app[main.py]
      end
    end
  end
end
style Ubuntu fill:#FFBCA4, stroke:#E0460F
style Docker fill:#EEFDFF, stroke:#74B9DC
style container fill:#FFD7E4, stroke:#E13C73
style python fill:#DCFFDC, stroke:#2F872F
style app fill:#ECECFF, stroke:#9674DC
style uvicorn fill:#FFFFDE, stroke:#B4AE43
```

**実行環境**

|要素|概要|
|:--|:--|
|terminal|ターミナル|
|Ubuntu|OS|
|Docker|コンテナー実行環境|

**Web サービス コンテナー**

|要素|概要|
|:--|:--|
|app-hello-fastapi|カスタムコンテナー|
|python|Python 実行環境|
|main.py|Python スクリプト|
|uvicorn|Web サーバー|

## 技術トピック

### FastAPI とは？

<details>
<summary>こちらを展開してご覧いただけます。</summary>

### [FastAPI](https://fastapi.tiangolo.com/ja/tutorial/)

:::note info
FastAPI は、Python の Web フレームワークの中でもとくに注目されている新しいフレームワークであり、多くの特徴とメリットがあります。

|キーワード|内容|
|:--|:--|
|高速な性能|FastAPI は非常に高速なWebフレームワークであり、ASGI (Asynchronous Server Gateway Interface) を活用することで高いパフォーマンスを提供します。非同期処理をサポートすることで、同時リクエストの処理が可能となります。|
|シンプルで直感的な記述|FastAPI はシンプルで直感的な構文を持ち、少ないコードで強力な API を構築できます。Python の型ヒント (Type Hints) を活用して、入力の検証やドキュメントの自動生成が行えます。|
|自動生成されたドキュメント|FastAPI は Swagger UI や Redoc を使って API のドキュメントを自動生成します。API エンドポイント、リクエストボディ、レスポンスなどが自動的にドキュメント化され、API の理解やテストが容易になります。|
|入力検証とシリアライゼーション|FastAPI はリクエストの入力データを自動的に検証し、Pydantic を使って型情報をシリアライズします。入力データのバリデーションエラーを自動的に処理できます。|
|依存性注入|FastAPI は依存性注入をサポートしており、依存関係の注入によってコードの再利用性とテスト容易性が向上します。依存性注入を活用することで、アプリケーションの部品を疎結合に保ちながら効果的に結合できます。|
|サポートされるデータベースと ORM|FastAPI はさまざまなデータベースと ORM (Object-Relational Mapping) をサポートしています。たとえば、SQLAlchemy、Tortoise ORM、MongoDB、Redis などが利用可能です。|
|セキュリティ|FastAPI は標準的なセキュリティ機能を提供しています。OAuth2.0 や API キーのサポート、HTTPS 対応など、セキュアな API の構築が可能です。|

:::

</details>

### Dockerfile とは？

<details>
<summary>こちらを展開してご覧いただけます。</summary>

### [Dockerfile](https://docs.docker.jp/engine/reference/builder.html#dockerfile)

:::note info
Dockerfile は、Docker コンテナーを構築するためのテキストファイルです。Docker コンテナーはアプリケーションやサービスを実行するための環境を含む軽量でポータブルな仮想化ユニットです。

|キーワード|内容|
|:--|:--|
|スクリプト形式|Dockerfile はシンプルなスクリプト形式で記述されるため、コンテナーのビルドプロセスを自動化することが容易です。|
|レイヤー構造|Docker イメージは Dockerfile の各命令が実行される際にレイヤーとして生成され、再利用やキャッシュが可能な構造となっています。|
|バージョン管理|Dockerfile はテキストベースであるため、コードと同様にバージョン管理システムで管理しやすいです。|
|ポータビリティ|Dockerfile により、アプリケーションとその依存関係が1つのコンテナイメージにパッケージ化されるため、異なる環境間での移植性が高まります。|
|自動化と効率化|Dockerfile を使用することで、アプリケーションのビルドや環境構築を自動化できます。これにより、手動での作業時間やヒューマンエラーが減り、開発・デプロイプロセスが効率的になります。|
|再現性|Dockerfile はビルド手順を完全に定義するため、異なる環境で同じアプリケーションを再現できます。これにより、開発、テスト、本番環境間での一貫性が確保されます。|
|環境の分離|Docker コンテナーはホストシステムから分離されるため、アプリケーションの依存関係やライブラリの衝突を回避し、より安全な環境で実行できます。|
|拡張性|Dockerfile を使用することで、カスタムイメージを作成できます。このため、特定のニーズに合わせてカスタマイズされたコンテナイメージを容易に作成できます。|

:::

</details>

## 開発環境

+ Windows 11 Home 23H2 を使用しています。

:::note info
[WSL](https://learn.microsoft.com/ja-jp/windows/wsl/about) の Ubuntu を操作しますので macOS の方も参考にして頂けます。  
:::

<details>

WSL (Microsoft Store アプリ版) [※ こちらの関連記事からインストール方法をご確認いただけます](https://qiita.com/studio_meowtoon/items/cd0c4a607d451dbf5430)

```sh
> wsl --version
WSL バージョン: 2.2.4.0
カーネル バージョン: 5.15.153.1-2
WSLg バージョン: 1.0.61
```

Ubuntu [※ こちらの関連記事からインストール方法をご確認いただけます](https://qiita.com/studio_meowtoon/items/cd0c4a607d451dbf5430)

```sh
$ lsb_release -a
No LSB modules are available.
Distributor ID: Ubuntu
Description:    Ubuntu 24.04 LTS
Release:        24.04
Codename:       noble
```

Docker [※ こちらの関連記事からインストール方法をご確認いただけます](https://qiita.com/studio_meowtoon/items/3bceee25ac67cc73ebbe)

```sh
$ docker --version
Docker version 27.0.3, build 7d4bcd8
```

</details>

:::note warn
この記事では基本的に **Ubuntu のターミナルで操作**を行います。**Vim を使用してコピペする方法**をはじめて学ぶ人のために、以下の記事で手順を紹介しています。ぜひ挑戦してみてください。
:::

https://qiita.com/studio_meowtoon/items/7771ad48676926e41463

## 作成する Web アプリケーションの仕様

|No|エンドポイント|HTTPメソッド|MIME タイプ|
|:--|:--|:--|:--|
|1|/api/data|GET|application/json|

:::note info
**/api/data** というエンドポイントに対して HTTP GET リクエストを送信すると、JSON データがレスポンスされるシンプルな Web サービスを実装します。
**{"message":"Hello World!"}**
:::

## Hello World を表示する手順

### Python の確認

:::note warn
通常 Ubuntu 24.04 には、Python はプリインストールされています。
:::

```sh
$ python3 --version
Python 3.12.3
```

### プロジェクトの作成

プロジェクトフォルダーを作成します。  
※ ~/tmp/hello-fastapi をプロジェクトフォルダーとします。

```sh
$ mkdir -p ~/tmp/hello-fastapi
$ cd ~/tmp/hello-fastapi
```

### Python ファイルの作成

**main.py** Python ファイルを作成します。

```sh
$ mkdir -p app
$ vim app/main.py
```

ファイルの内容

```py:app/main.py
from fastapi import FastAPI

app = FastAPI()

@app.get('/api/data')
async def get_data():
    response = {
        'message': 'Hello World!'
    }
    return response
```

<details>
<summary>型宣言を省略せずに記述したコード</summary>

```py:app/main.py
from fastapi import FastAPI

app: FastAPI = FastAPI()

@app.get('/api/data')
async def get_data():
    response: dict[str, str] = {
        'message': 'Hello World!'
    }
    return response
```

</details>

pipx をインストールします。

```sh
$ sudo apt update
$ sudo apt install pipx
```

バージョンを確認します。

```sh
$ pipx --version
1.4.3
```

pipx ツールへの PATH を追加します。

```sh
$ pipx ensurepath
```

パッケージを仮想環境にインストールします。

```sh
$ pipx install uvicorn
```

仮想環境にパッケージを追加でインストールします。

```sh
$ pipx inject uvicorn fastapi
```

ローカルで実行します。  
停止するときは ctrl + C を押します。

```sh
$ uvicorn app.main:app \
    --host 0.0.0.0 --port 8000
```

別ターミナルから curl コマンドで確認します。

```sh
$ curl -v http://localhost:8000/api/data -w '\n'
```

出力

```sh
* Host localhost:8000 was resolved.
* IPv6: ::1
* IPv4: 127.0.0.1
*   Trying [::1]:8000...
* connect to ::1 port 8000 from ::1 port 57270 failed: 接続を拒否されました
*   Trying 127.0.0.1:8000...
* Connected to localhost (127.0.0.1) port 8000
> GET /api/data HTTP/1.1
> Host: localhost:8000
> User-Agent: curl/8.5.0
> Accept: */*
>
< HTTP/1.1 200 OK
< date: Sat, 10 Aug 2024 13:40:43 GMT
< server: uvicorn
< content-length: 26
< content-type: application/json
<
* Connection #0 to host localhost left intact
{"message":"Hello World!"}
```

:::note info
ここまでの手順で、ターミナルに **{"message":"Hello World!"}** と表示され、**JSON データ**を取得することができました。
:::

### コンテナイメージをビルド

Dockerfile を作成します。

```sh
$ vim Dockerfile
```

ファイルの内容

```Dockerfile:Dockerfile
# set up the container.
FROM python:3.12-slim

# set the working dir.
WORKDIR /app

# copy the app dir.
COPY app app

# install libraries.
RUN pip install --no-cache-dir fastapi uvicorn

# expose the port.
EXPOSE 8000

# command to run the app using uvicorn.
CMD ["uvicorn","app.main:app","--host","0.0.0.0","--port","8000"]
```

<details>
<summary>説明を開きます。</summary>

|要素|説明|
|:--|:--|
|FROM python:3.10-slim|ベースイメージとして、Python 3.10 の slim版イメージを使用します。|
|WORKDIR /app|コンテナー内での作業ディレクトリを /app に設定します。|
|COPY app app:|ホストマシンのカレントディレクトリにある app ディレクトリを、コンテナー内の /app ディレクトリにコピーします。|
|RUN pip install --no-cache-dir fastapi uvicorn|必要な Python ライブラリをインストールします。fastapi と uvicorn は、FastAPI フレームワークと ASGI サーバーのライブラリです。--no-cache-dir フラグは、キャッシュを使用せずにライブラリをインストールすることを意味します。|
|EXPOSE 8000|コンテナーがポート 8000 を公開することを宣言します。|
|CMD ["uvicorn", "app.main:app", "--host", "0.0.0.0", "--port", "8000"]|コンテナー内で実行されるデフォルトのコマンドを定義します。FastAPI アプリケーションを起動するために uvicorn コマンドが使用されます。app.main:app は、FastAPI アプリケーションインスタンスを指すものです。--host 0.0.0.0 は、すべてのネットワークインターフェイスからの接続を許可することを意味し、--port 8000 は uvicorn がリッスンするポート番号を指定します。|

</details>

Docker デーモンを起動します。

```sh
$ sudo service docker start
```

:::note warn
Docker 環境をお持ちでない場合は、以下の関連記事から Docker Engine のインストール手順をご確認いただけます。
:::

https://qiita.com/studio_meowtoon/items/3bceee25ac67cc73ebbe

コンテナイメージをビルドします。

```sh
$ docker build \
    --no-cache \
    --tag app-hello-fastapi:latest .
```

コンテナイメージを確認します。

```sh
$ docker images | grep app-hello-fastapi
app-hello-fastapi    latest    c31921620823   53 seconds ago   153MB
```

:::note info
ここまでの手順で、ローカル環境の **Docker** にアプリの**カスタムコンテナイメージ**をビルドすることができました。
:::

### コンテナーを起動

ローカルでコンテナーを起動します。
※ コンテナーを停止するときは ctrl + C を押します。

```sh
$ docker run --rm \
    --publish 8000:8000 \
    --name app-local \
    app-hello-fastapi:latest
```

:::note info
ここまでの手順で、ローカル環境の **Docker** でアプリの**カスタムコンテナー**を起動することができました。
:::

### コンテナーの動作確認

別ターミナルから curl コマンドで確認します。

```sh
$ curl -v http://localhost:8000/api/data -w '\n'
```

出力

```sh
* Host localhost:8000 was resolved.
* IPv6: ::1
* IPv4: 127.0.0.1
*   Trying [::1]:8000...
* Connected to localhost (::1) port 8000
> GET /api/data HTTP/1.1
> Host: localhost:8000
> User-Agent: curl/8.5.0
> Accept: */*
>
< HTTP/1.1 200 OK
< date: Sat, 10 Aug 2024 14:23:23 GMT
< server: uvicorn
< content-length: 26
< content-type: application/json
<
* Connection #0 to host localhost left intact
{"message":"Hello World!"}
```

:::note info
ここまでの手順で、ターミナルに **{"message":"Hello World!"}** と表示され、**JSON データ**を取得することができました。
:::

コンテナーの状態を確認してみます。

```sh
$ docker ps
CONTAINER ID   IMAGE                      COMMAND                   CREATED          STATUS          PORTS                                       NAMES
a132ae388071   app-hello-fastapi:latest   "uvicorn app.main:ap…"   22 seconds ago   Up 21 seconds   0.0.0.0:8000->8000/tcp, :::8000->8000/tcp   app-local
```

### コンテナーに接続

別ターミナルからコンテナーに接続します。

```sh
$ docker exec -it app-local /bin/sh
```

コンテナー接続後にディレクトリを確認します。  
※ コンテナーから出るときは ctrl + D を押します。

```
# pwd
/app
# ls -lah
total 12K
drwxr-xr-x 1 root root 4.0K Aug 10 14:21 .
drwxr-xr-x 1 root root 4.0K Aug 10 14:24 ..
drwxr-xr-x 3 root root 4.0K Aug 10 13:26 app
```

top コマンドで状況を確認します。

```
# apt update
# apt install procps
# top
top - 14:26:44 up  2:11,  0 user,  load average: 0.11, 0.07, 0.01
Tasks:   3 total,   1 running,   2 sleeping,   0 stopped,   0 zombie
%Cpu(s):  0.0 us,  0.1 sy,  0.0 ni, 99.9 id,  0.0 wa,  0.0 hi,  0.0 si,  0.0 st
MiB Mem :  15949.2 total,  14997.9 free,    825.5 used,    403.4 buff/cache
MiB Swap:   4096.0 total,   4096.0 free,      0.0 used.  15123.7 avail Mem

    PID USER      PR  NI    VIRT    RES    SHR S  %CPU  %MEM     TIME+ COMMAND
      1 root      20   0   55068  48880  14176 S   0.3   0.3   0:00.75 uvicorn
      7 root      20   0    2576    892    800 S   0.0   0.0   0:00.00 sh
    175 root      20   0    8992   4732   2860 R   0.0   0.0   0:00.00 top
```

コンテナーの情報を表示してみます。

```cs
# cat /etc/*-release
PRETTY_NAME="Debian GNU/Linux 12 (bookworm)"
NAME="Debian GNU/Linux"
VERSION_ID="12"
VERSION="12 (bookworm)"
VERSION_CODENAME=bookworm
ID=debian
HOME_URL="https://www.debian.org/"
SUPPORT_URL="https://www.debian.org/support"
BUG_REPORT_URL="https://bugs.debian.org/"
```

:::note warn
このコンテナーは Debian GNU/Linux をベースに作成されています。つまり、Debian GNU/Linux と同じように扱うことができます。
:::

### ディレクトリ・ファイル構成

プロジェクトのファイル構成を表示してみます。

```sh
$ tree -I '__pycache__'
.
├── Dockerfile
└── app
    └── main.py
```

## まとめ

WSL Ubuntu の Docker 環境で、Dockerfile からビルドした Python FastAPI Web サービスのカスタムコンテナーを起動することができました。

:::note warn
クラウド開発においては、**Dockerfile の理解**は重要です。自動ビルドツールもありますが、手動で書く必要があるケースもあります。**Ubuntu** を使うと **Linux の知識**も身に付きます。最初は難しく感じるかもしれませんが、徐々に進めていけば自信を持って書けるようになります。
:::

どうでしたか？　WSL Ubuntu で、Python FastAPI Web アプリケーションを手軽に実行できます。ぜひお試しください。今後も Python の開発環境などを紹介しますので、ぜひお楽しみにしてください。

## 推奨コンテンツ

https://qiita.com/studio_meowtoon/items/b6201096ffebb140fefe