Docker for Windowsを使うと、Visual StudioからF5でコンテナ内にアプリをデプロイしてデバッグできるようになったという記事を見たのですが
気になるのは、どうやってデバッグしているかということです。ポイントとしてはDockerとホストで共有フォルダの設定をしないといけないということとと、もともとMicrosoftがクロスプラットフォームなデバッグ向けにMIEngineというものを開発しているという点です。
このあたりを手がかりに探ってみました*1。
まず、Visual Studio側でDocker Suppotを設定したときに作成されるファイルを見てみます。Dockerfileは非常にシンプルです。
FROM microsoft/aspnetcore:1.0.1 ENTRYPOINT ["dotnet", "WebApplication1.dll"] ARG source=. WORKDIR /app EXPOSE 80 COPY $source .
ビルドしたバイナリの転送とか、リモートデバッグの仕組みはほかにありそうです。docker-compose.dev.debug.ymlを見てみましょう。
version: '2' services: webapplication1: build: args: source: obj/Docker/empty/ labels: - "com.microsoft.visualstudio.targetoperatingsystem=linux" environment: - ASPNETCORE_ENVIRONMENT=Development - DOTNET_USE_POLLING_FILE_WATCHER=1 volumes: - .:/app - ~/.nuget/packages:/root/.nuget/packages:ro - ~/clrdbg:/clrdbg:ro entrypoint: tail -f /dev/null
環境変数の設定や、volumeのマウントが見えます。アプリケーションバイナリと、NuGetのバイナリは別にマウントしているんですね。clrdbgもマウントしていることから、やはりclrdbgを利用してリモートデバッグしているようです。
それでは、実際にリモートデバッグしたときの出力を見てみましょう。
1>------ ビルド開始: プロジェクト:WebApplication1, 構成:Debug Any CPU ------
1> docker ps --filter "name=webapplication1_webapplication1" --format {{.ID}} -n 1
1> b72591744860
1> docker exec -i b72591744860 /bin/bash -c "if PID=$(pidof -x dotnet); then kill $PID; fi"
1> C:\Program Files\dotnet\dotnet.exe build "d:\documents\visual studio 2015\Projects\WebApplication1" --configuration Debug --no-dependencies
1> Project WebApplication1 (.NETCoreApp,Version=v1.0) was previously compiled. Skipping compilation.
1> docker-compose -f "d:\documents\visual studio 2015\Projects\WebApplication1\docker-compose.yml" -f "d:\documents\visual studio 2015\Projects\WebApplication1\docker-compose.dev.debug.yml" -p "webapplication1" up -d
1> webapplication1_webapplication1_1 is up-to-date
1> docker ps --filter "name=webapplication1_webapplication1" --format {{.ID}} -n 1
1> b72591744860
1> docker exec b72591744860 /bin/bash -c "files=(/app/*); if [ ${#files[@]} -gt 1 ]; then echo true; fi"
1> true
1> docker exec b72591744860 /bin/bash -c "files=(/root/.nuget/packages/*); if [ ${#files[@]} -gt 1 ]; then echo true; fi"
1> true
1> docker exec b72591744860 /bin/bash -c "files=(/clrdbg/*); if [ ${#files[@]} -gt 1 ]; then echo true; fi"
1> true
1> docker exec -i b72591744860 /bin/bash -c "if PID=$(pidof -x dotnet); then kill $PID; fi"
========== ビルド: 1 正常終了、0 失敗、0 更新不要、0 スキップ ==========
実行中のコンテナを見つけて、内部のdotnetプロセスがあれば殺して、ビルドして、docker compose をupdateしています。実際に、~に対応する、Windowsのユーザーのホームディレクトリ%USERPROFILE%の下のclrdbgフォルダを見ると、.soファイルといったデバッグのためのバイナリが用意されています。
次に、Dockerの出力を見るとこのようになっています。ポートを見つけて、ブラウザで該当ページを開いています。
docker ps --filter "name=webapplication1_webapplication1" --format {{.ID}} -n 1
b72591744860
docker inspect --format="{{json .NetworkSettings.Ports}}" b72591744860
{"80/tcp":[{"HostIp":"0.0.0.0","HostPort":"32770"}]}
Waiting for response from http://localhost:32770/ ...
Launching http://localhost:32770/ ...
リモートデバッグするためのコマンドが表示されていないのですが、これはVisual Studio Tools for Docker がVisual Studio拡張内部で実行しているのではと推測しています。それを確認する方法ですが、タスクスケジューラーを開きながらリモートデバッグが開始すると、docker.exeプロセスが新規に実行されることがわかります。これの詳細を見てみましょう。
PS> Get-WmiObject Win32_Process -Filter "name='docker.exe'" Caption : docker.exe CommandLine : "docker" exec -i b72591744860 /clrdbg/clrdbg --interpreter=mi
関係あるところだけ抜き出しましたが、docker exec でclrdbgを実行していました。この仕掛けは、以前.NET Conf Japanでデモをした Visual Studio からリモートLinuxで実行する.NET Coreプロセスをデバッグ実行する、と同様のものです。
なので、この拡張をさらに拡張すれば、OpenShiftといった任意のコンテナプラットフォームに対してリモートデバッグできるのではと推測している今日このごろです。
*1:OSS Loveを謳うMicrosoftなのでいずれこのVS拡張は公開されて、コードを見ればわかる気もしますが