はじめに
Azure App Serviceに展開したPython(Flask)プログラムをOpenTelemetryで自動計測してみたので、その手順などを紹介したいと思います。
所謂トレースの取得はO11yメーカの独自APM AgentやOpenTelemetryで取得することができます。もちろんElasticも独自Agentを持っています。
https://www.elastic.co/docs/solutions/observability/apm/apm-agents/
OpenTelemetryはデファクトスタンダードになりつつあり、ElasticもElastic Distributionをリリースしています(EDOT)。
https://www.elastic.co/docs/reference/opentelemetry/edot-sdks
今回はOpenTelemetryを利用して、ソースコードには一切手を入れない自動計測(auto instrument)の使い方にこだわってみます。
ソースコードには手を入れないですが、流石にビルド部分では指定する必要があります。
2025/12/01現在の情報です
環境
- 開発環境
- Mac Sequoia v15.7.1
- 一部スクリプトがzshで動くことを前提としているところがあります
- Azure App Service
- IaaSでやると出来て当たり前になってしまうのでApp Serviceを選んでます
- Managed Redis
- DBはどう用意してもいいですが、楽なのでManagedを選んでます
- Redisへの接続部分がManaged Redisを前提にしてます(redis.cache.windows.net)
- Elastic
- v9.2.0
デモアプリケーション
FlaskからRedisへアクセスするという非常に単純なものです。V1とV2を切り替えられるようにしてあり、V1は常に成功するけど、V2はバグが入っててRedisへのアクセスが遅延する、という感じです。
今回は生成AIがどこまで出来るのか見てみたかったので、ほとんどのコードは生成AIが作ったものです。一部どうやっても修正できなかったものだけ手で直しました。
「オブザバビリティのデモアプリを作りたい。Azure App Service上で動作をしてOpenTelemetryによる自動計測を行い、Elasticsearchに送信する」という感じからスタートして、チャットを繰り返しながら作りました。
勿論動かしてみたらエラーは結構出ました。azコマンドも結構間違ってました。そのエラーメッセージを生成AIに入れて訂正させる、というのを結構繰り返してます。
Redis
Managed Redis
redis.shを実行すると.envから環境変数を読み込んで実行します
最後にアクセスキーが表示されますので、それを.envに記載します。
firewall
ひょっとするとManged Redisがネットワーク的にアクセスできないかも知れません。
その場合はfirewall.shを実施して、webappのIPアドレスを追記します。
Managed Redisでない場合
Redis Hostは以下の部分で指定しています
REDIS_HOST = os.environ.get("REDIS_NAME") + ".redis.cache.windows.net"
Redisへのアクセスはこの部分です
# Redis接続設定 (変更なし)
redis_client = None
if REDIS_HOST:
try:
logger.info("Connecting to Redis at %s:%d", REDIS_HOST, REDIS_PORT)
# Note: socket_connect_timeout=2 added for robustness
print("REDIS_HOST: ", REDIS_HOST)
redis_client = redis.Redis(host=REDIS_HOST, port=REDIS_PORT, db=0, socket_connect_timeout=2, password=REDIS_PASSWORD, ssl=True)
redis_client.ping()
logger.info("Redis connection successful!")
except Exception as e:
logger.error("Redis init failed: %s", e)
else:
logger.warning("REDIS_HOST not set. Redis client not initialized.")
Managed Redisでない場合は適宜変更してください。
手順
こちらのリポジトリをクローンしてcreate_から始まるシェルスクリプトを順に起動していけば自動的に動くようになってます。
https://github.com/legacyworld/app_service_demo/
Elasticクラスタ
手元のPCとかで作るとApp Serviceから送るのが結構難しいと思います。こちらから作ってください。
https://cloud.elastic.co/registration
HostedとServerlessがありますが、Serverlessは東京リージョンはまだAWSにしかありませんので、今回はhostedをベースに話を進めます
作成する際にObservabilityを選ぶと今回の用途には何かと便利です。
このキャプチャだと真ん中です。

