銀の光と碧い空

クラウドなインフラとC#なアプリ開発の狭間にいるエンジニアの日々

Visual Studio Code からdocker 上で走るASP.NET Core アプリにリモートデバッグする

NOTES: clrdbgはvsdbgに置き換えられました。次の記事を参照してください。

tech.tanaka733.net

Visual Studio Code Advent Calendarの18日目です。

qiita.com

今回の記事は先日のこの記事のVisual Studio Code版になります。

tech.tanaka733.net

リモートデバッグでつなぐ先には、同様にMIEngineをセットアップしておき、ほぼ同じ仕組みで接続し、接続元がVisual Studio Codeになるところだけが異なります。また、Visual Studio Codeが動いているOSはWindowsだけでなくLinuxでも動きます。Macも動くはずですが確認はしていません。

前提条件

これも同じになります。

  • docker exec コマンドなどでVS Codeを動かしているマシンからリモートでコンテナ上のコマンドを実行できる
  • docker上で動いているASP.NET Core のバイナリはデバッグビルドである

今回もOpenShift Container Platform 上でテストしています。

手順

前提条件を満たしたdockerが動ている前提からスタートします。ビルドしたコードをgit cloneなどで持って来てもいいですが、OpenShiftで動ている場合、oc rsyncコマンドでソースコード一式をコンテナとローカルのファイルを同期できます。これをVisual Studioの時に紹介しなかったのは、Windowsだとrsyncが使えないとか、パス長制限にひっかかるといった理由でうまくできないことがおおいからです…

$ POD_NAME=<pod_name>
$oc rsync $POD_NAME:. .

プロジェクトをVS Codeで開くとアセットのダウンロードとNuGet restoreの確認を聞かれますが、リモートデバッグでは必須ではないのでどちらもお好きな方で。

f:id:tanaka733:20161212134856p:plain

docker内にclrdbgをインストールします。ここの手順も同じです。

$ oc exec $POD_NAME -- curl -sSL https://raw.githubusercontent.com/Microsoft/MIEngine/getclrdbg-release/scripts/GetClrDbg.sh -o GetClrDbg.sh
$ oc exec $POD_NAME -- bash ./GetClrDbg.sh latest ./clrdbg
Info: Using clrdbg version '15.0.25904-preview-3404276'
Info: Creating install directory
Info: Determinig Runtime ID
Info: Using Runtime ID 'rhel.7.2-x64'
Info: Generating project.json
Info: Generating NuGet.config
Info: Executing dotnet restore
Info: Executing dotnet publish
Successfully installed clrdbg at './clrdbg'

同様に、プロセスにアタッチするので、コンテナ上で動いているASP.NET Core のPIDを確認します。OpenShiftの場合はこんな結果になりますが、PIDは62になります。

$ oc exec $POD_NAME -- ps aux 
USER        PID %CPU %MEM    VSZ   RSS TTY      STAT START   TIME COMMAND
default       1  0.5  0.4 3048552 37016 ?       SLsl 22:08   0:05 dotnet run
default      62  1.0  1.9 12134016 153332 ?     SLl  22:08   0:09 /opt/rh/rh-dotnetcore10/root/usr/lib64/dotnetcore/dotnet exec --additionalprobingpath /opt/app-root/src/.nuget/packages /opt/app-root/src/bin/Debug/netcoreapp1.0/src.dll
default     381  0.0  0.0  13352  1868 ?        Ss+  22:16   0:00 /bin/sh
default     676  0.0  0.0  48996  1692 ?        Rs   22:23   0:00 ps aux

ここからの設定ファイルの書式とデバッグのやり方が違ってきます。プロジェクトのルート直下に.vscode/launch.jsonというファイルを作成し、次のように記述します。既に存在している場合は、configurations要素の中に追加してください。

