こんにちは!マイクロサービス化が進んだモダンなシステムで、こんな悪夢を見たことはありませんか?
> 「ユーザーから『エラー画面が出た』と連絡があった。ログを見に行くと、API Gatewayでエラーログが出ている。しかし、原因を探るために下流のマイクロサービスへ行くと、ログがバラバラで、どのリクエストがどこにつながっているのか分からない……! 結局、タイムスタンプを頼りに犯人探しで半日溶けた」
……うん、エンジニアなら誰もが一度は通る「分散システムの迷宮」ですね。
モノリスであれば一箇所を見れば済んだエラー調査も、サービスが10個、20個と分かれていくと、エラーの「犯人(原因)」がどこに隠れているのか見えなくなります。この迷宮から私たちを救い出してくれるのが、Sentryの「分散トレーシング(Distributed Tracing)」であり、その心臓部である「Trace Propagation(トレース伝播)」です。
今回は、このトレース伝播の仕組みを、初心者の方にもスッと腹落ちするように、かつ現場で即座に使える実践的な知識を交えて優しく解説していきます。これをマスターすれば、カオスなマイクロサービスのエラー追跡が劇的に楽になりますよ!
—
1. マイクロサービスにおけるエラー追跡の難しさと分散トレーシングの必要性
なぜエラーは迷子になるのか?
マイクロサービス最大の美徳は「関心の分離」ですが、運用者にとっては「因果関係の分断」でもあります。
例えば、ユーザーが「注文ボタン」を押したとします。
1. API Gateway がリクエストを受け取る(Service A)
2. 認証サービス でトークンを検証する(Service B)
3. 決済サービス でクレジットカードを引く(Service C)
4. 在庫サービス で商品を減らす(Service D)
ここで、Service D(在庫)のデータベースでエラーが発生したとしましょう。
Sentryを導入していれば、Service D単体のSentryダッシュボードには「在庫データベースエラー」が記録されます。しかし、「一体どのユーザーの、どのアクションが、どのルートを通ってこのエラーを引き起こしたのか?」という全体像は、Service D単体では見えません。
分散トレーシングの正体
これを解決するのが分散トレーシングです。
分散トレーシングとは、システムの間を旅する1つのリクエスト(HTTPリクエストなど)に「旅券(トレースID)」を持たせ、すべてのサービスがその旅券にスタンプを押しながら処理を進める仕組みです。
Sentryは、この旅券の管理を驚くほど簡単に、しかも自動で行ってくれます。その魔法の裏側を覗いてみましょう。
—
2. HTTPヘッダーで実現する!「Trace Propagation」の仕組み
Sentryが異なるマイクロサービス間(しかも、Node.jsとPythonが混ざっていても!)でトレースをつなげられる理由、それはHTTPヘッダーを使った情報の受け渡し(Trace Propagation)にあります。
リクエストがサービス間を移動するとき、SentryはHTTPリクエストのヘッダーに特定の情報をこっそり混ぜ込んでいます。それが主に以下の2つのヘッダーです。
1. `sentry-trace`
2. `baggage`
① `sentry-trace` の中身
このヘッダーには、主に以下の3つの要素がカンマ区切り(またはハイフン区切り)で入っています。
- Trace ID(32文字のHEX): リクエスト全体を通じて不変のID。これが「旅券番号」です。
- Span ID(16文字のHEX): 各サービス内での処理の単位(スパン)を表すID。
- Sampled(0か1): このリクエストのデータをSentryに送信するかどうか(サンプリングフラグ)。
実際のHTTPヘッダーのイメージはこんな感じです:
sentry-trace: 4bf92f3577b34da6a3ce929d0e0e4736-00f067aa0ba902b7-1
② `baggage` の中身
`baggage` はW3C Trace Context標準に基づき、分散トレーシング全体で引き回したい「メタデータ(ユーザーID、環境名、トランザクション名など)」をkey=value形式で伝播させます。
baggage: sentry-environment=production,sentry-release=1.0.2,sentry-trace_id=4bf92f3577b34da6a3ce929d0e0e4736
伝播の流れ
1. Client / API Gateway からリクエストが発生すると、Sentry SDKが自動で `sentry-trace` と `baggage` を生成し、HTTPリクエストのヘッダーに付与します。
2. 下流のマイクロサービスがそのリクエストを受け取ります。
3. 下流サービスのSentry SDKが自動的にHTTPヘッダーから `sentry-trace` を読み取り、「おっ、親のTrace IDはこれだな」と認識して、自身のトレースをその親にぶら下げます(Parent-Child関係の構築)。
これによって、Sentryのダッシュボード上で「API Gateway → 認証 → 決済 → 在庫」という一本のきれいなウォーターフォール図(タイムライン)としてエラーや遅延が可視化されるのです。
—
3. 複数言語が混在するシステムでのSentry設定とトラブルシューティング
「うちのシステムは、フロントがNext.js、APIがGo、裏のバッチやAI処理がPythonなんだよね……」
安心してください。Sentryは主要なプログラミング言語をほぼ網羅しており、W3C標準のHTTPヘッダーを解釈するため、言語が混ざっていてもトレースは綺麗につながります。
ここでは、よくある構成として 「Node.js (Express)」から「Python (Flask/FastAPI)」へリクエストを飛ばすシナリオ での具体的な設定手順を見ていきましょう。
—
Step 1: 上流サービス(Node.js / Express)の設定
まずはリクエストの起点となるNode.js側です。Sentryの初期化時に `tracing`(パフォーマンストレーシング)を有効にします。
// app.js (Node.js Express)
const Sentry = require(“@sentry/node”);
const express = require(“express”);
const axios = require(“axios”);
// 1. Sentryの初期化
Sentry.init({
dsn: “https://your-public-dsn@o0.ingest.sentry.io/0”,
// トレーシングのサンプルレート(本番では0.1〜1.0の間で調整)
tracesSampleRate: 1.0,
});
const app = express();
// 2. Sentryのリクエストハンドラーを最初に配置
app.use(Sentry.Handlers.requestHandler());
app.use(Sentry.Handlers.tracingHandler());
app.get(“/checkout”, async (req, res) => {
try {
// 下流のPythonサービスへリクエストを送信
// axiosなどの一般的なHTTPクライアントなら、Sentryが自動でヘッダー(sentry-trace)を注入します!
const response = await axios.get(“http://payment-service.internal/charge”);
res.send(“Checkout success: ” + response.data);
} catch (error) {
// エラーは自動キャッチされますが、明示的にSentryに送ることも可能
Sentry.captureException(error);
res.status(500).send(“Checkout failed”);
}
});
// エラーハンドラーの配置
app.use(Sentry.Handlers.errorHandler());
app.listen(3000);
> 💡 先輩からのアドバイス:
> Axiosやfetchなどの主要なHTTPクライアントを使っていれば、Sentryのインテグレーションが自動的にHTTPヘッダーへ `sentry-trace` を仕込んでくれます。特別なコードを書く必要はありません。
—
Step 2: 下流サービス(Python / FastAPI)の設定
次に、リクエストを受け取るPython側です。こちらもSentry SDKを初期化するだけで準備完了です。
main.py (Python FastAPI)
import sentry_sdk
from fastapi import FastAPI, HTTPException
from sentry_sdk.integrations.fastapi import FastApiIntegration
1. Sentryの初期化
sentry_sdk.init(
dsn=”https://your-public-dsn@o0.ingest.sentry.io/0″,
traces_sample_rate=1.0,
integrations=[FastApiIntegration()],
)
app = FastAPI()
@app.get(“/charge”)
def charge_card():
# ここで意図的なエラーを発生させてみる
try:
raise ValueError(“Payment Gateway Timeout!”)
except Exception as e:
# Sentryが自動的に上流から届いた sentry-trace を読み取り、
# Node.js側のトレースとこのエラーを紐付けます!
sentry_sdk.capture_exception(e)
raise HTTPException(status_code=500, detail=str(e))
これで設定は完了です!Node.jsのエンドポイント `/checkout` を叩き、Python側のエラーが発火すると、Sentryのダッシュボードには次のような世界が広がります。
1. トレース一覧に `/checkout` のトランザクションが現れる。
2. その詳細を開くと、Node.jsの処理のあとに、HTTP通信を挟んでPythonの `/charge` の処理がズラッと並んでいる。
3. Python側で起きた `ValueError` が、どのユーザーの、どのリクエストフローで起きたものか一目瞭然!
—
トラブルシューティング:よくある「トレースが途切れる」原因と対策
「設定したのに、Sentryの画面でトレースが分断されて別々のエラーとして表示される……」
現場で最も多いトラブルとその対策をまとめました。
1. 内製プロキシやAPI Gateway、ロードバランサーでヘッダーが消されている
- 原因: Nginx、AWS API Gateway、K8s Ingressなどの設定で、カスタムHTTPヘッダー(`sentry-trace` や `baggage`)がホワイトリストに入っていらず、転送時にドロップされている。
- 対策: プロキシやAPI Gatewayの設定を確認し、`sentry-trace` および `baggage` ヘッダーをバックエンドへパススルーするように設定してください。
2. 非同期処理やキューイング(SQS, RabbitMQ, Redisなど)を挟んでいる
- 原因: HTTPリクエストではなく、メッセージキューを挟むと、自動的なヘッダー伝播が途切れます。キューのメッセージにトレース情報が含まれていないためです。
- 対策: キューにメッセージを積む際、現在のSentryスパンから `sentry-trace` の文字列を取り出し、メッセージのペイロード(カスタムヘッダーやメタデータ)に手動で含めます。コンシューマー側(受け取り側)でそれを読み取り、`sentry_sdk.continue_trace()` などの関数に渡してトレースを再開させます。
Pythonでキューから取り出した際にトレースを継続する例
from sentry_sdk import start_transaction, continue_trace
メッセージから sentry-trace の値を取り出したと仮定
incoming_sentry_trace = message.headers.get(“sentry-trace”)
incoming_baggage = message.headers.get(“baggage”)
トレースコンテキストを復元して処理を開始
transaction_data = continue_trace(
{‘sentry-trace’: incoming_sentry_trace, ‘baggage’: incoming_baggage}
)
with start_transaction(transaction=transaction_data, name=”queue.process”):
# ここでバックグラウンド処理を実行
process_message(message)
—
まとめ
今回は、Sentryの「Trace Propagation(トレース伝播)」について、その仕組みから多言語環境での実装、トラブルシューティングまでを解説しました。
- 分散トレーシングの本質は、リクエストの旅券番号(`sentry-trace`)をサービス間でパスし合うこと。
- SentryのSDKは、主要なHTTPクライアントであれば自動でヘッダーの付与と読み取りを行ってくれる。
- プロキシでのヘッダー消去や、メッセージキューを挟む場合の「手動伝播」にだけ注意すれば、カオスなマイクロサービスでも一瞬でエラーの原因箇所を特定できる。
「あのエラー、どこで起きているんだっけ……」とログの海をさまよう日々とは、今日でお別れです。
Sentryの強力な分散トレーシングを味方につけて、自信を持ってモダンなマイクロサービスを運用していきましょう!