Amazon Web Services ブログ

AWS Distro for OpenTelemetry, AWS X-Ray, Amazon CloudWatch を利用した Amazon EKS で実行されるアプリケーションのメトリクスとトレースのモニタリング

この記事は Adding metrics and traces to your application on Amazon EKS with AWS Distro for OpenTelemetry, AWS X-Ray and Amazon CloudWatch を翻訳したものです。

システムを監視できるようにするには、システムをインストルメント化する必要があります。これは、手動でライブラリか自動インストルメントエージェントを使用して、トレース、メトリクス、およびログを出力するコードをアプリケーションに追加する必要があることを意味します。一度デプロイされると、アプリケーションから計測されたデータがそれぞれのバックエンドに送信されます。利用可能なオブザーバビリティバックエンドは多数あり、どのコードをインストルメント化するかはソリューションによって異なります。

これまでは、オブザーバビリティバックエンドにデータを送信するための標準化されたデータフォーマットがありませんでした。さらに、オブザーバビリティバックエンドを切り替える場合は、コードを再インストルメント化し、新しい宛先にテレメトリデータを送信できるように新しいエージェントを設定する必要がありました。

OpenTelemetry (OTEL) プロジェクトの目標は、データを取り込み、変換し、オブザーバビリティバックエンドに送信するための標準化された SDK 、API 、ツールを提供することです。 AWS Distro for OpenTelemetry (ADOT) は、セキュアかつ本番環境での利用にも適した、 AWS がサポートする OpenTelemetry プロジェクトのディストリビューションです。AWS Distro for OpenTelemetry を利用すると、アプリケーションを一度インストルメント化するだけで、メトリクスとトレースを複数のモニタリングソリューションに送信できます。AWS Distro for OpenTelemetry は、バックエンドサービスにデータを送信するための SDK 、auto-instrumentation agents 、collectors 、exporters で構成されています。

このブログ記事では、Python で記述されたサンプルアプリケーションの PetAdoptionsHistory マイクロサービスに対して、OpenTelemetry Python クライアント SDK を使用してアプリケーションに分散型トレーシングとメトリクスを追加する方法を示します。AWS Distro for OpenTelemetry (ADOT) を使用してトレースを AWS X-Ray に送信し、メトリクスを Amazon CloudWatch に送信する方法について説明します。Amazon CloudWatch はモニタリングとオブザーバビリティのサービスです。Amazon CloudWatch は、アプリケーションを監視し、システム全体のパフォーマンス変化に対応し、リソース使用率を最適化するためのデータと実用的な洞察を提供します。Amazon CloudWatch は、ログ、メトリクス、イベントの形式でモニタリングおよび運用データを収集します。

このブログでは、Amazon CloudWatch の機能の 1 つである Amazon CloudWatch Service Lens を使用して、アプリケーションを構成するコンポーネントと、それらの繋がりを可視化します。さらに、マップノードから関連するメトリクス、ログ、トレースにすばやくドリルダウンできます。具体的には、CloudWatch Service Lens を活用して、PetAdoptionsHistory マイクロサービスの、インストルメンテーションコードを追加する前後のアプリケーションアーキテクチャや、サービス呼び出しの成功率、エラー率に関連する指標を確認します。

CloudWatch Logs Insights を活用してアプリケーションで生成されたログデータをインタラクティブに検索および分析する方法を中心に説明します。そして、Amazon CloudWatch ダッシュボードを作成し、関連するアプリケーションメトリクスを一つのビジュアルダッシュボードにまとめます。

実際のコード全量とステップバイステップの説明は、One Observability Workshop でご確認いただけます。このブログ記事では、OpenTelemetry Python クライアント SDK を使用して PetAdoptionsHistory マイクロサービスをインストルメント化する方法について、最も重要な部分を取り上げ、関連する概念について説明します。

アーキテクチャ全体像

PetAdoptionsHistory マイクロサービスは Python で記述されており、Amazon Elastic Kubernetes サービス (EKS) のコンテナで実行されます。AWS Distro for OpenTelemetry (ADOT) collector は同じクラスターにデプロイされ、アプリケーションからトレースを受信します。また collector は、HTTP を使用してアプリケーションの /metrics エンドポイントから定期的にメトリクスを取得するように設定されています。

AWS Distro for OpenTelemetry (ADOT) collector は、トレースを AWS X-Ray にパブリッシュし、Amazon CloudWatch にメトリクスを送信するように設定されています。

