Python: VSCodeからDocker内で動いているFastAPIにステップ実行デバッグを仕掛ける

# これをするとどんな良いことが？

デバッグが捗る。

## ソースコードにブレークポイントを設定して、

![スクリーンショット 2022-01-14 13.13.55.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/553715/0698299b-c468-63be-7f21-841bb8d5bf16.png)

## APIを実行してみると、

普通にブラウザで開くでもおk

![スクリーンショット 2022-01-14 13.17.31.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/553715/4e2bc5d1-7a31-5f4e-5bc8-baaa5eb6f5df.png)

## 処理が一時停止してVSCodeのデバッグビューがアクティブになる

その瞬間の変数の中身が見れたり、次のブレークポイントまで進んだり、一行ずつ進んだりしながらじっくり観察できる

![スクリーンショット 2022-01-14 13.22.32.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/553715/3d50f5fe-6a10-89a8-6a85-f97ee0764fb0.png)

その時だけ変数を勝手に書き換えて処理を続行させたりもできる

![スクリーンショット 2022-01-14 13.30.19.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/553715/fb8efae7-279f-e748-0afb-944841017056.png)

![スクリーンショット 2022-01-14 13.32.07.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/553715/f5c4d41e-69d1-eade-6d24-b5b5f076e615.png)

![スクリーンショット 2022-01-14 13.33.27.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/553715/07ff7e04-b692-c03c-9d74-67f8d752eb57.png)

# デメリット

動作が重くなる。
なので、デバッグの必要がない場合（サービス本稼働など）は、この手順を含まない方法で起動した方が良い。


# 試した環境

## 開発環境（端末）

* Macbook Air (M1)
* VSCode November 2021 (version 1.63)
* Python 3.9.9 (homebrewでインストールした)

## FastAPIサーバ(Docker)

* Docker Desktop for Mac 4.2.0
 * Docker version 20.10.12
 * docker-compose version 1.29.2

### コンテナ

* python:3.11-rc linux/x86-64

### pipでインストールしたライブラリ

|Package           |Version|
|----------------- |-------|
|anyio             |3.5.0|
|asgiref           |3.4.1|
|click             |8.0.3|
|fastapi           |0.71.0|
|h11               |0.12.0|
|idna              |3.3|
|pip               |21.2.4|
|pydantic          |1.9.0|
|python-multipart  |0.0.5|
|setuptools        |57.5.0|
|six               |1.16.0|
|sniffio           |1.2.0|
|starlette         |0.17.1|
|typing_extensions |4.0.1|
|uvicorn           |0.16.0|
|wheel             |0.37.0|

# VSCodeのプロジェクト構成（必要な各種設定ファイル）

```
.vscode/
  launch.json
  settings.json
src/
  ...
debug-docker-compose.yml
debug.Dockerfile
```

## debug.Dockerfile

debug と頭についているのは、通常稼働用と区別するため。

```Dockerfile
FROM --platform=linux/x86-64 python:3.11-rc

RUN apt update -y

RUN apt-get install build-essential
RUN pip install fastapi uvicorn python-multipart
RUN pip install debugpy

COPY src /src

WORKDIR /src

EXPOSE 8000

CMD ["python3", "-m", "debugpy", "--listen", "0.0.0.0:5678", "-m", "uvicorn", "prog:app", "--reload", "--host", "0.0.0.0", "--port", "8000"]
```

## debug-docker-compose.yml

debug と頭についているのは、通常稼働用と区別するため。

```yml
version: '3.7'

services:
  server:
    container_name: myfastapi
    build:
      context: .
      dockerfile: ./debug.Dockerfile
    restart: always
    volumes:
      - ./src:/src
    expose:
      - "8000"
      - "5678"
    ports:
      - 8000:8000
      - 5678:5678
```

## launch.json

```json
{
    // IntelliSense を使用して利用可能な属性を学べます。
    // 既存の属性の説明をホバーして表示します。
    // 詳細情報は次を確認してください: https://go.microsoft.com/fwlink/?linkid=830387
    "version": "0.2.0",
    "configurations": [
        {
            "type": "python",
            "request": "attach",
            "name": "FastAPI Remote Debug",
            "port": 5678,
            "host": "localhost",
            "pathMappings": [
                {
                    "localRoot": "${workspaceFolder}/src",
                    "remoteRoot": "/src"
                }
            ]
        }
    ]
}
```

