編集

次の方法で共有


Application Insights と OpenCensus を使用して分散システムを監視する

Azure Event Hubs
Azure Functions
Azure Service Bus
Azure Monitor

この記事では、Azure Functions、Azure Event Hubs、Azure Service Bus を使用して作成された分散システムについて説明します。 OpenCensus for Python と Application Insights を使用してエンド ツー エンド システムを監視する方法について詳しく説明します。 さらにこの記事では、分散トレースについて紹介し、Python コード例を使用して分散トレースがどのように動作するかについても説明します。 シナリオの説明をわかりやすくするために、このアーキテクチャでは架空の会社 Contoso を使用します。

注意

OpenCensus と OpenTelemetry はマージされますが、OpenCensus が引き続き Azure Functions を監視するために推奨されているツールです。 Azure 用 OpenTelemetry はプレビュー段階であり、一部の機能はまだ使用できません

Architecture

3 つの手順 (クエリ、処理、アップサート) に分割されている、実装済みアーキテクチャを示す図。

このアーキテクチャの Visio ファイルをダウンロードします。

ワークフロー

  1. クエリ。 タイマーによってトリガーされる Azure 関数を使用して、Contoso の内部 API に対してクエリを実行し、1 日に 1 回最新の売上データを取得します。 この関数では、Azure Event Hubs 出力バインドを使用して、非構造化データをイベントとして送信します。

  2. 処理。 Event Hubs では、非構造化データを処理して事前に定義された構造に書式設定する、Azure 関数をトリガーします。 この関数では、Service Bus 出力バインドを介してインポートする必要がある資産ごとに 1 つのメッセージを Service Bus に発行します。

  3. アップサート。 Service Bus では、キューのメッセージを使用し、会社の共通ストレージでアップサート操作を実行する Azure 関数をトリガーします。

このアーキテクチャで起こり得る操作エラーについて考慮することが重要です。 次に例をいくつか示します。

  • 内部 API が使用できない。このエラーが、アーキテクチャの手順 1 でデータのクエリ Azure 関数によって発生する例外につながります。
  • 手順 2 で、データの処理 Azure 関数で、関数の条件またはパラメーターに該当しないデータが検出された。
  • 手順 3 で、データのアップサート Azure 関数が失敗した。 再試行が何回も行われると、Service Bus キューからのメッセージは配信不能キューに入れられます。このキューは、事前に定義された回数だけ再試行しても処理できない、または受信側に配信できないメッセージを保持するセカンダリ キューです。 この後、メッセージは確立された自動プロセスに従うことも、手動で処理することもできます。

コンポーネント

  • Azure Functions は、アプリケーションを管理するサーバーレス サービスです。
  • Application Insights は、開発、テスト、運用の各環境のアプリケーションを監視する Azure Monitor の機能です。 Application Insights では、アプリケーションがどのように実行されたかを分析し、アプリケーションの実行データを確認してインシデントの原因を特定します。
  • Azure Table Storage は、非リレーショナル構造化データ (構造化された NoSQL データ) をクラウドに保存し、スキーマなしの設計でキー/属性ストアを提供するサービスです。
  • Event Hubs は、毎秒数百万件のイベントを受信して処理できる、スケーラブルなイベント インジェスト サービスです。
  • OpenCensus は、分散トレース、メトリック、ログ テレメトリの収集に使用できる、一連のオープンソース ライブラリです。 このアーキテクチャでは、OpenCensus の Python による実装を使用します。
  • Service Bus は、メッセージ キューと、パブリッシュとサブスクライブのトピックを備えたフル マネージド メッセージ ブローカーです。

シナリオの詳細

分散システムは、疎結合コンポーネントで構成されています。 コンポーネントがどのように通信するかを理解し、ユーザー要求のエンド ツー エンドの工程をすべて把握するのは困難な場合があります。 このアーキテクチャを利用すると、コンポーネントがどのように接続されているかを確認できます。

多くの企業がそうであるように、Contoso も、オンプレミスまたはサードパーティのデータをクラウドに取り込む必要があり、同時に、サービスや社内ツールを使用して売上に関するデータも収集する必要があります。 このアーキテクチャでは、Contoso のある部門が、非構造化データを公開する内部 API を構築し、そのデータを共通ストレージに取り込みます。 共通ストレージには、すべての部門の構造化データが含まれています。 このアーキテクチャは、Contoso がどのようにそのメタデータを抽出、処理し、クラウドに取り込むかを示しています。