このブログの後半では、OpenTelemetry collector の設定について詳しく説明し、collector がアプリケーションからメトリクスとトレースを取得する方法と、収集したデータの送信先となるサービスについて説明します。

Figure 1. AWS Distro for OpenTelemetry

図 1. AWS Distro for OpenTelemetry

Solution Walkthrough

アプリケーション全体像

ここでは、「PetAdoptionsHistory」というマイクロサービスに焦点を当てます。ペットを引き取るためのウェブアプリケーションである PetAdoptions アプリケーションがあり、PetAdoptionsHistory はその大きなアプリケーションの一部です。ペットを引き取るたびに、トランザクションが Amazon Aurora PostgreSQL データベースに記録されます。

PetAdoptionsHistory マイクロサービスは、トランザクションの詳細をクエリしたり、履歴データをクリーンアップするための API を公開しています。この API の呼び出しは、PetSite フロントエンドサービスから行われます。別のサービスであるトラフィックジェネレーターは、フロントエンドに定期的にアクセスすることで、PetSite Web サイトでの人のやりとりをシミュレートします。フロントエンドへアクセスすると、PetAdoptionsHistory サービスが呼び出され、記録された引き取りのリストを返すか、データベースからトランザクションのリストを消去するかのどちらかが実行されます。

PetAdoptionsHistory アプリケーションでは以下を使用します。

  • Flask : AWS Application Load Balancer からアプリケーションへのリクエストを扱う
  • pyscopg2 : Amazon Aurora PostgreSQL データベースへの接続を扱う

PetAdoptionsHistory アプリケーションは Amazon Elastic Kubernetes Service (EKS) クラスターにデプロイされます。サービスにインストルメンテーションを追加する前の Amazon CloudWatch service map の概要を以下に示します。PetAdoptionsHistroyは、この図にはまだ表示されていません。

 Service Map showing end-to-end view of the PetAdoptions microservices architecture, before adding instrumentation to the service.

図 2: CloudWatch Service Map

PetAdoptionsHistory マイクロサービスへの分散トレースの追加

分散型トレーシングでは、相関 ID を使用して、マイクロサービス同士や、各マイクロサービスのバックエンド (データベース、外部サービス) を跨ぐリクエストを可視化します。 Python で OpenTelemetry SDK を使い始めるには、OpenTelemetry のトレーシングライブラリをインポートし、初期化手順に従う必要があります。次のステップを詳しく見ていきましょう。

OpenTelemetry SDK には、トレースとメトリクスがアプリケーション内でどのように流れるかを定義するパイプラインが必要です。現在、AWS X-Ray に送信されるトレースは特定のフォーマットに従う必要があります。Python SDK を使用する場合は、 AWSXRayIdGenerator でフォーマットを扱うことができます。トレースパイプラインは次のようになります (簡潔にするために import ステートメントは省略しています)。

# Setup AWS X-Ray propagator
set_global_textmap(AwsXRayPropagator())

# Setup AWS EKS resource detector
resource = get_aggregated_resources(
    [
        AwsEksResourceDetector(),
    ]
)

# Setup tracer provider with the X-Ray ID generator
tracer_provider = TracerProvider(resource=resource, id_generator=AwsXRayIdGenerator())
processor = BatchSpanProcessor(OTLPSpanExporter())
provider.add_span_processor(processor)

# Sets the global default tracer provider
trace.set_tracer_provider(tracer_provider)

# Creates a tracer from the global tracer provider
tracer = trace.get_tracer(__name__)

実際にトレースデータを生成するには、アプリケーションの一部をインストルメント化する必要があります。次のスニペットでは、アプリケーションロードバランサーからの受信 HTTP リクエストをキャプチャできます。

from opentelemetry.instrumentation.flask import FlaskInstrumentor

app = Flask(__name__)
FlaskInstrumentor().instrument_app(app)

データベースのトランザクションをキャプチャするには、psycopg2 ライブラリをインストルメント化します。

from opentelemetry.instrumentation.psycopg2 import Psycopg2Instrumentor

Psycopg2Instrumentor().instrument()

キャプチャしたトレースにサービス名を付けるには、リソースとサービス名の属性を使用できます。 X-Ray では、サービスが以下の名前で表示されます。

resource = Resource(attributes={
    SERVICE_NAME: "PetAdoptionsHistory"
})

上記のインストルメンテーションにより、データベースと HTTP トランザクションを自動的に可視化できます。さらに、カスタムスパンを使用すると、アプリケーションの任意の部分をインストルメント化できます。たとえば、次のスニペットでは、引き取り履歴データベースのテーブルをクリーンアップする DELETE HTTP 呼び出し用に、 transactions_delete というスパンを作成します。