## settings.json

```json
{
  "python.pythonPath": "/opt/homebrew/bin/python3",
  "files.watcherExclude": {
    "**/homebrew/**": true
  }
}
```

# 手順

## FastAPIコンテナのビルド設定

必要なファイルは、前項の [VSCodeのプロジェクト構成（必要な各種設定ファイル）](https://qiita.com/yagrush/items/e46ab11b22495cd2c0a0#vscode%E3%81%AE%E3%83%97%E3%83%AD%E3%82%B8%E3%82%A7%E3%82%AF%E3%83%88%E6%A7%8B%E6%88%90%E5%BF%85%E8%A6%81%E3%81%AA%E5%90%84%E7%A8%AE%E8%A8%AD%E5%AE%9A%E3%83%95%E3%82%A1%E3%82%A4%E3%83%AB) の通りです。
ここでは、それらの内容のうち、要点をピックアップして説明します。

### [debug.Dockerfile] debugpyインストールを含めておく

```Dockerfile
RUN pip install debugpy
```

### [debug.Dockerfile] APIサーバの起動コマンドにdebugpyの待ち受けを入れ込む

通常はこんな感じだと思うが、

```Dockerfile
CMD ["uvicorn", "prog:app", "--reload", "--host", "0.0.0.0", "--port", "8000"]
```

↓こう変える。

```Dockerfile
CMD ["python3", "-m", "debugpy", "--listen", "0.0.0.0:5678", "-m", "uvicorn", "prog:app", "--reload", "--host", "0.0.0.0", "--port", "8000"]
```

### [debug-docker-compose.yml] debugpyの待ち受けポートをコンテナの外に解放するよう設定する

```yml
    expose:
      - "5678"
    ports:
      - 5678:5678
 ```

## VSCodeの設定

### [launch.json] debugpyに接続する構成を登録しておく

```json
        {
            "type": "python",
            "request": "attach",
            "name": "FastAPI Remote Debug",
            "port": 5678,
            "host": "localhost",
            "pathMappings": [
                {
                    "localRoot": "${workspaceFolder}/src",
                    "remoteRoot": "/src"
                }
            ]
        }
```

## FastAPIコンテナを起動する

```bash
docker-compose -f debug-docker-compose.yml up --build
```

## VSCodeからdebugpyに接続する

launch.jsonに登録した「FastAPI Remote Debug」構成を実行する

![スクリーンショット 2022-01-14 13.06.02.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/553715/d8dd2473-8f47-8b93-1603-9ef2764b1b88.png)

繋がると↓こうなる

![スクリーンショット 2022-01-14 13.10.51.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/553715/65ec23a5-9804-a91e-3be2-5d42ef5ca695.png)

## ソースコードにブレークポイントを設定してみる

![スクリーンショット 2022-01-14 13.13.55.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/553715/0698299b-c468-63be-7f21-841bb8d5bf16.png)

## APIを実行してみる

普通にブラウザで開くでもおk

![スクリーンショット 2022-01-14 13.17.31.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/553715/4e2bc5d1-7a31-5f4e-5bc8-baaa5eb6f5df.png)

## 処理が一時停止してVSCodeのデバッグビューがアクティブになる

その瞬間の変数の中身が見れたり、次のブレークポイントまで進んだり、一行ずつ進んだりしながらじっくり観察できる

![スクリーンショット 2022-01-14 13.22.32.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/553715/3d50f5fe-6a10-89a8-6a85-f97ee0764fb0.png)

その時だけ変数を勝手に書き換えて処理を続行させたりもできる

![スクリーンショット 2022-01-14 13.30.19.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/553715/fb8efae7-279f-e748-0afb-944841017056.png)

![スクリーンショット 2022-01-14 13.32.07.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/553715/f5c4d41e-69d1-eade-6d24-b5b5f076e615.png)

![スクリーンショット 2022-01-14 13.33.27.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/553715/07ff7e04-b692-c03c-9d74-67f8d752eb57.png)