Upgrade to Pro — share decks privately, control downloads, hide ads and more …

OpenTelemetry の Trace を中心としたパフォーマンス改善

rmatsuoka
November 15, 2023

OpenTelemetry の Trace を中心としたパフォーマンス改善

rmatsuoka

November 15, 2023
Tweet

More Decks by rmatsuoka

Other Decks in Programming

Transcript

  1. Mackerel の Otel 対応 • OpenTelemetry が策定した Telemetry 送受信プロトコル OTLP

    によってメトリックを 受信 • PromQL によってメトリッ クをクエリしてグラフに描画 画面は開発中のものです
  2. PromQL • Prometheus に実装されているメトリックをクエリする 言語(式) • たとえば ◦ http_request_total{job=”prometheus”} ▪

    jobをキー, ”prometheus” を値に持つ属性を持った http_request_total のメトリックをすべて取得する ◦ filesystem_usage / filesystem_capacity ▪ ファイルシステムの使用率を計算する • Mackerel では今回 PromQL エンジンを独自に実装して 提供します
  3. 開発しているのはこのコンポーネント RDB Time Series DB Otel metric Reader (ECS) Otel

    metric Writer (ECS) Otel Collector Web Browser Mackerel あなたの server あなた メトリックのメタデータを保存 メトリックの時系列データを保存 OTLP によってメト リックを受信 PromQL を評価 してメトリック を DB から取得
  4. 今回のお話する仕事 • もともとこのコンポーネントは PoC であった ◦ 実装の方針が正しいか調べるため ◦ あまりパフォーマンスを意識した実装にはなっていなかった •

    実際のサーバーからメトリックを送信してブラウザから 見ようとすると重くて使い物にならない ◦ 一つのダッシュボードを開くだけで Reader コンポーネントの CPU 使用率が 100 % になった • 「コンポーネントのパフォーマンスを改善しよう!」
  5. Trace を中心に計測してみた! • Trace をする。さらにTrace で見つかったボトルネックを 他のツールを使うことによって掘り下げていくトップダ ウンの方法で計測 • コンポーネント

    Writer / Reader どちらも Trace を使っ てパフォーマンス改善したが、今回の話は Reader に焦 点をあてる • 「Trace を入れよう」とは id:lufiabb が言った ◦ 曰く「タスクとしては Trace を入れて終わるつもりだったけど、 rmatsuoka がパフォーマンス改善を進められそうだから任せた」
  6. 分散トレース トレースは「スパン」の木構 造で表すことができる。 Span B は Span A を親に持 ち

    Span C と Span D を子に 持つ Span A (例: http Handler など) Span B (use case) Span C (database access) Span D
  7. Go で trace を計装するのは 3 ステップ! 1. Tracer の initialize

    を追加 2. http.Handler を otelhttp でラップ 3. 計測したい関数に trace start/end の2行追加
  8. 1. Tracer の initialize を追加 // main 関数あたりに trace の送り先を指定する

    traceExporter, err := otlptracegrpc.New(ctx, ...) ... -- nicepkg/tracer.go -- // pkg ごとに tracer を初期する var tracer = otel.Tracer("example.com/proj/nicepkg")
  9. 2. http.Handler を otelhttp でラップ // リクエスト処理全体のスパンを生成してくれる。 // metric reader

    コンポーネントは net/http と互換性のある // github.com/go-chi/chi によって書かれている - handler := newNiceHandler() + handler := otelhttp.NewHandler(newNiceHandler(), “servername”, ...)
  10. 1. 3. 計測したい関数に2行追加 // スパンを生成する func NiceFunction(ctx context.Context) { +

    ctx, span := tracer.Start(ctx, "SpanName") + defer span.End() + // function body }
  11. モニタリングサービスへ送信 • 今回、トレーシングデータは AWS X-Ray へ送信した。 ◦ AWS Open Distro

    for OpenTelemetry https://aws-otel.github.io/ • ECS にあるコンポーネントのサイドカーにAWS が提供し ている ADOT Collector (AWS Distro の OpenTelemetry Collector) を立てた。
  12. N + 1 問題 • データベースへの問い合わせに発生する問題 • 取り出すメトリック一覧を取得する RDB へのアクセス

    を1回 • それぞれのメトリックの時系列データを Time Series DB から取り出すことをループで N 回実行 • まとめて取得をするように変更して N+1 問題は解決
  13. DB への特定のリクエストが遅いことを 発見 • 特定の条件のリクエストだけが遅い • 大抵のリクエストはそんなに遅くない • テーブルはふたつある ◦

    メトリック (Mackerel のオーガニゼーションの ID や メトリック のタイプなど) ◦ メトリックのメタデータ (メトリックの属性のテーブル、メト リックに従属している)
  14. DB へのインデックスを追加 • DB の実行計画を`EXPLAIN ANALYZE` コマンドで見る • メタデータテーブルの index

    の貼り方がマズかったこと がわかった • 高速化のためメタデータテーブルを非正規化してオーガ ニゼーションID のカラムを追加した + ALTER TABLE <メタデータテーブル> ADD COLUMN "org_id" BIGINT; + CREATE INDEX <index_name> on <メタデータテーブル> ("org_id", ...);
  15. トレースを計装するときのヒント 2 // フィールドを追加する // トレースにリクエストの情報を追加して、トレースの詳細に見る時に // 参考にすることができる。 // この例ではリクエストされた

    PromQL をフィールドに追加している。 ctx, span := tracer.Start(ctx, "eval", trace.WithAttributes( attribute.String("query", query), attribute.Int64("start", start.Unix()), attribute.Int64("end", end.Unix()), ))
  16. OpenTelemetry の Mackerel の beta 参加者募集! • Mackerel「ラベル付きメトリック機能」ベータ版テスト お申し込みフォーム からお申し込みください

    ◦ ブログ記事 [ なぜ Mackerel は OpenTelemetry のラベル付きメトリックをサポートす るのか - Mackerel お知らせ #mackerelio https://mackerel.io/ja/blog/entry/why-does-mackerel-support-open-telemet ry-labeled-metrics ] からフォームに飛べます