@app.route('/petadoptionshistory/api/home/transactions', methods=['DELETE'])
def transactions_delete():
    with tracer.start_as_current_span("transactions_delete") as transactions_span:
        repository.delete_transaction_history(db)
        return jsonify(success=True)

スニペットの全量は Observability Workshop の GitHub リポジトリで確認することができます。

PetAdoptionsHistory マイクロサービスへのカスタムメトリクスの追加

インストルメンテーションによりトラブルシューティングが容易になり、メトリクスによってサービスの運用状況を確認することができます。メトリクスを使用すると、事前に定義されたしきい値に基づいてアラームを作成し、発生する可能性のある異常について通知を受け取ることができます。100 以上の AWS サービスが、追加費用なしでメトリクスを Amazon CloudWatch に自動的でパブリッシュします。サービスが Amazon CloudWatch にメトリクスをパブリッシュすることで、その使用状況に関するインサイトを利用者に提供します。たとえば、AWS Application Load Balancer を使用する場合、HTTPCode_Target_2XX_Count のような Amazon CloudWatch メトリクスを取得できます。これにより、 ALB ターゲットによって生成された HTTP レスポンスコードの数がわかります。

アプリケーションの理解を深めるために、アプリケーションのビジネスロジックに基づいたカスタムメトリクスを発行し、関連するビジネスクライテリアに基づいたアラートを作成できます。これを実現するための簡単かつ一般的な方法の 1 つは、 Prometheus を使用することです。 Prometheus は、オープンソースのメトリクスベースの監視システムです。シンプルでありながら強力なデータモデルとクエリ言語を備えているため、アプリケーションとインフラストラクチャのパフォーマンスを分析できます。

OpenTelemetry と Prometheus には、 HTTP レスポンスコードの数などのカスタムメトリクスをエンドポイントごとに動的に生成したり、それらのビジネスメトリクスを追加したりするためのライブラリが用意されています。 OpenTelemetry SDK のメトリクス設定は以下のようになります (簡潔にするためにインポート文は省略しています)。

# Setup metrics
reader = PrometheusMetricReader()
meter_provider = MeterProvider(resource=resource, metric_readers=[reader])

# Sets the global default meter provider
metrics.set_meter_provider(meter_provider)

# Creates a meter from the global meter provider
meter = metrics.get_meter(__name__)

# Flask exporter
from prometheus_flask_exporter import PrometheusMetrics

# This exposes the /metrics HTTP endpoint
metrics = PrometheusMetrics(app, group_by='endpoint')

# Create a business metric with a counter for the number of GET calls
transactions_get_counter = meter.create_counter(
    "transactions_get.count",
    description="The number of times the transactions_get endpoint has been called",
)

このアプリケーションでは、デフォルトの Prometheus メトリクスに加えて、 transactions_get_counter 変数を使用してビジネストランザクションの合計数を追跡することとしています。

@app.route('/petadoptionshistory/api/home/transactions', methods=['GET'])
def transactions_get():
    transactions_get_counter.add(1) # increment count
    transactions = repository.list_transaction_history(db)
    return jsonify(transactions)

Prometheus では、さまざまなタイプのメトリクスを利用することができます。カウンターメトリクスは、測定値が増加する場合にのみ使用されます。つまり、その値は上がることしかできません。唯一の例外は、カウンタが再起動されたときにゼロにリセットされることです。一方、ゲージは増加したり減少する測定に使用されます。ゲージの例としては CPU 使用率、メモリ使用量、キューのサイズなどがあります。Python では、ゲージはコールバック関数で定義できます。コールバック関数は、呼び出された時点のゲージの値を返します。

def transactions_history_callback(result):
    count = repository.count_transaction_history(db)
    yield Observation(count)

meter.create_observable_gauge(
    name="transactions_history.count",
    description="The number of items in the transactions history",
    callbacks=[transactions_history_callback])

transactions_get_counter = meter.create_counter(
    "transactions_get.count",
    description="The number of times the transactions_get endpoint has been called",
)

AWS Distro for OpenTelemetry