{
    "version": "0.2.0",
    "configurations": [
        {
            "name": ".NET Core Docker Remote Attach",
            "type": "coreclr",
            "request": "attach",
            "processId": "<PID>",
            "pipeTransport": {
                "pipeProgram": "oc",
                "pipeArgs": [ "exec", "-it", "<POD_NAME>", "--"],
                "debuggerPath": "/opt/app-root/src/clrdbg/clrdbg",
                "pipeCwd": "${workspaceRoot}"
            },
            "sourceFileMap": {
                "/opt/app-root/src": "${workspaceRoot}"
            }
        }
    ]
}

docker コマンドで接続する場合は--が不要です。この当たりはVisual Studio Codeの時と同じです。

        "pipeProgram": "docker",
        "pipeArgs": [ "exec", "-it", "<container_name>"],

あとは、Visual Studio Codeのデバッグメニューに追加されるので、追加した名前を選択してデバッグ実行すると、リモートデバッグが開始されます。

f:id:tanaka733:20161212134905p:plain

Visual Studioの時と同様、コンテナ名とかPIDとかの書き換えが面倒なのでその辺りを自動化できるプラグインでも作れるといい感じに使えそうです。

ASP.NET Core で複数Webサーバーでセッションを共有するときは、IDistributedCacheとIDataProtectionに注意しないといけない話

ASP.NET Core Advent Calendar 13日目です。

qiita.com

初日にSignalRでエントリ書きましたが、もともと予定していたセッションについて簡単に追いかけてみましょう。ASP.NET Core でSessionを使う場合、まずはこのドキュメントを読むのがよいでしょう。

Managing Application State | Microsoft Docs

で、Sessionを使うにはまずMicrosoft.AspNetCore.Sessionパッケージを追加しろとあります。その後のNoteに書いてあることを無視して、とりあえずこのパッケージだけ追加して、Startup.csを次のように書いてみましょう。

public void ConfigureServices(IServiceCollection services)
{
    //services.AddDistributedMemoryCache();
    services.AddSession();
}

// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
{
    app.UseSession();
}

これで実行するとこんなエラーが出ます。

