しばやん雑記

Azure とメイドさんが大好きなフリーランスのプログラマーのブログ

Azure App Service の .NET 5 Early Access を一通り試してみた

.NET Conf 2020 で .NET 5 が正式リリースされましたが、同じタイミングで App Service での .NET 5 Early Access 機能が発表され、多少の制約はありますがリリースとほぼ同時に利用可能になりました。

App Service Teams のブログと GitHub にドキュメントが用意されています。これまでは App Service のイメージ更新を待つ必要がありましたが、Early Access 機能により即座に利用可能です。*1

制約としては App Service Plan の初期化時に必要なランタイムをインストールするので、コールドスタートと Kudu を利用したビルド時に影響が出ると書かれています。ビルド時に影響が出る理由は、.NET Core をインストール後初めて実行すると初期化が走るからでしょう。

実際に Windows の App Service に対して .NET 5 を使えるようにしてみましたが、特殊な場所にインストールされるといった挙動は無く、これまでのようにイメージ更新された時と同じように使えました。

System Drive 以下には JitInstallMarker というディレクトリが作成されて、JIT インストールしたコンポーネントのマーカーファイルが保存されていました。MSBuild 16.8 も同時に入るようです。

実行時にランタイムをインストールするのでコールドスタートへの影響は絶対に避けられませんが、App Service Plan の初期化時に 1 回だけなので、発生するのは Tier の変更、スケールアウト、インスタンスのリタイア時ぐらいでしょう。個人的には許容範囲内かと考えています。

ちなみに Linux の場合は .NET 5 用の Docker Image が App Service にキャッシュされていないだけなので、初回に Docker Image の pull コストがかかります。その後は App Service Plan が同じの間キャッシュが効くはずなので、Windows よりもオーバーヘッドが小さい可能性がありそうです。

ここから先は .NET 5 Early Access を利用する App Service をデプロイする各種方法について、実際に試した結果を載せています。Visual Studio では作成できないので、Azure Portal や Azure CLI を使いましょう。

Azure Portal を使ってデプロイする

Azure Portal から App Service を作成すると、Runtime Stack の .NET グループに .NET 5 が出てくるので、これを選択してデプロイすれば有効になります。紹介の必要がないぐらい非常に簡単でした。

Windows と Linux が選べますが、両方の OS で .NET 5 の Early Access が選べるようになっています。

既存の App Service を .NET 5 に変更する

これからは .NET Core 3.1 から .NET 5 へのアップグレードが多くなると思いますが、もちろん問題なく移行可能でした。App Service 上は前述したように Runtime Stack としては .NET Core ではなく .NET 扱いになっているので少し戸惑いました。

Windows の App Service の場合は以下のように Stack として .NET を選べば選択肢に出てきます。

Linux でも同様ですが Windows とは異なり Major / Minor Version まで選べるようになっています。今のところは 1 つしかありませんが、今後選択肢が増える可能性が高そうです。

この辺りは Java に合わせてあるように感じました。自動アップデートの設定も追加されるでしょう。

Azure CLI を使ってデプロイする

App Service の利用可能な Runtime Stack の一覧を表示する az webapp list-runtimes を実行すると、明らかにそれっぽい DOTNET|5.0 という Runtime Stack が見つかりました。

この Runtime Stack は Windows と Linux 共通だったので、以下のコマンドで Windows と Linux の両方に対応できます。違いは App Service Plan がどちらの OS 向けに作られているかに依存します。

az webapp create -g rg-net5-example -p plan-net5-example -n app-net5-example --runtime "DOTNET|5.0"

Azure Portal を使っていると Runtime Stack の指定方法にあまり馴染みがありませんが、基本は az webapp list-runtimes を実行して調べるのが一番早いです。

ARM Template を使ってデプロイする

Azure Portal と Azure CLI でデプロイできるということは、ARM REST API や ARM Template でも問題なくデプロイできるということです。特に Azure Portal はリソース作成時に ARM Template をダウンロードできるので、簡単にパラメータを調べることが出来ます。

そうして違いを確認してみると Windows の App Service の場合は netFrameworkVersionv5.0 を設定することで、.NET 5 の Early Access が利用可能になるようでした。

同様に Linux の App Service に対しても確認してみると、こちらは linuxFxVersionDOTNETCORE|5.0 を設定することで、.NET 5 の Early Access が利用可能になるようでした。

最悪という感想しか出ませんが、Azure CLI での Runtime Stack 指定とは若干異なるので注意が必要です。

今後変更される可能性はありそうです。特に Linux の App Service はこういうのが多い印象があります。

Terraform を使ってデプロイする

最後は Terraform を使ったデプロイを試しましたが、現時点の Terraform Provider for Azure v2.35.0 では Windows の .NET 5 Early Access のデプロイはエラーになります。

単純に dotnet_framework_version のバリデーションが原因なので Issue を上げたところ、あっという間に対応されました。次の v2.36.0 から利用可能になるはずです。

とりあえず自前で Provider をビルドして動作を確認しておきました。Windows と Linux で .NET 5 の Early Access を利用するための Terraform の定義は、以下のように非常にシンプルです。

resource "azurerm_app_service" "windows" {
  name                = "app-net5-win"
  location            = azurerm_resource_group.windows.location
  resource_group_name = azurerm_resource_group.windows.name
  app_service_plan_id = azurerm_app_service_plan.windows.id

  site_config {
    dotnet_framework_version = "v5.0"
  }
}

resource "azurerm_app_service" "linux" {
  name                = "app-net5-linux"
  location            = azurerm_resource_group.linux.location
  resource_group_name = azurerm_resource_group.linux.name
  app_service_plan_id = azurerm_app_service_plan.linux.id

  site_config {
    linux_fx_version = "DOTNETCORE|5.0"
  }
}

こうしてみると DOTNETCORE|5.0DOTNET|5.0 に統一してほしさしかありません。作成した App Service 全てに ASP.NET Core 5 のアプリケーションを Zip Deploy してみましたが、全て問題なく動作しました。

JIT インストール結果は App Service Plan で共有

Linux の場合は Docker が使われているので、特に Early Access だからと言ってキャッシュの有無以外に違いはありませんが、Windows の場合は System Drive にインストールされるので挙動が少し異なります。

具体的には JIT インストールされたコンポーネントは App Service Plan を共有する App Service 全てに反映されるということです。以下は .NET 5 を使わない設定にしている App Service ですが、App Service Plan を共有しているので .NET 5 SDK がインストールされています。

GitHub Actions や Azure Pipelines でビルドしたアプリケーションをデプロイする場合では特に影響はありませんが、Kudu を使ってビルドする場合には予期せぬ SDK バージョンが使われることが出てきそうです。

その場合は global.json を使って必要な .NET Core SDK バージョンを指定する必要が出てくるでしょう。

*1:今回の JIT インストール機能は、以前からちょいちょい Java で使われていた気がする