🙆

Azure Container Apps 上のアプリケーションをモニタリングする方法

2024/07/07に公開

TL;DR

Azure Container Apps 上に展開したアプリケーションをモニタリングするにはいくつかの方法があります。公式のドキュメントだけではそれぞれの方法でできることやコンポーネントの関係性がいまいちよく分からなかったので整理してみました。

アプリケーションは Java の Spring Boot を前提にしていますが、Azure Container Apps に関する部分は他の言語やフレームワークでも適用できます。

前提知識 : OpenTelemetry によるアプリケーションのモニタリング

OpenTelemetry、Application Insights に詳しい方は読み飛ばしてください。

OpenTelemetry でできること

OpenTelemetry を使うとでログ、メトリクス、トレースを収集することができ、また、収集したデータを OpenTelemetry に準拠したさまざまなツールに連携できます。
OpenTelemetry のコンセプトや概要については公式のサイトを参照してください。重要なポイントは以下の部分です。

1. You own the data that you generate. There’s no vendor lock-in.
2. You only have to learn a single set of APIs and conventions.

https://opentelemetry.io/docs/what-is-opentelemetry/

Java アプリケーションで OpenTelemetry を利用するには 2 つの方法があります。Java エージェントとして OpenTelemetry のエージェントを java コマンドの引数で指定する方法(zero-code instrumentation)とソースコード(code-based instrumentation)として実装する方法です。Java エージェントは次のように java コマンドの引数で指定します。ソースコードの変更が必要無いため簡単に導入できます。

java -javaagent:./opentelemetry-javaagent.jar -jar myapp.jar

Java エージェントを利用した上で、以下のようなコードで openTelemetry のインスタンスが作成できます。

package com.example.otelsample;
import io.opentelemetry.api.OpenTelemetry;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import io.opentelemetry.api.GlobalOpenTelemetry;

@Configuration
public class OpenTelemetryConfig {
    @Bean
    public OpenTelemetry openTelemetry() {
        return GlobalOpenTelemetry.get();
    }
}

その上で、以下のようなコードで OpenTelmetry としてログの出力やイベントの生成、メトリックを生成します。

// OpenTelemetry の Span の開始とイベントの送信
Span span = tracer.spanBuilder("CalcController").startSpan();
span.addEvent("Start calc");

// ログの出力
logger.info("Call endpoint: " + entry.getKey() + " " + entry.getValue());

// カウンターの生成
counter = meter.counterBuilder("calc.counter")
        .setDescription("How many times the calculator have been ran.")
        .setUnit("runs")
        .build();
counter.add(1);

OpenTelemetry で出力されるデータは、何かしらの方法で取得し、可視化する必要があります。
オープンソースのツールでは、トレースやログは Jaeger や Zipkin で、メトリクスは Prometheus で収集し、Grafana で可視化ができます。

また、OpenTelemetry Collector を利用すると、アプリケーションにそれぞれのツールへのデータ送信のためのコードを追加する必要なく、データの収集ができます。OpenTelemetry Collector は、アプリケーションと各ツールのインターフェースを提供します。
公式のドキュメントで提供されているデモのアーキテクチャーが分かりやすいでしょう。