Unhandled Exception: System.InvalidOperationException: Unable to resolve service for type 'Microsoft.Extensions.Caching.Distributed.IDistributedCache' while attempting to activate 'Microsoft.AspNetCore.Session.DistributedSessionStore'.
   at Microsoft.Extensions.DependencyInjection.ServiceLookup.Service.PopulateCallSites(ServiceProvider provider, ISet`1 callSiteChain, ParameterInfo[] parameters, Boolean throwIfCallSiteNotFound)
   at Microsoft.Extensions.DependencyInjection.ServiceLookup.Service.CreateCallSite(ServiceProvider provider, ISet`1 callSiteChain)
   at Microsoft.Extensions.DependencyInjection.ServiceProvider.GetResolveCallSite(IService service, ISet`1 callSiteChain)

DIでIDistributedCacheを実装したサービスが見つからないということでエラーが出ます。無視したNoteにある通り、Sessionの機能はIDistributedCacheに依存して実装しているので少なくとも1つはその実装を追加しないといけません。実際に、Microsoft.AspNetCore.SessionMicrosoft.Extensions.Caching.Abstractionsに依存していることもパッケージからわかります。

www.nuget.org

開発用途限定ですが、IDistributedCacheを実装するパッケージがMicrosoft.Extensions.Caching.Memoryです。これは特に設定の必要もなく、次のようにコード一行追加するだけで動きます。

public void ConfigureServices(IServiceCollection services)
{
    services.AddDistributedMemoryCache();
    services.AddSession();
}

// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
{
    app.UseSession();
}

この開発用途限定と言っているパッケージ、名前の通りインメモリーにキャッシュを保存します。注意しないといけないのは複数サーバーでスケールアウトする場合で、当然各サーバーで別々にセッションをメモリーに保存するので、別のサーバーに行くとその前にアクセスしたセッションにはアクセスできません。というわけで本番環境では、IDistributedCacheを実装しているサービスを用意する必要があります。ASP.NET Coreチームから提供されているのはSQLServerとRedisに格納するものになります。

github.com

www.nuget.org

www.nuget.org

よし、これで複数サーバーでSticky Sessionの設定をせずともセッションを共有できる... という訳には実はいきません。そのことは実はだいぶ前のエントリで書いています。

tech.tanaka733.net

この記事に書いている通り、ASP.NET Coreではセッションデータを格納する際に暗号化しています。これはIDataProtectionというインターフェースを実装するサービスがその役割をになっており、デフォルトはマシンごとに固有の鍵をローカルファイルシステムにファイルとして保存します。さらにこのインターフェースはIDistributedCacheとは独立しているので、Redis Cachingを使ったとしてもIDataProtectionはデフォルト実装のままです。デフォルト実装の際に保存されるファイルパスはこのコードにあります。

DataProtection/FileSystemXmlRepository.cs at rel/1.1.0 · aspnet/DataProtection · GitHub

実際、このようなファイルが保存されています。

.aspnet/DataProtection-Keys/key-c28289ea-0e92-4e6b-94ff-6985ba6700ac.xml
$ cat .aspnet/DataProtection-Keys/key-c28289ea-0e92-4e6b-94ff-6985ba6700ac.xml 
<?xml version="1.0" encoding="utf-8"?>
<key id="c28289ea-0e92-4e6b-94ff-6985ba6700ac" version="1">
  <creationDate>2016-11-30T12:24:55.245682Z</creationDate>
  <activationDate>2016-11-30T12:24:55.220079Z</activationDate>
  <expirationDate>2017-02-28T12:24:55.220079Z</expirationDate>
  <descriptor deserializerType="Microsoft.AspNetCore.DataProtection.AuthenticatedEncryption.ConfigurationModel.AuthenticatedEncryptorDescriptorDeserializer, Microsoft.AspNetCore.DataProtection, Version=1.1.0.0, Culture=neutral, PublicKeyToken=adb9793829ddae60">
    <descriptor>
      <encryption algorithm="AES_256_CBC" />
      <validation algorithm="HMACSHA256" />
      <masterKey p4:requiresEncryption="true" xmlns:p4="http://schemas.asp.net/2015/03/dataProtection">
        <!-- Warning: the key below is in an unencrypted form. -->
        <value>uT3l6pCiERM0Mwd6F8T4nOnnv9DmwSU6ii8AaZj6XG6qR0MorUN1Akxa5Lqivt8hKfu2L9TBmryC16I6BuOXrQ==</value>
      </masterKey>
    </descriptor>
  </descriptor>
</key>

というわけですので、IDataProtectionも必要に応じて共有できる実装に切り替えましょう。Data Protectionについては別のドキュメントで説明されており、デフォルトでは同じ物理パスを指定してもマシン固有になることも書かれています。

Configuring Data Protection | Microsoft Docs

上に引用した記事を書いた時点では、.NET Core on Liniuxで(比較的)容易に用意できる共有先はNFSくらいだったのですが、現在はRedis実装がPreviewですが用意されています。またAzureStorage向けのものも用意されています。

www.nuget.org

www.nuget.org

といったところで長くなってきたので、実際に動くコードで検証するのは続編でかきたいと思います。

Visual Studio から AzureやWindows以外のdocker containerで走るASP.NET Core をリモートデバッグする

空いていたので予定を早めて Visual Studio Advent Calendar 12日目のエントリを書きます。

qiita.com

Windows上のdockerやAzure Container ServicesであればVisual Studio Tools for Dockerを使えばVisual StudioからASP.NET Coreのアプリにリモートデバッグできます。Microsoft謹製のサービスしか接続できないので、なにか闇の技術でも使われているかと思われるかもしれないですが、そんなことはなくてGitHubで公開されているMIEngineというデバッグエンジンが利用されています。

github.com

この当たりの話は、Visual StudioからSSHを使えば、Linux上の.NET Coreプロセスにリモートデバッグできるというネタで何度かセッションでデモをしてきました。

というわけで、Visual StudioをインストールしているWindowsマシンからdockerにリモートでコマンドを実行できれば、他のdocker containerであってもリモートデバッグができます。今回は、OpenShift Container Platform上で動くASP.NET Coreアプリにリモート接続してみます。

続きを読む