大晦日ですし、2016年を振り返ってみたいと思います。
- 仕事
- MVP活動
- 登壇
- 執筆
- Global Summit
- テクノロジ関連
- C#
- Azure
- PowerShell on Linux や SQL Server on Linux
- その他
- プライベートなど
- ダイエット
- 野球
- 最後に
大晦日ですし、2016年を振り返ってみたいと思います。
NOTES: clrdbgはvsdbgに置き換えられました。次の記事を参照してください。
Visual Studio Code Advent Calendarの18日目です。
今回の記事は先日のこの記事のVisual Studio Code版になります。
リモートデバッグでつなぐ先には、同様にMIEngineをセットアップしておき、ほぼ同じ仕組みで接続し、接続元がVisual Studio Codeになるところだけが異なります。また、Visual Studio Codeが動いているOSはWindowsだけでなくLinuxでも動きます。Macも動くはずですが確認はしていません。
これも同じになります。
今回も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の確認を聞かれますが、リモートデバッグでは必須ではないのでどちらもお好きな方で。
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のデバッグメニューに追加されるので、追加した名前を選択してデバッグ実行すると、リモートデバッグが開始されます。
Visual Studioの時と同様、コンテナ名とかPIDとかの書き換えが面倒なのでその辺りを自動化できるプラグインでも作れるといい感じに使えそうです。
ASP.NET Core Advent Calendar 13日目です。
初日に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.Session
はMicrosoft.Extensions.Caching.Abstractions
に依存していることもパッケージからわかります。
開発用途限定ですが、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に格納するものになります。
よし、これで複数サーバーでSticky Sessionの設定をせずともセッションを共有できる... という訳には実はいきません。そのことは実はだいぶ前のエントリで書いています。
この記事に書いている通り、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向けのものも用意されています。
といったところで長くなってきたので、実際に動くコードで検証するのは続編でかきたいと思います。