🐥note.

小鳥とMicrosoft <3 なエンジニアの技術Blog📚

Server side BlazorをAzure Web AppsにDeployする

以前Azure Blob Storage上に静的サイトとしてClient side Blazorをデプロイする記事を書きました。

blog.piyosi.com

Azure CLIに慣れる目的で、今回はServer side BlazorをAzure上にデプロイしたいと思います。
使うサービスはAzureのWeb Apps, Key Vault, SignalR Serviceです。

目次

Azureの設定手順

プロビジョニングはWindows環境のAzure CLI(ver2.0.78)を使用します。

AzureへLogin

ターミナルを起動しaz loginを実行しAzureにログインします。

ResourceGroupの設定

Resource Groupを作成します。
ロケーションは東日本のjapaneastを指定します。

< >で括られた値は任意の値に変更してください。

az group create --name <GroupName> --location japaneast

備考:Locationの調べ方

下記コマンドで調べることができます。

az account list-locations --output table

App Serviceの設定

Web Appsを使用するためにApp Serviceを作成します。

こちらの記事に記載した通りAzure CLIWindowsのApp Serviceを作成すると.NET CoreのRuntimeが選べません。仕方がないので今回はLinuxを選択します。
※Azure Portalで手動で作る場合はWindowsでも.NET Coreが選べるのに…。

SKUはとりあえずFREEで。

az appservice plan create  --resource-group <GroupName> --name <AppServicePlanName> --location japaneast --sku FREE --is-linux

Web Appsの設定

Web Appsを作成します。

Deploy用のリポジトリなどを用意する場合は適宜--deployment-source-urlなどのオプションを付与しましょう。

今回はVisual Studio Code拡張機能Azure App Serviceを使用してDeployするのでオプションは指定しません。

az webapp create --resource-group <GroupName> --name <WebAppsName> --plan <AppServicePlanName> --runtime "DOTNETCORE|LTS"

Powershellで上記コマンドを実行すると|の部分がエラーとなります。
一時的にcmdに切り替えて実行してください。

Managed Service Identityの設定

WebAppsにADのManaged Idをアサインします。

az webapp identity assign --name <WebAppsName> --resource-group <GroupName>

登録後に下記コマンドでprincipalIdを調べます。

az webapp identity show --name <WebAppsName> --resource-group <GroupName>

以下のようなJsonが吐かれるので、principalIdをコピーしておきます。
※以下は例です。

{
  "principalId": "abcdefgh-0000-9999-1234-hogefuga",
  "tenantId": "piyopiyo-1234-5678-9012-foobarhogefuga",
  "type": "SystemAssigned",
  "userAssignedIdentities": null
}

SignalR Serviceの設定

SignalR Serviceを利用せずとも、Web Appsの設定でWeb socketsを有効化すればBlazorは動きます。

az webapp config set --web-sockets-enabled true --resource-group <GroupName> --name <WebAppsName>

しかし接続ユーザが増えてくるとパフォーマンスが悪くなる"らしい"ので、Azure SignalR Serviceを使用して左記問題を回避します。

下記コマンドでAzure SignalR Serviceのサービスを作成します。
SKUは無料のFree_F1にします。

az signalr create --resource-group <GroupName> --name <SignalRName> --sku Free_F1

SignalRのConnectionStringを調べる

Server side BlazorからSinalRに接続するために必要なConnectionStringを調べます。

az signalr key list --resource-group <GroupName> --name <SignalRName>

以下のようなJsonが吐かれるので、primaryConnectionStringをコピーしておきます。
※以下は例です。

{
  "primaryConnectionString": "Endpoint=https://xxx.service.signalr.net;AccessKey=yyyzzz1=;Version=1.0;",
  "primaryKey": "yyyzzz1=",
  "secondaryConnectionString": "Endpoint=https://yyy.service.signalr.net;AccessKey=yyyzzz2=;Version=1.0;",
  "secondaryKey": "yyyzzz2="
}

Azure Key Vaultの設定

Key Vaultを設定します。

<SecretValue>は先程調べたSignalRのConnectionStringであるprimaryConnectionStringを指定します。
ポリシーの割り当て先は先程ADに登録したWeb AppsのprincipalId--object-idで指定します。

az provider register -n Microsoft.KeyVault
az keyvault create --resource-group <GroupName> --name <VaultName> --location japaneast
az keyvault secret set --vault-name <VaultName> --name <SecretName> --value <SecretValue>
az keyvault set-policy --name <VaultName> --object-id <PrincipalId> --secret-permissions get list

以上でAzure側の設定は完了です。

Server side Blazorの作成

Server side Blazorを作成します。
SignalRやKeyVault関連のPackageを追加します。

dotnet new blazorserver -o SampleBlazor
cd SampleBlazor
dotnet add package Microsoft.Azure.SignalR
dotnet add package Microsoft.Azure.KeyVault
dotnet add package Microsoft.Azure.Services.AppAuthentication
dotnet add package Microsoft.Extensions.Configuration.AzureKeyVault

appsettings.jsonの編集

Azure KeyVaultのEndPointと、SignalRのEndPointを格納したSecret名を記載します。
※Key VaultのEndPointはaz keyvault show --resource-group <GroupName> -n <VaultName>で表示されるvaultUriがそれに該当します。

