可観測性が本当に必要になるのは、ダッシュボードを冷静に見ているときではありません。ユーザーが「チェックアウトが遅い」と書き込んだとき、エラー グラフは正常に見え、ログには切断されたメッセージの行が見つかるだけです。
OpenTelemetry はその瞬間を避けるために作成されました。グラフィックスを増やすためではなく、部分を接続するために作成されました。リクエストは API に入り、データベースを呼び出し、外部プロバイダーを経由し、キューに入れられたジョブをポストし、その後 3 つのサービスで失敗する可能性があります。分散トレースを使用しない場合、そのストーリーを手作業で再構築することになります。 OpenTelemetry があれば、少なくとも地図は手に入ります。
重要なのは trace ではなく、ストーリーです
trace は span のシーケンスです。そう言うと寒く聞こえますね。実際には、それぞれの span は、POST /checkout、SELECT inventory、call payment provider、publish order.created という物語の一部です。
実際の質問に答え始めると価値が生まれます。
- どの外部サービスが遅くなっているのか?
- エラーは特定のバージョンから発生しているのでしょうか?
- 問題は全員に影響しますか、それとも 1 人のテナントだけに影響しますか?
- 再試行はタイムアウトを隠しますか?
- 非同期ジョブが開始されましたが、別の場所で終了しましたか?
これらの質問は、急いでconsole.log を投げても解決できません。実際、緊急時に追加されたログは、今日は役立ちますが、明日にはノイズになることがよくあります。
これをアプリに組み込むにはどうすればよいでしょうか Node.js
最も健全な設定はシンプルです。アプリがテレメトリを生成し、Collector がそれを送信する場所を決定します。
Node.js app -> OpenTelemetry Collector -> backend di observability
なぜベンダーに直接輸出しないのでしょうか?なぜなら、最初は高速なように見えますが、各サービスには異なる構成、異なる再試行、異なるフィルタがあり、機密データを削除したり宛先を変更したりするための中心的なポイントがないことに気づきます。
Collector はあらゆる意味で退屈です。 OTLP を受信し、バッチ処理を実行し、フィルター処理、サンプリングを実行し、共通属性を追加し、複数のシステムにエクスポートできます。
自己計測: 良いですが十分ではありません
Node.js では、自動計測から始めます。これにより、HTTP、サポートされているフレームワーク、データベース、および共通ライブラリを即座に確認できます。
npm install @opentelemetry/sdk-node \ @opentelemetry/auto-instrumentations-node \ @opentelemetry/exporter-trace-otlp-http
次に、アプリの残りの部分の前に SDK を初期化します。
import { NodeSDK } from '@opentelemetry/sdk-node'; import { OTLPTraceExporter } from '@opentelemetry/exporter-trace-otlp-http'; import { getNodeAutoInstrumentations } from '@opentelemetry/auto-instrumentations-node'; const sdk = new NodeSDK({ traceExporter: new OTLPTraceExporter({ url: process.env.OTEL_EXPORTER_OTLP_TRACES_ENDPOINT, }), instrumentations: [getNodeAutoInstrumentations()], }); sdk.start();
ただし、これは製品ではなくフレームワークを参照します。ユーザーがクエリを作成したことは知っていますが、そのクエリが「注文の作成」または「サブスクリプションの更新」であったことは知りません。そのためには、優位性が重要なポイントでspan のマニュアルが必要です。
const span = tracer.startSpan('checkout.create_order'); try { span.setAttribute('cart.items_count', input.items.length); const order = await createOrder(input); span.setAttribute('order.id', order.id); return order; } catch (error) { span.recordException(error as Error); throw error; } finally { span.end(); }
span マニュアルはどこにも置きません。午前 3 時に、コードベースを半分も読まなくても何が起こったのかを理解したい場所に貼り付けます。
混乱を避けるための 3 つのルール
最初のルール: 各サービスには、service.name、環境とバージョンが必要です。些細なことのように思えますが、これらの属性がなければ、trace はあまり役に立ちません。デプロイによって何かが壊れた場合、2 秒以内にバージョンによってフィルタリングする必要があります。
2 番目のルール: 機密データを属性に含めないでください。電子メール、トークン、整数ペイロード、およびアドレスが誤って可観測性バックエンドに入ってはいけません。ユーザーを識別する必要がある場合は、内部 ID、ハッシュ、または機密性の低いフィールドを考慮してください。
3 番目のルール: 基数に注意してください。 trace の属性として user.id を使用することは理にかなっています。指標ラベルとしては、コストとパフォーマンスを損なう可能性があります。
メトリクス: 少ないが良好
非常に実用的な指標から始めたいと思います。
- リクエストのレート、エラー、期間。
- 外部依存関係のレイテンシー。
- タイムアウトと再試行の回数。
- 尾部の深さ。
- 作業期間。
- バージョンごとのエラーの割合。
残りは必要に応じて追加されます。誰も見ないグラフが満載のダッシュボードは家具であり、可観測性ではありません。
ログ: まだ役に立ちますが、リンクされています
ログは消えません。 trace_id と span_id を持ち歩くと、さらに便利になります。したがって、エラー ログから開始して trace を開くことも、低速の trace から開始して、そのパスで生成されたログのみを読み取ることもできます。
相関関係がなければ、針を探していることになります。相関関係を使用すると、少なくともどの引き出しを調べるべきかがわかります。
「カバーされています」と言う前に使用するチェックリスト
- trace は実際には複数のサービスにまたがっています。
- ログには
trace_idとspan_idが含まれます。 - Collector にはバッチ処理とメモリ制限が設定されています。
- エラーはspanに記録されます。
- サンプリングポリシーがあります。
- メトリックによってカーディナリティが制御されています。
- 機密データはフィルタリングされます。
- アラートはランダムなグラフではなく、ユーザーの症状から始まります。
結論
OpenTelemetry は、それ自体では生産上の問題を解決しません。しかし、それらへの対処方法は変わります。やみくもにログを追加するのではなく、リクエストの実際のパスを追跡し始めます。
私にとって、それがうまくいっている兆候は単純です。何かが起こったとき、チームは「どこを見ているのか?」と尋ねなくなります。そして「なぜあの作品は遅いのですか?」と尋ね始めます。そこでは可観測性がダッシュボードのコレクションではなくツールになります。