delhi09の勉強日記

技術トピック専用のブログです。自分用のメモ書きの投稿が多いです。あくまで「勉強日記」なので記事の内容は鵜呑みにしないでください。

Docker上のDjangoアプリをVS Codeでデバッグ実行する (2020/5/9時点)

前回MacOS上でDjangoアプリをVS Codeデバッグ実行することに成功したので、今回はDocker上のDjangoアプリをVS Codeデバッグ実行する方法を検証する。

公式ドキュメントは以下
code.visualstudio.com

目的

→ Docker上でもホストOS上と遜色ない環境で、VS Codeで開発ができるのであれば、ローカルにvenvで開発環境を構築する必要がなくなる。

先に検証結果

  • Docker上でDjangoアプリをVS Codeデバッグ実行することはできた。
  • Docker上でVS Codeを使って開発もできたが、以下の理由により現時点(2020/5/8)で開発環境に使うのは厳しいと感じた。
    • 1.コンテナの起動に時間がかかる。(VS Codeやextentionをコンテナ起動時にインストールするため)
    • 2.1を改善するために、インストールするextensionを必要最低限に絞ることはできるが、その場合、使いたいextensionが使えなくなる。
    • 3.2の対応をしても、やはりコンテナの起動に時間がかかる。

→ 現状ではDockerコンテナ上でVS Codeを使用するのはデバッグ用途のみに留めておいた方がよさそう。

Docker上のDjangoアプリをVS Codeデバッグ実行するまでの手順

1.VS CodeMicrosoft公式の「Remote Development」というパッケージをインストールする。

以下をインストール
f:id:kamatimaru:20200509225653p:plain

2.devcontainer.jsonを作成する。

リモート環境のVS Codeの設定を記述する「devcontainer.json」というファイルを作成する。

まずはプロジェクトのルートディレクトリ直下に「.devcontainer」というディレクトリを作成する。

次に、.devcontainer配下に「devcontainer.json」というファイルを作成する。

djangogirls-tutorial-docker/
└── .devcontainer
    └── devcontainer.json

3.devcontainer.jsonに設定を記述する。

devcontainer.jsonに設定を記述していく。
一から書いていくのは辛いので、ベースとなるテンプレートが欲しいなと思っていたところ、Microsoft公式のリポジトリが存在した。
github.com

この中に「python-3」というテンプレートがあったので、これをベースにすることにした。
https://github.com/microsoft/vscode-dev-containers/blob/master/containers/python-3/.devcontainer/devcontainer.json

最終的にdevcontainer.jsonは以下のようになった。

{
    "name": "djangogirls-tutorial-docker",
    "dockerComposeFile": "../docker-compose.yml",
    "service": "my-djangogirls-tutorial",
    "context": "..",
    "workspaceFolder": "/root/djangogirls-tutorial",
    "appPort": [
        8000
    ],
    // Set *default* container specific settings.json values on container create.
    "settings": {
        "terminal.integrated.shell.linux": "/bin/bash",
        "editor.cursorSurroundingLinesStyle": "all",
        "editor.formatOnSave": true,
        "editor.tabCompletion": "on",
        "editor.codeActionsOnSave": {
            "source.organizeImports": true
        },
        "workbench.startupEditor": "newUntitledFile",
        "git.suggestSmartCommit": false,
        "files.autoSave": "onFocusChange",
        "files.exclude": {
            "**/.mypy_cache": true,
        },
        "git.untrackedChanges": "separate",
        "csscomb.preset": {},
        "todo-tree.tree.showScanModeButton": false,
        "python.pythonPath": "python3",
        "python.linting.enabled": true,
        "python.linting.pylintEnabled": false,
        "python.linting.flake8Enabled": true,
        "python.formatting.provider": "black",
        "python.linting.mypyEnabled": true,
        "python.formatting.blackPath": "black",
        "python.linting.flake8Path": "flake8",
        "python.linting.mypyPath": "mypy",
        "autoDocstring.startOnNewLine": true,
        "autoDocstring.docstringFormat": "sphinx",
    },
    // Add the IDs of extensions you want installed when the container is created.
    "extensions": [
        //"donjayamanne.githistory",
        //"eamodio.gitlens",
        //"Gruntfuggly.todo-tree",
        //"jebbs.plantuml",
        //"mechatroner.rainbow-csv",
        //"mrmlnc.vscode-csscomb",
        //"ms-azuretools.vscode-docker",
        "MS-CEINTL.vscode-language-pack-ja",
        "ms-python.python",
        //"ms-vscode-remote.remote-containers",
        //"ms-vscode-remote.remote-ssh",
        //"ms-vscode-remote.remote-ssh-edit",
        //"ms-vscode-remote.remote-wsl",
        //"ms-vscode-remote.vscode-remote-extensionpack",
        //"njpwerner.autodocstring",
        //"oderwat.indent-rainbow",
        //"streetsidesoftware.code-spell-checker",
        //"thebarkman.vscode-djaneiro",
        //"wmaurer.change-case",
    ]
    // Use 'forwardPorts' to make a list of ports inside the container available locally.
    // "forwardPorts": [],
    // Use 'postCreateCommand' to run commands after the container is created.
    // "postCreateCommand": "pip3 install --user -r requirements.txt",
    // Uncomment to connect as a non-root user. See https://aka.ms/vscode-remote/containers/non-root.
    // "remoteUser": "vscode"
}

以下、テンプレートから編集した箇所

build

テンプレートでは「build」という項目が存在し、その中にネストさせる形で「dockerfile」、「context」などの設定を書いているが、設定ファイルが不正であるというエラーが発生してしまった。