{
+ "KeyVault": {
+   "EndPoint": "Azure KeyVaultのEndPoint URL",
+   "SignalRConnectionStringSecret": "Azure KeyVaultに追加した<SecretName>"
+ },
  "Logging": {
    "LogLevel": {
      "Default": "Information",
      "Microsoft": "Warning",
      "Microsoft.Hosting.Lifetime": "Information"
    }
  },
  "AllowedHosts": "*"
}

ConfigurationBuilderにKeyVaultを追加

Program.csCreateHostBuilderでAzure KeyVaultのデータを取得し、builderに組込みます。

+ public class KeyVaultSetting
+ {
+     public string EndPoint { get; set; }
+     public string SignalRConnectionStringSecret { get; set; }
+ }

public static IHostBuilder CreateHostBuilder(string[] args) =>
            Host.CreateDefaultBuilder(args)
+               .ConfigureAppConfiguration((ctx, builder) =>
+               {
+                   var config = builder.Build();
+                   var keyVaultSetting = new KeyVaultSetting();
+                   config.GetSection("KeyVault").Bind(keyVaultSetting);
+                   if (!string.IsNullOrEmpty(keyVaultSetting.EndPoint))
+                   {
+                       var azureServiceTokenProvider = 
+                               new AzureServiceTokenProvider();
+                       var keyVaultClient = new KeyVaultClient(
+                           new KeyVaultClient.AuthenticationCallback(
+                             azureServiceTokenProvider.KeyVaultTokenCallback));
+                       builder.AddAzureKeyVault(keyVaultSetting.EndPoint,
+                                                 keyVaultClient,
+                                                 new DefaultKeyVaultSecretManager());
+                   }
+               })
                .ConfigureWebHostDefaults(webBuilder =>
                {
                    webBuilder.UseStartup<Startup>();
                });

SignalRのServiceの設定

Startup.csを編集し、SignalRのサービスを追加します。

public void ConfigureServices(IServiceCollection services)
{
  services.AddRazorPages();
  services.AddServerSideBlazor();
  services.AddSingleton<IConfiguration>(Configuration);
  services.AddSingleton<WeatherForecastService>();

+ var keyVaultSetting = new KeyVaultSetting();
+ Configuration.GetSection("KeyVault").Bind(keyVaultSetting);
+ var signalREndPoint = Configuration.GetSection(keyVaultSetting.SignalRConnectionStringSecret).Value;
+ services.AddSignalR().AddAzureSignalR(cfg =>
+ {
+   cfg.ConnectionString = signalREndPoint;
+ });
}

余談:Azure Key Vaultを使用しない場合

とりあえずで動かしたいときはappsettings.jsonにSignalR用の設定を記述しConfigureServicesservices.AddSignalR().AddAzureSignalR();と追記するだけで動きます。

{
+ "Azure":{
+   "SignalR":{
+     "Enabled": true,
+     "ConnectionString":"Endpoint=https://xxx.service.signalr.net;AccessKey=hogefugapiyofoobar___yWtbjObiDBZwunDFH4MpNk=;Version=1.0;"
+   }
+ },
  "Logging": {
    "LogLevel": {
      "Default": "Information",
      "Microsoft": "Warning",
      "Microsoft.Hosting.Lifetime": "Information"
    }
  },
  "AllowedHosts": "*"
}

RuntimeIdentifierの設定

App Serviceで--is-linuxを指定したため、Web Appsの中身はLinuxです。
よって、.csprojRuntimeIdentifierの定義を追加します。

<Project Sdk="Microsoft.NET.Sdk.Web">

  <PropertyGroup>
    <TargetFramework>netcoreapp3.1</TargetFramework>
+   <RuntimeIdentifier>linux-x64</RuntimeIdentifier>
  </PropertyGroup>

  <ItemGroup>
    <PackageReference Include="Microsoft.Azure.Cosmos.Table" Version="1.0.6" />
    <PackageReference Include="Microsoft.Azure.SignalR" Version="1.2.3" />
    <PackageReference Include="Microsoft.Extensions.Options.ConfigurationExtensions" Version="3.1.0" />
  </ItemGroup>

</Project>

以上です。

Web AppsへのDeploy

任意の方法でDeployします。
今回はVisual Studio Code拡張機能Azure App Serviceを使用したいと思います。

f:id:piyo_esq:20191230180556p:plain
Visual Studio Code Extension : Azure App Service

Azure App Service

F1を押下しAzure App Service: Deploy to Web App...を選択します。

Deployするコードがあるフォルダを選択し、Deploy先のWeb Appsを選択します。

この時点で設定ファイルが作成され、以下のダイアログが表示されます。

f:id:piyo_esq:20191230180621p:plain
Azure App Service Dialog

ここでいったんキャンセルを押下します。

Publishするフォルダは先程作成されたsetting.jsonファイルに記載されています。
デフォルトだとsrc/bin/Release/netcoreapp3.1/publishになっているのでパスを変更します。

{
    "appService.preDeployTask": "publish-release",
-   "appService.deploySubpath": "src/bin/Release/netcoreapp3.1/publish"
+   "appService.deploySubpath": "src/bin/Release/netcoreapp3.1/linux-x64/publish"
}

準備完了です。

F1を押下しAzure App Service: Deploy to Web App...を選択しDeployします。

おわり

  • 起動時のKey Vaultへのアクセスが結構遅いのが辛い

  • 開発者ツールのログにSignalRのURLが表示されるので気になる人はLogの出力を抑制した方がよいかも

  • Azure CLIからWindows OSの.NET Core Stackを使用する設定ができないのはAzure CLIのバグなんでしょうか…?Azure初心者なのでよくわかりません…