LoginSignup
0
2

Azure Spring Apps Enterprise の Tips (C#版)

Last updated at Posted at 2023-08-20

はじめに

Azure Spring Apps には Basic, Standardという Tier もありますが、これらは.NET Core 3.1にしか対応していないため、使えるシナリオは限られています。一方 Enterprise は .NET 6, 7に対応しています(2023年8月現在)。つまり実質的に Enterprise しか使うことはないと思われます。ここでは Enterprise 版についてのみの Tips を書いていきます。

環境

  • spa-enterprise という名前の  Azure Spring Apps を構築済みです。
  • リソースグループ名は rg-springapps です。
  • Azure Spring Apps の中に dotnetapp という  Polyglot アプリを Azure Portal で作成済みです。

Azure CLI の準備

 Azure Spring Apps そのものの操作は Azure Portal から大抵のことはできますが、デプロイはAzure CLI が必要です。 Azure CLI をローカルにインストールするか Azure Portal の Clous Shell を使う必要があります。そして Azure Spring Apps の操作を Azure CLI で行うには az spring コマンドを使うのですが、このコマンドは既定ではインストールされていません。次のコマンドを使用してインストールします。

zsh
az extension add --name spring --upgrade
az provider register --namespace Microsoft.SaaS

これ以降、az spring コマンドを使う Tips を下記に記載しています。az コマンドのオプション名が長くて嫌になってしまいます。次の2つの Tips を覚えておいてください。

  1. オプション名の短縮版を使う
    • --service は -s
    • --resource-group は -g
    • --name は -n
  2. リソースグループと Azure Spring Apps のオプション値の既定値をセットしてそもそも入力しないで済むようにする
    • リソースグループの既定値をセットする(このディレクトリで叩く時だけ)
      az config set defaults.group=MyResourceGroup --local
      
    • Azure Spring Apps 名の 既定値をセットする(このディレクトリで叩く時だけ)
       az config set defaults.spring=asa-enterprise --local
      

--local をつけておくと、そのディレクトリでコマンドを叩く時だけの既定値をセットしたことになります。値は ./.azure/configに保存されます。 --localをつけないと、設定値は~/.azure/configに保存されます。

C#(.NET)のアプリをデプロイする

C#(.NET)のソースをアップロードする場合、Project ファイル(csproj ファイル)が存在するフォルダを --source-path で指定します。 Solution ファイルを Project の上位フォルダに単独で作る構成の場合、Solution ファイルだけがあるフォルダを指定してもビルドエラーになりますので注意してください。

C#(.NET)のアプリのソースをUpload&デプロイする

zsh
az spring app deploy \
--service spa-enterprise \
--resource-group rg-springapps \
--name dotnetapp \
--builder default \
--source-path ./dotnetapp

 このコマンド例では --builder オプションでビルダーをわざわざ指定していますが、何も指定しなかったら default という名前のビルダーが使用されるので、このコマンド例の場合だと本当は指定不要です。しかし規定のビルダーはJava, .NET, Python, Node.jsなど沢山のビルダーが組み込まれており、ビルドエラーになった時に原因を特定しにくいのです。できるだけ dotnet だけのビルダーを作成し、このコマンド例のように --builder オプションで指定すると良いでしょう。

.NET 7のソースがうまくビルドされて稼働することがデプロイ時のログから確認できます。

log
  Dividing build output into layers to optimize cache reuse

  Generating SBOM for /workspace
      Completed in 23ms

  Removing source code

Tanzu Buildpack for ASP.NET Core Runtime 0.0.19
  Resolving ASP.NET Core Runtime version
    Candidate version sources (in priority order):
      runtimeconfig.json -> "7.0.0"
      <unknown>          -> ""

    No exact version match found; attempting version roll-forward

    Selected ASP.NET Core Runtime version (using runtimeconfig.json): 7.0.8

  Reusing cached layer /layers/tanzu-buildpacks_dotnet-core-aspnet-runtime/dotnet-core-aspnet-runtime

Tanzu .NET Execute Buildpack 0.10.13
  Generating SBOM for /workspace
      Completed in 7ms

  Assigning launch processes:
    dotnetapp (default): /workspace/dotnetapp

C#(.NET)のアプリを Publish した結果を Zip にして Upload & Deploy する(失敗)

.NET 7 のソースを Publish した結果を次のコマンドで Upload & Deployしてみましたが、うまくいきませんでした。docs には .NET (Core)アプリは Zip ファイルのアップロードでデプロイできる記載を見つけることができますが、実際にやっているとうまく動作しません。
.NET Coreの Buildpack だけを含んだビルダーを作成して--builder で指定しても、 --main-entryオプションでエントリポイントを含むアセンブリを指定しても全く変わりません。

ログを見る限り、プロジェクトファイルが見当たらない、というメッセージがありますので、dotnet ビルドをしようとして失敗している可能性が高いと思われます。ということは、VMWare Tanzu の Buildpack に対してビルドをスキップするように指定できれば良いのですが、方法がわかりません。またカスタムの Buildpack を作って登録する機能が Azure Spring Apps Enterpriseには用意されていませんので、今のところやり方がわかりません。

zsh
az spring app deploy \
--service spa-enterprise \
--resource-group rg-springapps \
--name dotnetapp \
--builder nonjava \
--main-entry dotnetapp.dll \
--artifact-path ./dotnetapp/bin/Release/net7.0/publish.zip

C#(.NET)のアプリが複数のプロジェクトで構成されている場合にソースを Upload & Deploy する

ソースを Upload してデプロイ する場合、 az spring app deploy コマンドに --source-path オプションを使ってソースが格納されているフォルダを指定します。この時、このコマンドは --source-path オプションで指定したフォルダ配下しか Upload しません。また --source-path にプロジェクトファイルが存在していることを前提にビルドが起動します。しかし、.NET では次のような物理構成を取ることが標準でしょう。

  • SomeSolution.sln
    • Web(フォルダ)
      • Web.csproj
    • Service(フォルダ)
      • Service.csproj
    • Repository(フォルダ)
      • SomeRepo.csproj

Web が Service をプロジェクト参照し、 Service が Repository をプロジェクト参照しているとします。
このような複数プロジェクトによる構成をとっているとしても、--source-path オプションはフォルダを1つしか指定ができないため、どのフォルダを指定してもうまくビルドが動きません。

この問題の解決に随分悩まされましたが、次のページに答えが書いてありました。

Azure Spring Apps Enterprise は VMWare Tanzu の機能を取り込んでいます。ビルドは VMWare Tnazu の Buildpack を使っていますから、ビルドで困ったときは VMWare のサイトを見れば良い、ということを覚えておいてください。

ページを見てみると、環境変数 BP_DOTNET_PROJECT_PATH にビルド対象となるプロジェクトファイルが存在するパスを記載せよ、書かれています。ビルド時に環境変数をセットするオプションは --build-env を使います。

次のコマンドをソリューションファイルが配置してあるフォルダで実行すれば、3つ全てのフォルダが Upload され、 Web.csproj を使って Build & Deploy が動きます。

zsh
az spring app deploy \
--service spa-enterprise \
--resource-group rg-springapps \
--name dotnetapp \
--source-path . \
--build-env BP_DOTNET_PROJECT_PATH=./web

.NET 用の ビルダーを作る

左メニューの VMWare Tanzu コンポーネントのビルド サービスをクリックし、ビルダーの追加をクリックします。
image.png

ビルダー名は好きな名前を入れます。dotnetのビルド専用だとわかる名前が良いでしょう。
OS スタックとはアプリをイメージ化する時のベースイメージです。
Bionicは Ubuntu 18.04 (Bionic Beaver)ベースで2023年10月以降サポートされないため、使用しない方がいいです。一方は Jammyは Ubuntu 22.04 (Jammy Jellyfish)ベースなのでこちらを選びましょう。Tiny, Base, Full の違いは 公式 docs を参照してください。基本的には Base を採用しておけば良いでしょう。
image.png

独自コンテナレジストリを使う

 本番用の Azure Spring Apps を構築する場合は独自のコンテナレジストリを使用することを強くお勧めします。細かい話は「Azure Spring Apps Enterprise の Tips (Python版) - 初回デプロイに失敗した時は要注意」に記載しましたが、独自のコンテナレジストリを使用することでビルドとデプロイのプロセスが分離され、トラブル発生時の問題特定にとても役に立ちます。プロセスが分離されるだけでソースアップロードによるビルドは変わらず実行できますし、ビルド結果生成されたイメージが見える化されるため、管理が容易になります。

 Azure Spring Apps を作成後に独自コンテナレジストリを使うようにすることはできません。作成時に指定する必要があります。ここでは Azure Container Registry を使うように Azure Spring Apps Enterprise を作成する方法をご紹介します。

Azure Container Registryを作成してください。 作成後、左メニューのアクセスキーを選択し、右側の管理者ユーザーを有効にします。

image.png

ログインサーバー、ユーザー名、パスワードを控えておきます。

image.png

Azure Spring Apps Enterprise を作成します。この時、VMWare Tanzu の設定画面でコンテナーレジストリの箇所で「独自のコンテナーレジストリを使用してビルドされたイメージを保存する」にチェックを入れます。そして、Azure Container Registry のログインサーバー、ユーザー名、パスワード情報を指定します。

image.png

ソースからビルドした結果をコンテナレジストリへ Push

 独自コンテナレジストリを使う場合、ビルドとデプロイのプロセスが分離されるため、まずビルドします。ソースを Upload & ビルドした結果をコンテナレジストリに Push するコマンドを叩きます。

 このコマンド例は複数プロジェクトの場合です。通常依存の大元となるのは ASP.NET Core プロジェクトですから、そのプロジェクトファイルが格納されているフォルダを指定します。ここでは「./web」と指定しています。1つしかプロジェクトファイルが無い場合は --build-env オプションが不要です。

zsh
az spring build-service build create \
-s spa-enterprise \
-g rg-springapps \
--name dotnetapp \
--build-env BP_DOTNET_PROJECT_PATH=./web \
--source-path . 

初回のコマンドは上記のように

az spring build-service build create

ですが、2回目からは下記のように

az spring build-service build update

create を update に変更します。

ビルドが正常に終了すると、コンテナレジストリにイメージが格納されます。この時、最新イメージのタグ名は result となっています。latest ではないので注意しましょう。

image.png

また、独自コンテナレジストリを使用する場合はビルド結果を Azure Spring Apps が保持しています。過去のビルド結果を見ることも、ビルド結果そのものを削除することも可能です。
image.png

ビルド結果はこのように履歴が残っていきます。Managed のコンテナレジストリを使う場合であってもこのビルド結果は見れるようにして欲しいものですが、残念ながらそうはなっていません。

image.png

トラブル発生時はこのビルド結果を削除してやり直すことも手段の1つであることは覚えておくと良いでしょう。

コンテナイメージを指定してデプロイ

 ビルドした結果、イメージがコンテナレジストリに格納されたらデプロイをします。次のコマンドを使います。

zsh
az spring app deploy \
-s spa-enterprise \
-g rg-springapps \
--name dotnetapp \
--container-image <イメージ名>:result \
--container-registry <コンテナレジストリサーバーアドレス> \
--registry-username <ユーザー名> \
--registry-password <パスワード>

ビルド時はコンテナレジストリを指定する必要がないのに、デプロイ時はコンテナレジストのアドレスどころかユーザー名とパスワードまで指定する必要があります。これはかなり使い勝手が悪いです。Managed IDを使うなどで指定が不要になると良いのですが。今後の更なる改善に期待したいところです。

Application Insights を使う

Azure Spring Apps では Java アプリケーションに限り Application Insights を自動的に有効にしてくれる機能があります。.NET を始めとする他言語では手動でセットアップする必要があります。簡単にセットアップできますが、最初だけ手間がかかるのは残念ですね。

ASP.NET Core に Application Insights の SDK を入れる

次のパッケージを ASP.NET Core アプリケーションに Nuget からインストールします。

Microsoft.ApplicationInsights.AspNetCore

次に Application Insights を有効するための処理を実装します。

Program.cs
builder.Services.AddApplicationInsightsTelemetry();

もし、Startup.cs が存在する場合は Startup.cs に実装します。

Startup.cs
public void ConfigureServices(IServiceCollection services)
{
    services.AddApplicationInsightsTelemetry();
    ・・・

appsettings.json を編集して Application Insights に出力するログレベルを調整できるようにしましょう。

appsettings.json
{
  "Logging": {
    "LogLevel": {
      "Default": "Information",
      "Microsoft.AspNetCore": "Warning"
-    }
+    },
+    "ApplicationInsights": {
+      "LogLevel": {
+        "Default": "Information"
+      }
+    }
  },
  "AllowedHosts": "*"
}

 ローカルで実行する分にはこれだけでOKですが、Azure Spring Apps にデプロイしたアプリから Application Insights にログを飛ばすためには Applicatin Insights のキーまたは接続情報をアプリから読み取れるようにする設定が必要です。

 Azure Spring Apps はアプリごとに環境変数を設定することができますので、そこに設定します。左メニューのアプリを選択し、右画面のアプリ一覧から該当のアプリをクリックします。次の例では dotnet-appinsights というアプリをクリックします。

image.png

開いた画面の左メニューの構成を選択し、右画面上部の環境変数をクリックします。
キーに次の値をセットします。

APPLICATIONINSIGHTS_CONNECTION_STRING

値には Application Insights の接続文字列をセットします。

image.png

Application Insights の接続文字列は概要に表示されています。

image.png

あとはアプリケーションをデプロイするだけです。

Service Registry を使う

Azure Spring Apps Enterprise では VMware Tanzu® Service Registry を Service Registry として使います。

セットアップ

 以前は Azure Spring Apps Enterprise を作成時に Service Registry を有効にしないと後から有効にはできなかったのですが、今は Azure Portal から有効・無効にすることができます。

 左メニューの「VMWare Tanzu コンポーネント」のサービスレジストリを選択し、右側の「管理」クリックします。新しくパネルが表示されます。パネルには「サービスレジストリを有効にする」チェックボックがありますので、このチェックボックスをON/OFFしてから下の適用ボタンをクリックします。

image.png

アプリケーションを Service Register に登録

 登録はとても簡単で Azure Portal からポチポチするだけです。 左メニューの「VMWare Tanzu コンポーネント」のサービスレジストリを選択し、右側の「アプリをバインドしています」→「アプリをバインドします」をクリックします。

image.png

 ポップアップの中のドロップダウンから作成済みのアプリケーションを選択し、適用ボタンをクリックします。これでアプリケーションの Service Registry への登録は完了です。

image.png

Service Registry に登録されたアプリケーションを呼び出す

 公式 docs には Java のソースしか記載がなく、 Eureka Client というライブラリが必要な旨が記載してあります。じゃあ C# はその Eureka Client に変わるものが残念ながらありません。そのため、Eureka サーバーが公開する REST API を使います。

 REST API からの戻り値からサーバーのURLを抜き出します。次のようなメソッドを用意して使い回すことになるでしょう。

C#
static async Task<string> GetServiceUrl(string serviceName)
{
    HttpClient httpClient = new HttpClient();
    string eurekaHost = "https://$AZURE_SPRING_APPS_NAME.svc.azuremicroservices.io/eureka/default/eureka";

    HttpResponseMessage response = await httpClient.GetAsync($"{eurekaHost}/eureka/apps/");
    if (!response.IsSuccessStatusCode)
    {
        return null;
    }

    string xmlContent = await response.Content.ReadAsStringAsync();
    XmlDocument xmlDoc = new XmlDocument();
    xmlDoc.LoadXml(xmlContent);

    // XPathで特定のサービス名にマッチするhomePageUrlを探す
    string xPathQuery = $"//application[name='{serviceName.ToUpperInvariant()}']/instance/homePageUrl";
    XmlNode node = xmlDoc.SelectSingleNode(xPathQuery);

    return node?.InnerText;
}

追記: 実は Service Registry は不要??

 Azure Spring Apps は 裏側で Kubernetes が稼働しており、 Kubernetes は既定で Service Discovery を搭載しています。そのため、Service Registryへアプリケーションを登録しなくても http://アプリ名 で別アプリを呼び出すことが可能です。

 となると、 Service Registry は不要です。いちいちアプリケーションを登録する必要なんてありません。
複数ノードを跨る場合などに Service Registry は威力を発揮しますが、Azure Spring Apps ではそのような基盤を意識する必要がないため、わざわざ Serivce Registry を使う意味はほぼありません。

Service Connector で他の Azure サービスと接続する

TODO

Cosmos DB を使う

TODO

Azure Database for MySQL Flexible Server を使う

TODO

Azure Database for PostgreSQL Flexible Server を使う

TODO

Azure SQL Database を使う

TODO

Azure Redis Cache を使う

TODO

Azure KeyVault を使う

TODO

Azure Service Bus を使う

TODO

0
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
2