
こんにちは。クラウド事業部の遠見です。
- アイキャッチ画像はイメージです。画像内のロゴや名称は各社の商標または登録商標です。
前回に続いて、Microsoft Learn(以下、MS Learn)のラーニングコースでオブザーバビリティを学習しています。
今回は、以下のコースで実際に環境を構築して、Python FlaskアプリケーションにOpenTelemetryを実装し、Azure Application Insightsを使ってパフォーマンスのボトルネックを特定するハンズオンを行いました。
この記事では、ハンズオンの手順に沿ってアプリを実行した様子や、Application Insightsなどのツールでどのように観測データ(テレメトリ)が確認できるのかをまとめたいと思います。
目次
- 目次
- この記事の対象者
- 検証日時
- アプリの実行とOpenTelemetryの実装
- Python実装コードの詳しい解説
- ボトルネックの特定(トラブルシューティング)
- その他のApplication Insightsの機能を見てみる
- 検証にかかったコストについて
- まとめ
- お知らせ
この記事の対象者
- Azureでオブザーバビリティを簡単に触れてみたい人
- PythonアプリにOpenTelemetryを実装し、Application Insightsへ連携する方法を学びたい人
- 分散トレーシングを利用してパフォーマンスのボトルネックを特定する手法を体験したい人
検証日時
2026年3月6日の情報となります。
アプリの実行とOpenTelemetryの実装
今回のハンズオンでは、ドキュメント処理パイプライン(検証、エンリッチ、保存の3ステージ)を実行するPython Flask Webアプリケーションを構築しました。
手順にはありませんが、まずはAzureにリソースグループを作っておく必要があります。

そのほかは、特に問題なく手順通りに構築できたと思います。
アプリケーションには、Azure Monitor OpenTelemetry Distro パッケージを導入します。
これにより、わずかな構成(configure_azure_monitor() の呼び出しなど)でOpenTelemetry SDKとAzure Monitor エクスポーターが設定され、自動的にテレメトリが送信されるようになります。
アプリを起動し、ブラウザで http://localhost:5000 にアクセスします。

画面左下の「Check Telemetry Status」をクリックすると、ステータスが ACTIVE となり、service.name が document-pipeline-app として認識され、Application Insightsへのエクスポートが正しく構成されていることが確認できます。

続いて「Process Documents」を実行すると、5つのドキュメントがパイプラインで処理されます。
ここで注目すべきは、DOC-0003 と DOC-0005 の「Enrich」処理に著しく時間がかかっており、SLOW タグが付いていることです。
今回のハンズオンでは、外部サービスのボトルネックをシミュレートするために、意図的にこの2つのドキュメントの処理に1.5秒から3秒の遅延が発生するようにコードが記述されています。

Python実装コードの詳しい解説
コードにどのような処理を組み込んでこの観測を実現しているのか、重要なポイントを解説します。
今回の実装の特徴は、親スパン(全体処理)と子スパン(各ステージの処理)の階層構造を作り、そこに検索用の属性(タグ)を付与している点です。
① テレメトリの初期設定
def configure_telemetry(): # ...前略... from azure.monitor.opentelemetry import configure_azure_monitor credential = DefaultAzureCredential(exclude_managed_identity_credential=True) configure_azure_monitor( connection_string=connection_string, credential=credential, )
ここでは configure_azure_monitor() という関数を呼び出しています。
これは Azure Monitor OpenTelemetry Distro パッケージの機能で、この関数を1つ呼び出すだけで、OpenTelemetry SDKとAzure Monitorへのエクスポーター(データ送信機能)の両方が自動的に構成されます。
手動で複雑な設定をする手間が省けるのが利点です。
② 親スパンの作成
def process_documents(doc_count): tracer = get_tracer() with tracer.start_as_current_span("process-documents") as parent_span: parent_span.set_attribute("document.count", doc_count) # ...各ドキュメントの処理ループ...
tracer.start_as_current_span("process-documents") を使用して、複数ドキュメントのバッチ処理全体を囲む「親スパン」を作成しています。
さらにset_attributeを使って、処理するドキュメントの総数 (document.count) などを属性として付与し、後から検索できるようにしています。
③ 子スパンの作成と遅延のシミュレート
def enrich_document(doc_id): tracer = get_tracer() with tracer.start_as_current_span("enrich-document") as span: span.set_attribute("document.id", doc_id) span.set_attribute("document.stage", "enrich") # ボトルネックのシミュレート if doc_id in ("DOC-0003", "DOC-0005"): delay = random.uniform(1.5, 3.0) span.set_attribute("enrichment.slow", True) else: delay = random.uniform(0.05, 0.2) span.set_attribute("enrichment.slow", False) time.sleep(delay)
ここでも start_as_current_span を使っていますが、親スパンの処理内で呼び出されるため、自動的に「子スパン(階層の1つ下)」として紐づけられます。
また、DOC-0003 と DOC-0005 に対して意図的に遅延を発生させています。
重要なのは、遅延が発生したスパンには span.set_attribute("enrichment.slow", True) としてカスタム属性のフラグを立てている点です。
ボトルネックの特定(トラブルシューティング)
それでは、Azureポータルの Application Insights に移動し、送られてきたテレメトリからこの遅延(ボトルネック)をどのように特定できるかを見ていきます。
トランザクションとエンドツーエンドのトレース
Application Insightsの「概要」画面から、「検索」機能を使って過去のデータを表示します。