システム (特に分散システム) を構築する場合は、そのシステムを監視できるようにすることが重要です。 監視できるシステムでは:

  • 分散アプリケーションの正常性を包括的に把握できます。
  • システムの運用パフォーマンスを測定します。
  • 問題をすばやく解決できるように、エラーを特定して診断します。

分散トレース

このアーキテクチャでは、システムは一連のマイクロサービスです。 各マイクロサービスはそれぞれ単独で、さまざまな理由によってエラーになる可能性があります。 エラーが発生した場合は、トラブルシューティングを実行できるように、何が起こったかを把握することが重要です。 エンド ツー エンド トランザクションを分離し、アプリ スタック (サービスまたはマイクロサービスで構成) の流れに従っていくことが適切です。 この方法は、"分散トレース" と呼ばれます。

次のセクションでは、このアーキテクチャで分散トレースを設定する方法について説明します。 次の [Azure へのデプロイ] ボタンを選択して、インフラストラクチャと Azure 関数アプリをデプロイします。

注意

このアーキテクチャには内部 API がないため、API を呼び出す代わりに Azure ファイルが読み取られます。

Azure へのデプロイ

トレースとスパン

トランザクションは "トレース" によって表され、トレースはスパンのコレクションです。 たとえば、購入ボタンを選択して eコマースの Web サイトで注文を行うと、その後にいくつかの操作が実行されます。 次のような操作が考えられます。

  • API への POST 要求の送信。これによって "待機ページ" にリダイレクトされます。
  • コンテキスト情報を使用したログの書き込み。
  • 課金ページを要求する Web ベース ソフトウェアの外部呼び出し。

これらの各操作は、スパンの一部である場合があります。 トレースは、購入ボタンを選択したときに行われるすべての内容を記述したものです。

同様に、このアーキテクチャでは、データのクエリ Azure 関数で売上データの日々の取り込みの開始がトリガーされると、複数のスパンを含むトレースが作成されます。

  • トリガーの詳細を確認するためのスパン。
  • 内部 API に対してクエリを実行するためのスパン。
  • イベントを作成して Event Hubs に送信するためのスパン。

スパンには子スパンを設定できます。 たとえば、次の図では、データのクエリ Azure 関数をトレースとして示しています。

スパンとその子スパンで構成されるトレース全体を示す図。

  • sendMessages スパンは、splitToMessages と writeToEventHubs の 2 つの子スパンに分割されます。 sendMessages スパンには、メッセージを送信するためにこれら 2 つのサブ操作が必要です。

  • すべてのスパンはルート スパンの子です。

  • スパンを使用すると、データのクエリ Azure 関数のクエリ手順に関係する、すべての部分を簡単に記述できます。 各 Azure 関数は 1 つのトレースです。 そのため、エンドツーエンドのパススルーの Contoso のインジェスト システムは、3 つのトレース (つまり 3 つのAzure 関数) の結合です。 3 つのトレースとそのテレメトリを結合する際に、エンドツーエンドの工程を構築し、アーキテクチャのすべての部分を記述します。

トレーサーと W3C トレース コンテキスト

"トレーサー" は、コンテキスト情報を保持するオブジェクトです。 そのコンテキスト情報が、Azure 関数を介してデータ転送として伝達されることが理想的です。 この情報を伝達するために、OpenCensus 拡張機能では W3C トレース コンテキストを使用します。

ドキュメントに記載されているように、W3C トレース コンテキストは、"分散トレース シナリオを可能にするコンテキスト情報を伝達するための標準 HTTP ヘッダーと値の形式を定義する仕様" です。

システムのコンポーネント (関数など) では、トレースの親を読み取って呼び出しを行っている、前のコンポーネントのコンテキストを使用してトレーサーを作成できます。 トレースの形式は次のとおりです。

Traceparent: [version]-[traceId]-[parentId]-[traceFlags]

たとえば、traceparent = 00-0af7651916cd43dd8448eb211c80319c-b7ad6b7169203331-00 の場合

base16(version) = 00

base16(traceId) = 0af7651916cd43dd8448eb211c80319c

base16(parentId) = b7ad6b7169203331

base16(traceFlags) = 00

トレース ID と親 ID が最も重要なフィールドです。 トレース ID は、トレースのグローバル一意識別子です。 親 ID は、スパンのグローバル一意識別子です。 このスパンは、トレース ID で識別されるトレースの一部です。

詳細については、「Traceparent ヘッダー」を参照してください。

この記事の残りのセクションでは、base16(version) と base16(traceFlags) が 00 に設定されていることを想定しています。

OpenCensus 拡張機能を使用してトレーサーを作成する

