App Service の CI/CDでビルドエラー

ちょっとはまったのでメモ。(そのうち改善されると思われる)

はまった環境: Visual Studio 2017 で .NET Framework 4.6.2 なクラスライブラリでNuGetなパッケージを使用しているC#プロジェクト(NuGetはPackageReferenceを参照する)と、それを参照するASP.NETなアプリ(1つのソリューションにASP.NETとクラスライブラリなプロジェクトがある状態)
はまった理由:  App Service上(Kudu上)で msbuild 15.x がないから(たぶん)

普通にApp Service上でCI/CDを使って(今回はLocal Git)上記なプロジェクトをデプロイ(git push)すると以下のようにNuGetで取得するパッケージに含まれるアセンブリが見つからない旨のエラーがでてビルドに失敗します。(nuget restoreやdonet restoreしてるにも関わらず)

省略
:
remote: D:\Program Files (x86)\dotnet\sdk\2.0.0-preview1-005977\Microsoft.Common.CurrentVersion.targets(1964,5): warning MSB3245: Could not resolve this reference. Could not locate the assembly "Newtonsoft.Json, Version=10.0.0.0, Culture=neutral, PublicKeyToken=30ad4fe6b2a6aeed, processorArchitecture=MSIL". Check to make sure the assembly exists on disk. If this reference is required by your code, you may get compilation errors. [D:\home\site\repository\web1\Shared\Shared.csproj]
:
以下略

カスタムデプロイスクリプトを使用するようにして、dotnet build や dotnet msbuild を使ってもダメです。
※Kudu上に現状Previewなmsbuildがありますがそちらを使ってもダメ
※ちなみにプロジェクトがPackage.configを参照するようになってたら問題なくビルドできます。PackageReferenceの場合のみおかしい。

回避策: クラスライブラリなプロジェクトファイル(.csproj)を新しい形式に変換する

変換は以下のURLを参考に。