.env
.env_sampleを編集してから.envに名前を変更します。
- RESOURCE_GROUP
- これはそのままですね
- REGION
- これもそのまま
- WEB_APP_NAME
- webappsの名前なのでユニークなものにします
- APP_PLAN
- これもユニーク
- SLOW_RATE,ERROR_RATE
- 特に変えなくても大丈夫です
- REDIS_NAME
- Managed Redisを作った場合はその名前
- 後でプログラム部分で
REDIS_NAME + redis.cache.windows.netをREDIS_HOSTとします
- REDIS_PASSWORD
- Managed RedisのAccess Key
- OTEL_RESOURCE_ATTRIBUTES
- 適当にこんな感じで入れましょう。ここの情報は後ほどElasticの画面で表示されます
- service.name=Azure_demo,service.version=1.0.0,deployment.environment=dev
- 適当にこんな感じで入れましょう。ここの情報は後ほどElasticの画面で表示されます
- OTEL_EXPORTER_OTLP_ENDPOINT / OTEL_EXPORTER_OTLP_HEADERS
- これはElastic Cloudから取ってきます
Application -> OpenTelemetryとクリック

少しスクロールダウンしてConfigure Agentの部分を見ます

OTEL_EXPORTER_OTLP_ENDPOINT: serverUrlをそのまま入れます。ダブルクォーテーションでくくらないです
OTEL_EXPORTER_OTLP_HEADERS: Bearer%20<sercretToken>
例えばsecretTokenがhogehogeだとするとBearer%20hogehogeとします。これもダブルクォーテーションでくくらないです
Serverlessでやっている場合はBearerではなくApiKeyとなってます。スペースをそのままにするとエラーになるので必ず%20に変更します。
webapp作成
create_app.shを実行します。
勝手に.envを読んできて実行します。
最後の部分で認証方法を変えています。ここはそれぞれの環境に応じて変えてください。
az resource update --resource-group "$RESOURCE_GROUP" --name scm --namespace Microsoft.Web --resource-type basicPublishingCredentialsPolicies --parent sites/"$WEB_APP_NAME" --set properties.allow=true
az resource update --resource-group "$RESOURCE_GROUP" --name ftp --namespace Microsoft.Web --resource-type basicPublishingCredentialsPolicies --parent sites/"$WEB_APP_NAME" --set properties.allow=true
環境変数登録
create_variable.shを実行
起動スクリプト登録
create_gunicorn.shを実行
opentelemetry.shが起動時に呼ばれて、opentelemetry-instrumentが実行されて自動計測が行われます。
Git登録
create_git.shを実行すると、リモートリポジトリ登録までします。
既存の.gitは消します。
実行するとgit push用のユーザ名とパスワードが表示されます。
その後以下を実行します。(勿論hogeは何でも良いです)
git add *
git commit -m "hoge"
最後にgit pushですが、Basic認証を変更していなければ以下のようにusername/password入力が必要です。
git push azure main
Username for 'https://hogehoge.scm.azurewebsites.net': $app-hogehoge
Password for 'https://%24hogehoge@hogehoge.scm.azurewebsites.net':
環境にもよりますが、以下の画面でUsername/passwordが表示されます。
デプロイセンター->ローカルGitまたはFTPSの視覚情報

これが終わるとDeploymentが始まります。手元の環境では立ち上がるまでに10分ぐらいかかりました。
ログストリームが以下のようになっていればOKです
2025-11-06T07:07:34.3746884Z [2025-11-06 07:07:34 +0000] [2169] [INFO] Starting gunicorn 23.0.0
2025-11-06T07:07:34.3945803Z [2025-11-06 07:07:34 +0000] [2169] [INFO] Listening at: http://0.0.0.0:8000 (2169)
2025-11-06T07:07:34.3946705Z [2025-11-06 07:07:34 +0000] [2169] [INFO] Using worker: sync
2025-11-06T07:07:34.493297Z [2025-11-06 07:07:34 +0000] [2184] [INFO] Booting worker with pid: 2184
2025-11-06T07:07:34.5421729Z Connecting to Redis at takeoRedis.redis.cache.windows.net:6380
2025-11-06T07:07:34.5422714Z 2025-11-06 07:07:34,535 INFO [app] [app.py:34] [trace_id=0 span_id=0 resource.service.name=Azure_demo trace_sampled=False] - Connecting to Redis at takeoRedis.redis.cache.windows.net:6380
2025-11-06T07:07:34.6910992Z Redis connection successful!
トレース
特にエラーが出ていなければアプリにアクセスしてトレースを発生させます。
アプリ作動
ブラウザで
https://<webapp名>.azurewebsites.net/
アドレスはApp Serviceの既定のドメインにも記載があります。
最初はv1で全て成功するようになっており、2秒に一回/api/statusを叩きます。
これがトレースでどう見えるのか、Elastic側に移ってみましょう
Otel Trace
左のメニューからApplicationをクリックして、Azure_demo(OTEL_RESOURCE_ATTRIBUTESでservice.nameに指定した値)をクリックします。