Azure Functions に固有の OpenCensus 拡張機能を使用します。 他の場合 (Python Webapps など) に使用する可能性がある OpenCensus パッケージは使用しないでください。

Azure Functions には多くの入出力バインドが用意されており、バインドごとに異なる方法でトレースの親を埋め込みます。 このアーキテクチャでは、イベントとメッセージが使用されると、2 つの Azure 関数がトリガーされます。

2 つの関数をトリガーできるようにするための事前条件:

  1. コンテキスト (トレースの識別子と現在のスパンの識別子によって特徴付けられます) を、W3C トレース コンテキスト形式でトレースの親に埋め込む必要があります。 この埋め込みは、出力バインドの性質に依存します。 たとえば、このアーキテクチャでは、Event Hubs をメッセージング システムとして使用します。 トレースの親はバイトにエンコードされ、送信されたイベントに診断 ID プロパティとして埋め込まれます。こうすると、出力バインドで適切なトレース コンテキストが得られます。

    親と子でない 2 つのスパンもリンクできます。 分散トレースの場合、現在のスパンは次のトレースをポイントします。 リンクを作成すると、この関係が確立されます。

    埋め込みとリンクは、Azure Functions Worker パッケージを使用して管理します。

  2. エンドツーエンド フローの中ほどにある Azure 関数を使用して、渡されたトレースの親からコンテキスト情報を抽出します。 この手順では Azure Functions 用の OpenCensus 拡張機能を使用します。 各 Azure 関数のコードにこのプロセスを追加する代わりに、OpenCensus 拡張機能を使用して、関数アプリ レベルで事前呼び出しフックを実装します。

    事前呼び出しフックを使用して、以下を行います。

    • 前のスパンの情報を保持し、Azure 関数をトリガーする、スパン コンテキスト オブジェクトを作成します。 次のセクションで、この手順を視覚的に示した例を参照してください。
    • スパン コンテキストを含むトレーサーを作成し、トリガーされた Azure 関数用の新しいトレースを作成します。
    • トレーサーを Azure 関数実行コンテキストに挿入します。

    トレースが Application Insights に確実に表示されるように、configure メソッドを呼び出して、テレメトリをエクスポートする Azure エクスポーターを作成して構成する必要があります。

    この拡張機能はアプリ レベルであるため、このセクションの手順は、関数アプリ内のすべての Azure 関数に適用されます。

コードを理解して構造化する

このアーキテクチャでは、Azure 関数のコードはスパンを使用して構造化されています。 Python で、with ステートメントを使用して OpenCensus スパンを作成し、Azure 関数実行コンテキストに挿入されるトレーサーのスパン コンテキスト部分にアクセスします。 次の文字列は、現在のスパンとその親の詳細を示します。

    with context.tracer.span("nameSpan"):
        # DO SOMETHING WITHIN THAT SPAN

次のコードは、データのクエリ Azure 関数の詳細を示しています。

import datetime
import logging

import azure.functions as func
from opencensus.extension.azure.functions import OpenCensusExtension
from opencensus.trace import config_integration

OpenCensusExtension.configure()
config_integration.trace_integrations(['requests'])
config_integration.trace_integrations(['logging'])

def main(timer: func.TimerRequest, outputEventHubMessage: func.Out[str], context: func.Context) -> None:

    utc_timestamp = datetime.datetime.utcnow().replace(
        tzinfo=datetime.timezone.utc).isoformat()

    if timer.past_due:
        logging.info('The timer is past due!')

    logging.info(f"Query Data Azure Function triggered. Current tracecontext is:      {context.trace_context.Traceparent}")
    with context.tracer.span("queryExternalCatalog"):
        logging.info('querying the external catalog')
        content = {"key_content_1": "thisisavalue1"}
        content = json.dumps(content)

    with context.tracer.span("sendMessage"):
        logging.info('reading the external catalog')

        with context.tracer.span("splitToMessages"):
            # Do sthg
            logging.info('splitting to messages')

        with context.tracer.span("setMessages"): 
            logging.info('sending messages')
            outputEventHubMessage.set(content)

    logging.info('Python timer trigger function ran at %s', utc_timestamp)

このコードの重要点は次のとおりです。

  • OpenCensusExtension.configure 呼び出し。 この呼び出しは、関数アプリごとに 1 つの Azure 関数でのみ実行します。 このアクションを実行して、ログ、メトリック、トレースなどの Python テレメトリを Application Insights にエクスポートするように Azure エクスポーターを構成します。

  • OpenCensus requests および logging 統合 は、HTTP 呼び出しの要求モジュールとロギング モジュールからのテレメトリ コレクションを構成します。

  • 次の 5 つのスパンがあります。

    • 実行前にコンテキストに挿入されるトレーサーの一部であるルート スパン
    • queryExternalCatalog
    • sendMessage
    • splitToMessages (sendMessage の子)
    • setMessages (sendMessage の子)