この例では、AWS Distro for OpenTelemetry collector を使用してトレースとメトリクスを収集し、 AWS X-Ray と Amazon CloudWatch に送信します。これは OpenTelemetry configuration components を使用して実現されます。一度設定したこれらのコンポーネントは、OpenTelemetry collector 内のデータフローを定義するパイプラインを介して有効にする必要があります。以下のセクションでは、 3 つのコンポーネントを使用するアプリケーションのパイプラインについて説明します。

  • Receivers: receiver は、プッシュベースまたはプルベースでデータを Collector に取り込みます
  • Processors: processors は、データを受信してからエクスポートするまでの処理を実行します
  • Exporters: exporter は、プッシュベースまたはプルベースで1 つ以上の宛先にデータを送信します

AWS X-Ray の設定

AWS X-Ray では、リクエストがアプリケーションを通過する際の全体像がわかり、ペイロード、関数、トレース、サービス、 API のデータを視覚化できます。AWS X-Ray を使用すると、分散トレースを分析し、システム全体を理解できます。AWS X-Ray の詳細については、ワークショップの CloudWatch ServiceLens Map をご覧ください。

このレシーバー構成では、アプリケーションが gRPC または HTTP を使用して以下のいずれかのエンドポイントにトレースデータを送信することを想定しています。

receivers:
  otlp:
    protocols:
      grpc:
        endpoint: 0.0.0.0:4317
      http:
        endpoint: 0.0.0.0:4318

AWS X-Ray へのトレースの送信は、以下に定義されている awsxray exporter で設定されます。AWS リージョンやプロキシなどのより高度な設定オプションについては、AWS Distro for OpenTelemetry (ADOT) ウェブサイトの Getting Started with X-Ray Receiver in AWS OpenTelemetry Collector セクションをご覧ください。

exporters:
  awsxray:

Amazon CloudWatch metrics の設定

ここでは、AWS Distro for OpenTelemetry collector によって収集されたアプリケーションの Prometheus メトリクスをAmazon CloudWatch metrics に送信する例をご紹介します。

以下のレシーバー設定では、AWS Distro for OpenTelemetry collector は、20 秒ごとに HTTP 呼び出し経由で Prometheus メトリクスの専用パスをスクレイピングします (上記のインストルメンテーションセクションを参照)。AWS Distro for OpenTelemetry は Prometheus 設定をサポートしているため、サービスディスカバリメカニズムを使用して Kubernetes コンテナやポッド名などの環境情報を収集することができます。

receiver:
  prometheus:
    config:
      global:
        scrape_interval: 20s
        scrape_timeout: 10s
      scrape_configs:
        - job_name: "otel-collector"
          kubernetes_sd_configs:
            - role: pod
          relabel_configs:
            - source_labels: [__meta_kubernetes_pod_container_port_number]
              action: keep
              target_label: '^8080$'
            - source_labels: [ __meta_kubernetes_pod_container_name ]
              action: keep
              target_label: '^pethistory$'
            - source_labels: [ __meta_kubernetes_pod_name ]
              action: replace
              target_label: pod_name
            - source_labels: [ __meta_kubernetes_pod_container_name ]
              action: replace
              target_label: container_name

これらのメトリクスを Amazon CloudWatch に送信するために、CloudWatch 埋め込みメトリクスフォーマット (EMF) を使用する awsemf exporter を設定しました。EMF は、構造化されたログイベントに埋め込まれたメトリクス値を自動的に抽出するように Amazon CloudWatch Logs に指示するために使用される JSON の仕様です。これにより、Prometheus 形式のメトリクスを Amazon CloudWatch metrics に変換できます。

ここでは、これらのメトリクスをAmazon CloudWatch metrics の PetAdoptionsHistory 名前空間 (メトリクスのコンテナ) の中で取得するための例を示します。transactions_get_count_total メトリクスには、pod_namecontainer_name の 2 つのディメンションが関連付けられます。

exporters:
  namespace: "PetAdoptionsHistory"
  metric_declarations:
    - dimensions: [ [ pod_name, container_name ] ]
      metric_name_selectors:
        - "^transactions_get_count_total$"
        - "^transactions_history_count$"
        - "^process_.*"
      label_matchers:
        - label_names:
          - container_name
          regex: ^pethistory$

パイプラインの定義

すべてを一元的に管理するためには、OpenTelemetryの設定として service 定義の下にパイプラインに関する定義が必要です。以下に例を示します。

service:
      pipelines:
        traces:
          receivers: [otlp]
          exporters: [awsxray]
        metrics:
          receivers: [prometheus]
          exporters: [awsemf]

OpenTelemetry collector の全設定は、こちらのリンクにあります。

結果

アプリケーションをインストルメント化し、AWS Distro for OpenTelemetry collector と一緒にデプロイすることで、トレースを AWS X-Ray に、メトリクスを Amazon CloudWatch に送信することができます。