このトランザクションのいろんな値が表示されますが、スクロールダウンしてGET /api/statusをクリックします。

移った先のページで一番下までスクロールすると遅延の分布と、とある時点でのトレースが表示されます(表示されているサンプルは遅延の分布でCurrent Sampleと示されています)

Latency Distributionを見るとほとんどが100ms以内に収まっています。
表示されているサンプルのトレースでは全体で10msかかってRedisへのSETで1.9msかかっていることがわかります。
Service Mapをクリックするとコンポーネント間の関係が表示されます。

今回はLog Correlationもしているので、このトレースに関係するログも表示されます

ソースコードのapp.pyを見るとわかりますが、これらのトレースの情報に関わることは何も書いていません。自動計測でここまで詳細な情報を取ることが出来るようになりました。
Latency Correlation
これはOpenTelemetryはあまり関係ないですが、新しいバージョンをリリースしたときに何か問題が起きた場合に使える機能のご紹介です。
バージョンアップシミュレーション
するといままでにはなかった背景が黄色や赤になった遅延が発生しています。しばらくこのまま置いておきましょう。
数分待ったらまた先程のTraceの画面に行きます。
Latency Distributionを見ると先ほどとは違い赤い部分があり、そこは遅延が大きくなっています。分布も大きく変わり数百msのものが増えています。これはv2でわざと遅延を入れているからです。
赤い部分をマウスで選びます(クリックではなく時間の範囲をドラッグして選択)

これはソースコードのこの部分に当たります。
for i in range(REDIS_LOAD_COUNT):
# ★ 修正: f-stringにREDIS_KEY_PREFIXとiを指定 ★
key = f"{REDIS_KEY_PREFIX}:{i}"
redis_client.set(key, "load_data", ex=1)
forループで毎回Redisに接続しているのでこのようなことになります。普通はpipeを使うのではないでしょうか。
Latency Correlation
Latency DistributionのところのLatency Correlationをクリックすると、遅延の大きさが何と相関があるのかを調べます。
ちなみに相関関係は単に外から見たときに「関係がありそう」というもので因果関係とは異なります。
例えば「アイスクリームの売上と溺死者数」は正の相関関係(アイスクリームの売上が上がると溺死者数が増えるように見える)が強く出ますが、単に気温が高くなると両方増えるというだけで、気温という交絡因子を見落としているだけです。
生成AIで「相関関係は高いけど因果関係はまったくない例」と聞いてみるとたくさんおもしろい例を出してくれます。
ですので、このLatency Correlationで高い数値が出たから原因というわけではありませんが、一つのきっかけにはなる可能性があります。
今回の場合は以下のように出るかもしれません。v1とv2の実行時間とか、v1でも遅い場合が結構あるとか、状況によってはうまく相関関係が見えないこともあります。
以下は一例です。原因は違うところにあるかも知れませんが、とりあえず新しいバージョンを疑うきっかけにはなります。

まとめ
Azure App ServiceのようにIaaSではないサービスではいろいろ制限があります。自動計測を行うには今回説明したようにちょっとした手間がかかりますが、その効果は絶大です。
自動計測にしておけば変更も容易ですし、オープンな規格のOpenTelemetryを存分に使うことが出来ます。
ElasticはOpenTelemetryへの投資を続けており、O11yの1つの柱であるトレース情報を簡単に取得できるようにしていきます。
この機会にぜひElasticでOpenTelemetryを体感してみてください。