トレーサーとスパン

次の図は、スパンが作成されるたびに、トレーサーのスパン コンテキストがどのように更新されるかを示しています。

関数のコード行を示す図。

前の図では:

  1. Azure 関数がトリガーされます。 トレースの親は、事前呼び出しフックを使用してトレーサー コンテキスト オブジェクトに挿入されます。このフックは、関数の実行前に Python ワーカーによって呼び出されます。
  2. Azure 関数が実行されますOpenCensusExtension.configure メソッドが呼び出され、Azure エクスポーターが初期化されて、Application Insights へのトレース書き込みが可能になります。

次の詳細では、このアーキテクチャでのトレーサーとスパンの関係について説明します。

  • Azure 関数コンテキストのトレーサー オブジェクトには、ルート スパンを記述する span_context フィールドが含まれています。
  • コードでスパンを作成するたびに、新しいグローバル一意識別子が作成され、実行コンテキストのトレーサー オブジェクトの span_context プロパティが更新されます。
  • span_context フィールドには、trace_id フィールドと id フィールドが含まれています。
  • trace_id は更新されませんが、id は生成された一意識別子に更新されます。
  • 前の図では、ルート スパンに queryExternalApi と sendMessage という 2 つの子スパンがあります。
    • queryExternalApi スパンと sendMessage スパンには、root_span_id とは異なる新しいスパン ID があります。
    • sendMessage スパンには、splitToMessages と setMessages という 2 つの子スパンがあります。 これらのスパン ID は、コンテキストのトレーサー オブジェクトの span_context フィールドで更新されます。
  • 子スパンとその親の間の関係をキャプチャするため、spans_list フィールドに、スパンの系列がリスト形式で表示されます。 splitToMessages スパンでは、spans_list フィールドに sendMessage (親スパン) と splitToMessages (現在のスパン) が含まれます。 この親子関係は、Azure 関数を実行する中で、一連の分離された操作をどのように作成するかを表します。

コンテキスト フィールドを使用して関数を連結する

一連の操作が 1 つの Azure 関数として整理されたので、それを次の Azure 関数によって実行される後続の操作に連結できます。

関数がどのように連結されるかを示す図。

前の図では:

  • setMessages スパンは、データのクエリ Azure 関数の最後のスパンです。 スパン内のコードから Event Hubs にメッセージが送信され、後続の Azure 関数がトリガーされます。 コンテキスト トレーサー オブジェクトの span_context フィールドには、このスパンに関連する情報が含まれています。 この情報は、データのクエリ Azure 関数のコンテキストに関連付けられます。
  • Azure Functions Worker で、バイト エンコードされた Diagnostic-Id が送信されたイベントのプロパティに追加され、後続の Azure 関数のルート スパンへのリンクが作成されます。
  • 後続のデータの処理 Azure 関数の事前呼び出しフックで Diagnostic-Id が読み取られ、コンテキストが設定されます。これで Azure 関数が連結され、これらの関数は個別に実行されます。

データの処理 Azure 関数から Service Bus キューにメッセージが送信されるときに、コンテキストが同じ方法で渡されます。

監視構成が整ったら、Application Insights の機能を使用して、エンドツーエンドのトランザクションに対するクエリおよびそれらのトランザクションの視覚化を行います。

テレメトリの種類

Application Insights では、いくつかの種類のテレメトリを使用できます。 このアーキテクチャのコードを使用して、次のテレメトリを生成します。

  • 要求テレメトリは、HTTP を呼び出すとき、または Azure 関数をトリガーするときに生成されます。 Contoso のシステムへのエントリには、要求テレメトリを生成するデータのクエリ Azure 関数用のタイマー トリガーがあります。
  • 依存関係テレメトリは、Azure サービスまたは外部サービスを呼び出すときに生成されます。 Azure 関数では、Event Hubs へのイベントの書き込み時に依存関係テレメトリを生成します。
  • トレース テレメトリは、Azure Functions ランタイムと Azure Functions によって生成されたログから生成されます。 Azure 関数内のログからトレース テレメトリが生成されます。

共同作成者

この記事は、Microsoft によって保守されています。 当初の寄稿者は以下のとおりです。

プリンシパル作成者:

その他の共同作成者:

パブリックでない LinkedIn プロファイルを表示するには、LinkedIn にサインインします。

次のステップ