Docker上で動くgunicorn + DjangoアプリケーションをVS Codeからリモートデバッグする

# はじめに
Pythonを使ったWeb開発を始めるにあたり、まずは快適なデバッグ環境を整えようと考えました。Web上では、```Docker``` ```gunicorn+Django``` ```VSCode```のすべての要素をカバーした情報がうまく見つからなかったので、備忘録として残します。

## 構成の概要

![python remote debugging.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/227813/c92a49d0-10af-b7d0-0807-4142b3a4d237.png)

### クライアント側
VS Codeに、[Python extension for VS Code](https://marketplace.visualstudio.com/items?itemName=ms-python.python)をインストールして、ポート3000番経由で、サーバー側で起動しているデバッグサーバーにアクセスします。

### サーバー側
[ptvsd (Python Tools for Visual Studio debug server)](https://github.com/Microsoft/ptvsd/)を gunicorn から呼び出し、デバッグサーバーを立ち上げます。デバッグサーバーはポート3000番でクライアントの接続を待ち受けます。

### 各種バージョン
- Mac OS 10.14.6
- Visual Studio Code 1.37
- Docker 19.03.2
- Python 3.7.4
- gunicorn 19.9.0
- Django 1.11.8
- ptvsd 4.3.2

# デバッグの準備
## 1.クライアント側準備
### 1-1.Python extention for Visual Studio Codeのインストール
VS Codeのメニューから、該当の拡張機能をインストールします。

### 1-2.VS Codeにデバッグ設定を追加する
左側のメニューからデバッグアイコン（虫の形）を選択し、上部のメニューより「設定の追加」⇨「Python」⇨「Remote Attach」を選択します。

<img width="428" alt="vscode-debug.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/227813/0bb771cc-7eac-83fe-20fc-1026e158646f.png">

開いた```launch.json```に以下のように編集します。

```json
{
    "version": "0.2.0",
    "configurations": [
        {
            "name": "Remote Django Remote Debug",
            "type": "python",
            "request": "attach",
            "port": 3000,
            "host": "localhost",
            "pathMappings": [
                {
                    "localRoot": "${workspaceFolder}",
                    "remoteRoot": "."
                }
            ]
        },
    ]
}
```

## 2.サーバー側準備
### 2-1.ptvsdのインストール
ptvsd 4.3.2 を requirements.txtに加えます。昔は[3.0.0に固定しないとリモートデバッグが動かないバグ](https://github.com/Microsoft/vscode-python/issues/133)があったが、最新版では解消しているようです。

### 2-2.Docker環境で3000番ポートを解放する
```docker-compose.yml``` に以下の設定を加えます。

```yml
ports:
  - "3000:3000"
```

### 2-3.gunicorn から ptvsd を起動する
Django のエントリポイント（最初に呼び出される処理）に ptvsd を呼び出すコードを追記します。ここでは、ローカルホストで先ほど解放したポート番号3000番で接続を受け付けます。

```python
import ptvsd
ptvsd.enable_attach(address=('0.0.0.0', 3000))
```

エントリポイントごとのファイルは以下の通りです。
- 管理コマンドから起動する場合: ```manage.py```
- gunicorn から mysite:wsgi と呼び出す場合: ```mysite/wsgi.py```


### 2-4.gunicornのタイムアウトを伸ばす
デバッグ中にブレークポイントで停止させていると、gunicorn がタイムアウトします。[こちら](https://docs.gunicorn.org/en/latest/settings.html?highlight=timeout#timeout)に記載がある通り、以下の引数で gunicorn のプロセスのタイムアウトを変更できます。

```bash
gunicorn "mysite.wsgi"　--timeout 1000000
```

また、[環境変数として](http://docs.gunicorn.org/en/stable/settings.html)```.evn.docker-compose```ファイルに記述すると、```docker-compose.yml```の修正せずにTIMEOUTを変更できます。

```bash
GUNICORN_CMD_ARGS="--timeout=1000000"
```

## 3. デバッグ
### 3-1.アプリケーションの起動
```docker-compose up```等のコマンドで、デバッグ対象のアプリケーションを起動します。gunicorn 起動時に ptvsd が呼び出されて、ポート3000番でクライアントの接続を待ち受けます。

### 3-2.ブレイクポイントの設定
デバッガーで確認したいソースコードの行の左をクリックし、ブレークポイント（赤丸）を設定します。

### 3-3.デバッガのアタッチ
左側のメニューからデバッグアイコン（虫の形）を選択します。上部のドロップダウンメニューから「Remote Django Remote Debug」を選択し、デバッグを開始します。

### 3-4.アプリケーションへのアクセス
ローカル環境で起動したアプリケーションにアクセスします。うまくデバッガーに接続できていれば、ブレークポイントに到達した段階で、アプリケーションの処理が一時停止し、デバッグが可能となります。

![vscode-debug3.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/227813/56a388f3-cb00-c371-d613-53bc2deba2d3.png)


快適なデバッグライフを！

## 参考文献
- http://ramkulkarni.com/blog/debugging-django-project-in-docker/
- https://gist.github.com/veuncent/1e7fcfe891883dfc52516443a008cfcb
- https://code.visualstudio.com/docs/python/debugging