アプリケーションは OpenTelemetry Collector のエンドポイント(http://localhost:4317 or 4318) にデータを送信し、OpenTelemetry Collector の設定でバックエンドのエンドポイントを指定しています。

https://opentelemetry.io/docs/demo/architecture/

たとえば以下は、OpenTelemetry Collector の設定です。

collector-config.yaml
receivers:
  otlp:
    protocols:
      grpc:
        endpoint: 0.0.0.0:4317
      http:
        endpoint: 0.0.0.0:4318

exporters:
  debug:
    verbosity: detailed
  otlp/jaeger:
    endpoint: jaeger-all-in-one:4317
    tls:
      insecure: true
  prometheus:
    endpoint: "0.0.0.0:8889"
    const_labels:
      label1: value1
  zipkin:
    endpoint: "http://zipkin-all-in-one:9411/api/v2/spans"
    format: proto
processors:
  batch:

extensions:
  health_check:
  pprof:
    endpoint: :1888
  zpages:
    endpoint: :55679

service:
  telemetry:
    logs:
      level: DEBUG
      sampling:
        enabled: false
  extensions: [pprof, zpages, health_check]
  pipelines:
    traces:
      receivers: [otlp]
      exporters: [debug, zipkin, otlp/jaeger]
    metrics:
      receivers: [otlp]
      processors: [batch]
      exporters: [debug, prometheus]
    logs:
      receivers: [otlp]
      exporters: [debug]

receivers で受信エンドポイントを設定し、exporters でエクスポート先の設定をします。さらに、exporters で設定したサービスを、servicepipelines で設定すると、traceを otlp の受信エンドポイントで受信し、Zipkin へエクスポートする、といったことが可能になります。

OpenTelemetry は前述のコンセプトのように、ベンダーロックインしないという原則があります。これは開発者に自由な選択肢を与える一方、さまざまなツールを利用する必要性があることも想定できます。上記で紹介したように、ログは Jaeger、メトリックは Prometheus といったようなことが起きます。それぞれに設定があるため、設定を熟知し管理していく必要があります。

Application Insights でできること

Application Insights は、OpenTelemetry のデータを受け取ることができるため、OpenTelemetry を利用したアプリケーションであれば、コードの変更なく Application Insights でモニタリングできます。また、トレース、ログ、メトリックのそれぞれを受信し、一つのサービスでそれぞれを確認できる便利なサービスです。

Application Insights を Java から利用するには、前提知識で紹介した OpenTelemetry によるモニタリング同様、2 つの方法があります。Java エージェントとして Azure Monitor OpenTelemetry Distro を指定する方法と、ソースコードとして実装する方法です。Java エージェントを利用する方法は、エージェントを OpenTelemetry と入れ替えるだけです(接続文字列を環境変数もしくは設定ファイルに書いておく必要があります)。

export APPLICATIONINSIGHTS_CONNECTION_STRING=<Your Connection String> // Application Insights への接続文字列を環境変数に設定
java -javaagent:"path/to/applicationinsights-agent-3.5.3.jar" -jar myapp.jar

https://learn.microsoft.com/ja-jp/azure/azure-monitor/app/java-spring-boot

Application Insights の Java エージェントは、OpenTelemetry のコンポーネントのラッパーであり、OpenTelemetry に関しては同様の機能を提供します。

さらに、Application Insights で提供されている機能を利用できるように拡張された実装が含まれています。ドキュメントには以下のように書かれています。

Azure Monitor OpenTelemetry Distro" を使用する必要がある理由
コミュニティのネイティブ OpenTelemetry よりも Azure Monitor OpenTelemetry Distro を使用することには、いくつかの利点があります。

  • 有効化作業を減らす
  • Microsoft によってサポートされている
  • 次のような Azure 固有の機能が導入されます。
    • 従来の Application Insights SDK と互換性のあるサンプリング
    • Microsoft Entra 認証
    • オフライン ストレージと自動再試行
    • Statsbeat
    • Application Insights 標準メトリック
    • さまざまな Azure 環境でクラウド ロール名とクラウド ロール インスタンスを自動入力するためのリソース メタデータを検出する
    • ライブ メトリック

https://learn.microsoft.com/ja-jp/azure/azure-monitor/app/opentelemetry-add-modify?tabs=aspnetcore#why-should-i-use-the-azure-monitor-opentelemetry-distro

Azure Container Apps 上のアプリケーションをモニタリングする方法

ここからが本題です。Azure Container Apps に展開されたアプリケーションを監視するための方法としては、以下の方法があります。

  1. Application Insights(Azure Monitor OpenTelemetry Distro) の利用
  2. Container Apps Environment の OpenTelemetry Collector の利用
  3. Dapr の利用
  4. Log Analytics へのシステムログ/コンソール ログの転送

1. Application Insights(Azure Monitor OpenTelemetry Distro) の利用


アプリケーションのコンテナ内で Application Insights のエージェントを同梱する、もしくはアプリケーションに実装する方法です。
https://learn.microsoft.com/ja-jp/azure/azure-monitor/app/opentelemetry-enable?tabs=aspnetcore
Azure Container Apps に依存しないため、既に Application Insights でモニタリングしている場合であれば何も変更はありません。当然 Application Insights で利用できる機能をそのまま利用できます。

一方で、アプリケーション単位(Container Apps 単位)で Application Insights のエージェントの指定なり、コードへの組み込みが必要になるため、コンテナイメージのサイズが大きくなりがちで、接続文字列の管理も必要になります。

  • トランザクションの見え方

    app1 の Container Apps から add / sub / mul / div の Container Apps のそれぞれを呼び出している様子が分かります。

ちなみに、例外が発生した場合はこんな感じで見えます。

例外が発生した場合

0 除算をした結果ですが、どのサービス呼び出しでどのような例外が発生したかが一目で分かります。

  • メトリック

    OpenTelemetry の Meter で生成したメトリックが Application Insights に送信されています。

2. Container Apps Environment の OpenTelemetry Collector の利用


Container Apps Environment では、OpenTelemetry Collector を有効にできます。Container Apps から OpenTelemetry SDK で出力されるデータを受信できます。

こちらも Application Insights の利用と同様に OpenTelemetry エージェントの指定・コード実装が必要になりますが、Application Insights を使わないことで接続文字列を Container Apps 単位で管理する必要が無くなります。

あくまで Container Apps 単位で利用しないという意味で、エクスポート先として Application Insights を利用する際は、Container Apps Environment 単位で Application Insights の接続文字列を指定する必要があります。

このオプションは既に OpenTelemetry を利用している場合に最も簡単な方法です。ただし、現在のところ Application Insights へのメトリックの送信は出来ません。

有効化の方法

Container Apps Envrionment に対して設定します。設定が有効化されるとアプリケーションの環境変数として OTEL_EXPORTER_OTLP_ENDPOINT が自動的に設定されます。

https://learn.microsoft.com/en-us/azure/container-apps/opentelemetry-agents?tabs=arm

  • Azure CLI
az containerapp env telemetry app-insights set -n aca-otelsample -g rg-opentelemetry --connection-string $APPLICATIONINSIGHTS_CONNECTION_STRING --enable-open-telemetry-traces true --enable-open-telemetry-logs true
  • Azure Portal

  • トランザクションの見え方

    1 の方法と同様にサービス間呼び出しの関係が分かります。

3. Dapr の利用

Container Apps は Dapr を使うことで Container Apps 間のリクエストの呼び出しを http://localhost:3500/v1.0/invoke/checkout/method/checkout/100 のようにできます。Container Apps サイドカーとして展開されている Dapr コンテナが自動的にリクエストをキャプチャし対象の Container Apps へリクエストをルーティングしてくれます。

この時、Dapr を経由したリクエストのトレースを Application Insights で受け取ることができます。Container Apps Environment に設定した Application Insights の接続文字列を使います。Container Apps では Dapr を有効化するだけで、Application Insights や OpenTelemetry のエージェントの設定も不要です。
Dapr を使うことを前提であれば最も簡単な方法です。

一方で、Dapr 単体ではサービス間の通信はキャプチャできますが、サービス内のトレースやメトリックは収集できません。サービス内の情報を取得するには Application Insights や OpenTelemetry のエージェントを利用する必要があります。

尚、「2. Container Apps Environment の OpenTelemetry Collector の利用」 と同時に利用することは出来ません(接続文字列を設定するときにエラーになります。あくまで Application Insights へのデータ送信の観点であり、Dapr 自体が利用できないということではありません)。

https://learn.microsoft.com/ja-jp/cli/azure/containerapp/env?view=azure-cli-latest#az-containerapp-env-create

有効化の方法

  • Azure CLI
az containerapp env create --name aca-otelsample --resource-group rg-opentelemetry --location japaneast --dapr-instrumentation-key $APPLICATIONINSIGHTS_CONNECTION_STRING
  • Bicep
resource env 'Microsoft.App/managedEnvironments@2024-03-01' = {
  name: aca_env_name
  location: location
  properties: {
    appLogsConfiguration: {
      destination: 'log-analytics'
      logAnalyticsConfiguration: {
        customerId: la.properties.customerId
        sharedKey: la.listKeys().primarySharedKey
      }
    }
    zoneRedundant: false
    daprAIConnectionString: appinsights_connection_string // <----- Application Insights の接続文字列を設定
    workloadProfiles: [
      {
        workloadProfileType: 'Consumption'
        name: 'Consumption'
      }
    ]
    peerAuthentication: {
      mtls: {
        enabled: false
      }
    }
  }
}
  • トランザクションの見え方


    サービスの呼び出しのそれぞれの関係は保持されず、サービス呼び出しがあったことだけが分かります。

4. Log Analytics へのシステムログ/コンソール ログの転送


Container Apps Environment では、システムログやコンソールログを Log Analytics に転送できます。あくまでコンソールに出力されるログだけなので、イベントやメトリックは収集できませんが、とりあえずプラットフォームのログを出力したいという場合にアプリケーションの実装方法や Container Apps の設定を全く変更する必要が無いため最も手間がかかりません。

https://learn.microsoft.com/ja-jp/azure/container-apps/log-options

  • ログの見え方

見て頂く通り関連付けるIDが無いので Log Analytics に出力されたログからアプリケーションのトランザクションを追うことは困難です。

Microsoft (有志)

Discussion