トレースの一覧を見ると、処理にかかった時間が可視化されています。

該当の POST /process-documents のトランザクションを選択すると、エンドツーエンド トランザクションの詳細 が表示されます。
ここでは、親スパン(process-documents)と子スパン(validate、enrich、store)の階層構造がガントチャートのように表示され、どの処理に時間がかかっているかが一目でわかります。
明らかに enrich-document のスパンがボトルネックになっていることが視覚的に確認できます。

KQL (Kusto Query Language) を使ったログ分析
さらに詳細に分析するために、「ログ」メニューからKQLを使用してクエリを実行してみます。
以下のクエリで、カスタムスパンとコード内で付与したスパン属性(document.id、document.stage、enrichment.slow)を抽出します。

カスタム属性を展開して抽出するクエリ
dependencies | where timestamp > ago(1h) | project timestamp, name, duration, documentId = customDimensions["document.id"], stage = customDimensions["document.stage"], slow = customDimensions["enrichment.slow"] | order by timestamp desc
customDimensions["document.id"] のように指定することで、Pythonコード内の span.set_attribute() で設定した値を独立した列として取り出しています。
これにより、「どのドキュメントIDの処理か」が一目でわかる表を作成できます。
さらに、遅延が発生した(slow == True)エンリッチメント処理と、通常処理(slow == False)の平均所要時間を比較するクエリも実行しました。

遅延の有無で平均処理時間を比較するクエリ
dependencies | where timestamp > ago(1h) and name == "enrich-document" | extend slow = tostring(customDimensions["enrichment.slow"]) | summarize avgDuration = round(avg(duration), 0) by slow
extend 句で enrichment.slow を新しい列として定義し、summarize 句で「slow列の値(TrueかFalseか)」ごとに、処理時間(duration)の平均値を計算しています。
結果を見ると、True の場合は平均で1,500ミリ秒(画像では2,770ミリ秒)以上かかっているのに対し、False の場合は200ミリ秒以下(画像では97ミリ秒)で完了していることが明確に数値として確認できました。
スパン属性を使うことで、影響を受けたドキュメントを素早く特定できるのが非常に便利です。
その他のApplication Insightsの機能を見てみる
ハンズオンのメイン手順以外にも、Application Insightsにはシステムを俯瞰できる面白い機能がありました。
アプリケーション マップ
システムのコンポーネント間の関係や、それぞれのパフォーマンス(平均応答時間や呼び出し回数)を視覚的なマップとして確認できる機能です。

パフォーマンスとリソース ビジュアライザー
操作ごとの応答時間分布を確認できる「パフォーマンス」画面や、関連するAzureリソースの依存関係を図示してくれる「リソース ビジュアライザー」も、運用監視において強力なツールになりそうです。

使用とコストの見積もり
Application Insightsの左側メニューにある「使用とコストの見積もり」では、Log Analyticsワークスペースに取り込まれるデータ量や、それに伴う推定コストを確認できます。
データのサンプリング設定を行ったり、1日のデータボリュームの上限(キャップ)を設定したりして、意図しないコストの増加を防ぐ工夫もここから設定できるようです。

検証にかかったコストについて
クラウドサービスを使ってハンズオンを行う際に一番気になるのが「コスト」ですよね。
今回のハンズオン検証後に、Azureの「コスト分析」画面から実際の累積コストを確認してみたところ、以下の画像のようにAzure MonitorやLog Analyticsのコストは ¥0.00 となっていました。

無料でしっかりオブザーバビリティの体験ができるのは嬉しいポイントですね。
まとめ
今回は「OpenTelemetryによるアプリのインストルメント化とAzure Application Insights」をテーマに、一連のハンズオンに取り組んでみました。
コードにカスタムスパンや属性(タグ)を数行追加するだけで、Application Insights上で「どのドキュメントIDの」「どの処理ステージが」遅いのかを、視覚的にもクエリ(KQL)的にも簡単に特定できることが体験できました。
オープンソース標準であるOpenTelemetryと、Azureの強力な分析機能が組み合わさることで、クラウドネイティブなアプリの監視が非常に効率的になることを実感できました。
まだまだ使いこなせていない機能もあるので、今回の経験を足掛かりに、さらに深掘りしていきたいと思います。
お知らせ
当社では、お客様と共にDX実現と内製化を推進する「クラウドネイティブ内製化支援サービス for Microsoft Azure」を提供しています。
www.ap-com.co.jp
また、Datadogの導入支援から運用サポートまでをトータルでご支援するサービスを提供しています。
初期設計・エージェント展開・モニタリング設定・ダッシュボード構築まで、お客様のニーズに合わせた支援が可能です。
「自社だけでの導入が不安」「もっと効率的に監視環境を整えたい」という方は、ぜひお気軽にご相談ください。
www.ap-com.co.jp
一緒に働いていただける仲間も募集中です!
今年もまだまだ組織規模拡大中なので、ご興味持っていただけましたらぜひお声がけください。
www.ap-com.co.jp