最低限Project要素の属性を新しくして TargetFrameworkVersion を TargetFrameworkにしてv.4.6.2 とかを net462 とかにします。あとは不要な要素はバスバスけして、AssemblyInfo.csも消したりしました。
修正後、Visual Studio 2017上でちゃんとプロジェクトがロードできてビルドできればひとまず大丈夫かと思います。(Minなcsprojにしてから後でNuGetパッケージ追加したりもろもろいじってもよいかと思います。

変換後は dotnet build や dotnet publish で問題なくビルドできます。(つら)

過渡期な問題だと思いますが、もし嵌った人がいれば参考にしてもらえると。

ノートPCでも外付けGPUを使いたい

今使ってるノートPCでGPU使いたいなーとふと思いまして、外付けGPUユニットを買いました。
ノートPCのスペック的には外付けGPUもいけるだろうと思ってThunderbolt 3 (PCI Express Gen3 x 2レーン)で40Gbps双方向サポートされてる機種です。(DELLのXPS 13

外付けGPUを載せる筐体としてはPowerColor DEVIL BOXを選んでみました。(納期等の都合)
で、つないでみたのですがGPUを認識しないんですよね。TB3の設定みると
image
外付けGPUサポート:いいえ とか言われる始末。ファームが更新されたりドライバが更新されるのを心待ちしてたわけですが、親切なてんきちゃんに教えてもらったところ外付けの筐体側のファームを更新してみよう(人柱ヨロ)ということだったのでそのようにしてみました。

いろいろ見ると

As of 23-April-2017, TI83 (red) marked enclosures deliver only half their TB3-specced host-to-device bandwidth and are awaiting a firmware fix.

とか書いており、そのままじゃダメそうです。ただ

Allows eGPU detection in Windows for systems reporting “external GPUs supported: no” in the Intel Thunderbolt software.

とあるのでリンクにあるファーム更新ツールで強制的にファームを変えてみることにしました。※接続してファーム更新後OS再起動
TBソフトウェア上は外付けGPUサポートはいいえのままですが、ちゃんとGPU認識しました!やったね。

というわけでノートPCでも無事GPUが使えるようになりましたとさ。

※ GPUの取り外し時の確認ウィンドウがちゃんと描画されなかったりするし、稼働アプリがある状態で切断するとアプリ落ちるしその後の挙動が怪しい気もする(ノートPCのディスプレイの解像度がちゃんとならなかったり)ので、気軽に接続・切断はしにくくなった気もしますが概ね満足です。

Azure Functions で実行中にコンパイルするとどうなるの

裏でFunction Appが実行中のときにコンパイルして(コンパイルエラーになったりして)るときに実行中のものはどうなるのか、予想では別物なんで問題ないと思いますが一応。

検証用コード

using System.Net;

public static async Task<HttpResponseMessage> Run(HttpRequestMessage req, TraceWriter log)
{
    log.Info($"Start ----");
    // parse query parameter
    string name = req.GetQueryNameValuePairs()
        .FirstOrDefault(q => string.Compare(q.Key, "name", true) == 0)
        .Value;

    await Task.Delay(TimeSpan.FromSeconds(20));

    // Get request body
    dynamic data = await req.Content.ReadAsAsync<object>();

    // Set name to query string or body data
    name = name ?? data?.name;

   log.Info($"End ----------");
   return name == null
        ? req.CreateResponse(HttpStatusCode.BadRequest, "Please pass a name on the query string or in the request body")
        : req.CreateResponse(HttpStatusCode.OK, "Hello " + name);
}

HttpTriggerなC#のテンプレートにStartとEndのログと間にTask.Delayで処理止めてるだけのシンプルなやつです。
※ログにFunction Startedとか出るので無駄な気もするけど一応

手順的にはこんな感じ

1.  上記コードを実行する(適当に呼び出す)
2. Task.Delayしてる間にコードを適当に弄ってSaveしてコンパイルエラーにする
3. 結果を見る
4. そのまま再度呼び出す

実行結果はこんな感じになりました

2016-07-19T03:34:42.334 Function started (Id=14c91f2c-2490-437c-b461-258a13872d82)
2016-07-19T03:34:42.334 Start ----
2016-07-19T03:34:45.698 Script for function 'HttpTriggerCSharp1' changed. Reloading.
2016-07-19T03:34:45.698 Compiling function script.
2016-07-19T03:34:45.776 run.csx(14,13): error CS1002: ; expected
2016-07-19T03:34:45.776 run.csx(11,48): error CS0246: The type or namespace name 'aaaa' could not be found (are you missing a using directive or an assembly reference?)
2016-07-19T03:34:45.776 run.csx(14,13): error CS0103: The name 'data' does not exist in the current context
2016-07-19T03:34:45.776 run.csx(17,20): error CS0103: The name 'data' does not exist in the current context
2016-07-19T03:34:45.776 run.csx(14,5): warning CS0168: The variable 'dynamic' is declared but never used
2016-07-19T03:34:45.776 Compilation failed.
2016-07-19T03:35:02.334 End ----------
2016-07-19T03:35:02.334 Function completed (Success, Id=14c91f2c-2490-437c-b461-258a13872d82)
2016-07-19T03:35:27.915 Function compilation error
2016-07-19T03:35:27.915 run.csx(14,13): error CS1002: ; expected
2016-07-19T03:35:27.915 run.csx(11,48): error CS0246: The type or namespace name 'aaaa' could not be found (are you missing a using directive or an assembly reference?)
2016-07-19T03:35:27.915 run.csx(14,13): error CS0103: The name 'data' does not exist in the current context
2016-07-19T03:35:27.915 run.csx(17,20): error CS0103: The name 'data' does not exist in the current context
2016-07-19T03:35:27.915 run.csx(14,5): warning CS0168: The variable 'dynamic' is declared but never used
2016-07-19T03:35:27.915 Function completed (Failure)
2016-07-19T03:35:27.961 Exception while executing function: Functions.HttpTriggerCSharp1. Microsoft.Azure.WebJobs.Script: Script compilation failed.

StartとEndの間でコンパイル走ってエラーになってますが、Function Id=14c91f2c-2490-437c-b461-258a13872d82は継続して処理が完了してます。

その後再度呼び出してみるとコンパイルが完了してないので再度コンパイル→失敗→500エラーが返る、という感じです。

予想通りでしたね。なんかおかしい、という場合はぜひ追加の検証をお願いします。

Azure Management REST API用のBearerトークン

よく忘れるので。

$adal = "${env:ProgramFiles(x86)}\Microsoft SDKs\Azure\PowerShell\ServiceManagement\Azure\Services\Microsoft.IdentityModel.Clients.ActiveDirectory.dll"
$adalforms = "${env:ProgramFiles(x86)}\Microsoft SDKs\Azure\PowerShell\ServiceManagement\Azure\Services\Microsoft.IdentityModel.Clients.ActiveDirectory.WindowsForms.dll"
[System.Reflection.Assembly]::LoadFrom($adal)
[System.Reflection.Assembly]::LoadFrom($adalforms)
$adTenant = "<あなたのテナント>.onmicrosoft.com"
# well-known ID
$clientId = "1950a258-227b-4e31-a9cf-717495945fc2"
$redirectUri = "urn:ietf:wg:oauth:2.0:oob"
$resourceAppIdURI = "https://management.core.windows.net/"
$authority = "https://login.windows.net/$adTenant"
$authContext = New-Object "Microsoft.IdentityModel.Clients.ActiveDirectory.AuthenticationContext" -ArgumentList $authority
$authResult = $authContext.AcquireToken($resourceAppIdURI, $clientId, $redirectUri, "Auto")
$authHeader = $authResult.CreateAuthorizationHeader()
# Write-Host "Bearer Token: $authHeader"
$authHeader | Out-File bearerToken.txt

これでBearerトークン出来上がり。認証はいつもの認証ダイアログ的なのが出るので安心です。
(要Azure PowerShell)

参考:Setting up PostMan to call the Azure Management APIs

Azure関連でMicrosoftのサポートを受けるには

これがソーシャルハラスメント!

とにかく、Azureのサポートに連絡してサポートを受ける方法です(日本語難しい)

続きを読む