他のテンプレートを見ると「build」という項目は使用していなかったので、削除してみた。するとエラーが発生しなくなった。
github.com

name

任意のプロジェクト名を書く。

dockerComposeFile

今回はDocker Composeを使用しているので、項目名には「dockerFile」ではなく「dockerComposeFile」を使用する。

「dokcer-compose.yml」のパスを「devcontainer.json」からの相対パスで指定する。

service

デバッグ対象のコンテナのサービス名称(docker-compose.ymlに定義したもの)を書く。

context

この設定値が何に使われているのかよく分からなかった。
実験で関係ないパスを指定してみたり、コメントアウトしてみたりしたが、コンテナは正常に起動した。

docker本体の「ビルドコンテキスト」という概念と関係があるのだろうか?
docs.docker.com

とりあえず、以下のテンプレートのコメントを見ると、devcontainer.jsonの1階層上を指定する場合は「..」と書けばよいようなので、倣っておく。
github.com

この項目の意味を理解することは宿題とする。

workspaceFolder

VS Codeワークスペースのルートディレクトリにしたいコンテナ上のディレクトリのパスを書く。
通常はコンテナ側のマウント先のディレクトリと同じになるはず。

※ 設定しないと以下のように「/」がワークスペースのルートディレクトリとして認識されてしまい、コンテナ上の全てのディレクトリがVS Codeに表示されてしまうので注意。
f:id:kamatimaru:20200509182839p:plain

appPort

DockerのポートフォーワードでホストOS側からアクセスするポートを設定する項目らしい。
Dockerの「EXPOSE」、Docker Composeのportsと意味合いが異なるのかどうか分からないが、指定しておく。

code.visualstudio.com

settings

python-3のテンプレートに記載されている設定値と現状のVS Codeのsettings.jsonの設定値をマージした。
※ 結果的に、コンテナ立ち上げの時間を短縮するためにextensionを削ったので、必要ない設定値も混ざっている。

※ settings.jsonは設定画面で以下のボタンを押すと表示できる。
f:id:kamatimaru:20200510000157p:plain

extensions

コンテナ上のVS Codeにインストールしたいextensionsの一覧を記載する。
現在VS Codeにインストールされているextensionsの一覧は以下のコマンドで取得できる。

$ code --list-extensions

最初はホストOS側でインストールしているextensionを全てインストールしようとしていたが、先述の通りコンテナの立ち上げに時間がかかるので、少しでも早くしようと

以外はコメントアウトした。

4.launch.jsonを作成する。

Docker上でのDjangoアプリの起動設定を記述する「launch.json」というファイルを「.vscodeディレクトリ配下に作成する。

5.launch.jsonに設定を記述する。

基本的には前回の設定と同じでよい。
但し、今回はIPアドレスを「0.0.0.0」で起動する必要があるので、そこだけ設定を追加する。

{
    // IntelliSense を使用して利用可能な属性を学べます。
    // 既存の属性の説明をホバーして表示します。
    // 詳細情報は次を確認してください: https://go.microsoft.com/fwlink/?linkid=830387
    "version": "0.2.0",
    "configurations": [
        {
            "name": "Python: Django",
            "type": "python",
            "request": "launch",
            "program": "${workspaceFolder}/manage.py",
            "args": [
                "runserver",
                "--noreload",
                "--nothreading",
                "0.0.0.0:8000"
            ],
            "django": true
        }
    ]
}

6.Dockerコンテナにtarとgitをインストールする。

[Dockerfile]

RUN yum install -y tar
RUN yum install -y git

tar : VS Codeを解凍するために必要。コマンドが存在しないとコンテナ起動時にエラーが発生するので必須。

git : コンテナ上のVS Codeでgitを使用するために必要。コンテナ上のVS Codeでgitを使用しない場合はなくても問題ない。

7.Dockerfileの「ENTRYPOINT」をコメントアウトする。

コンテナ上でDjangoアプリをVS Code上からデバッグ実行する必要がある。
ただ、既にdocker-entrypoint.shでコンテナ起動時にサーバーを起動するようにしているため、コンテナ起動後にデバッグ実行しようとするとポートが重複して「Error: That port is already in use.」が発生してしまう。

また、Djangoアプリのサーバーはコンテナのメインプロセスであるため、killするとコンテナが停止してしまう。
docs.docker.com

従って、きれいな解決方法ではないが、Dockerfileの「ENTRYPOINT」をコメントアウトして、デバッグ時にVS Code上からサーバーを起動するようにした。

マイグレーションもdocker-entrypoint.sh内で行っていたため、コンテナ起動後に手動で実行する必要があるので忘れないように注意。

[Dockerfile]

#ENTRYPOINT /bin/bash ${DEPLOY_DIR}/docker-entrypoint.sh

8.VS Codeでコンテナを起動する。

1.対象のワークスペース直下でVS Codeを開く。

2.「Remote Development」をインストールすると、VS Codeの画面左下に緑色のボタンが表示されるようになるので、それを押す。
f:id:kamatimaru:20200510002600p:plain

3.「Remote Containers: Reopen in Container」を押す。
f:id:kamatimaru:20200510000749p:plain

4.コンテナの起動・コンテナへのVS Code・extensionsのインストールが完了するまでしばらく待つ。(この時間が結構かかる。)

5.「ENTRYPOINT」をコメントアウトしているので、コマンドラインからコンテナにログインして

$ python3 manage.py migrate 

を実行する。

6..Djangoのサーバーをデバッグで起動する。(操作は前回と同じ)

7.ブレークポイントを貼って、ブレークポイントで処理が停止することを確認できればOK
f:id:kamatimaru:20200510002043p:plain

以上