CloudWatch Service Map

CloudWatch Service Map には、サービスのエンドポイントとリソースが「ノード」として表示され、各ノードとその接続のトラフィック、レイテンシー、エラーを表示する機能が備わっています。ノードを選択すると、関連するメトリクス、ログ、トレースといった詳細な情報を確認できます。アプリケーションのエンドツーエンドビューは、パフォーマンスのボトルネックを特定し、影響を受けるユーザーをより効率的に特定するのに役立ちます。これにより、問題とその問題が与えるアプリケーションへの影響を調査することができます。

下の図は、PetAdoptionsHistory ノードとその接続が記載されたサービスマップです。

Amazon CloudWatch Service Map after adding instrumentation to the service, showing the PetAdoptionsHistory node and its connections.

図 3: アップデートされた Amazon CloudWatch Service Map

サービスマップ上でのPetAdoptionsHistory サービスを選択すると、他のエンティティとの接続が強調されます。

Highlighting the PetAdoptionsHistory service in the Service Map showing connections to other entities

図 4: サービスマップ上での PetAdoptionsHistory サービスのハイライト

マップ上で PetAdoptionsHistory ノードを選択することで、このサービスに関連するレイテンシー、リクエスト、障害などの関連メトリクスを、サービスとその接続を含むノードマップとともに表示することができます。

Selecting the PetAdoptionsHistory service in the Service Map showing metrics on Latency, Requests and Fault (5xx) on the service

図 5: サービスマップ上での PetAdoptionsHistory サービスの選択

トレースを選択することで、上記の transactions_delete の設定だけでなく、トランザクションの発信元であるフロントエンド Web サイト (PetSite)についても確認することができます。

Trace Map for transactions_delete showing the origin of the transaction all the way from the front-end website (PetSite).

図 6: transactions_deleteのトレースマップ

Amazon CloudWatch Logs Insights

Amazon CloudWatch Logs Insights を使用すると、Amazon CloudWatch Logs のログデータをインタラクティブに検索して分析できます。これはトラブルシューティングに役立ちます。たとえば、ノードレベルのメトリクスが急上昇すると、タスクレベルのエラーをさらに詳しく知ることができます。Amazon CloudWatch の埋め込みメトリクスフォーマット (EMF) が Amazon CloudWatch Logs に書き込まれることで、Amazon CloudWatch Logs Insights を活用して、メトリクスが特定のしきい値を超えたタイミングと頻度を調査できます。下の例では、 transactions_history_count > 90 で絞り込んでいます。

90.” width=”700″ height=”515″>

図 7: Amazon CloudWatch Logs Insights

Amazon CloudWatch Metrics

Amazon CloudWatch metrics explorer によって、アプリケーションから収集されたすべてのメトリクスを PetAdoptionsHistory の名前空間の中で表示することができます。

Amazon CloudWatch metrics explorer showing metrics in the custom namespace PetAdoptionsHistory.

図 8: Amazon CloudWatch metrics explorer

トレースとメトリクスデータの両方が揃ったので、Amazon CloudWatch dashboards を作成して、アプリケーションのパフォーマンスを一元的に把握できるようになりました。

Amazon CloudWatch dashboards showing custom application metrics.

図 9: Amazon CloudWatch dashboards

まとめ

AWS Distro for OpenTelemetry は、オブザーバビリティデータの管理に様々な選択肢を与えます。この記事では、OpenTelemetry client SDK を使用してアプリケーションをインストルメント化する方法を説明しました。AWS Distro for OpenTelemetry collector を設定して、アプリケーションのトレースを AWS X-Ray に、メトリクスを Amazon CloudWatch に送信するように設定しました。この設定によって、インタラクティブなサービスマップを提供する Amazon CloudWatch ServiceLens を使用して、アプリケーションのメトリクス、ログ、トレースを関連付けることができます。

One Observability Workshop では、多くの AWS オブザーバビリティサービスを試すことができます。ワークショップは、AWS 主導のイベントであれば事前に用意されたアカウントを使用して実施することができますし、自分のペースで進めたい場合には、自分のアカウントを利用して実施することもできます。PetAdoptionsHistory マイクロサービスを自分で実行したり、Contributor Insights や Logs Insights などの Amazon CloudWatch の他の機能を調べたりするには、One Observability Workshop の各セクションを確認してください。

翻訳はソリューションアーキテクトの津郷が担当しました。原文はこちらです。