OpenTelemetryを使ってSpanをW&Bに記録する (2)
はじめに
こんにちは、@Getty708 です。ML パイプライン開発において、開発と運用へのシームレスな統合についてアイデアを試しています。前回の記事では、OpenTelemetry を使って Span を W&B に記録する方法を考えました。今回はより運用よりの観点から、W&B への記録に加えて、Grafana と Jeager での Span の可視化を行います。
前回の記事: OpenTelemetry を使って Span を W&B に記録する
今回の目標
目標とするシステムの全体像は以下の図のとおりです。前回の記事で「Model Development」の部分を実装したので、今回は「Operation」の部分を追加していきます。具体的には、OTel Collector を追加します。そして、OTel Collector 経由で、Prometheus にメトリクスを、Jeager に Span を送信します。アーキテクチャは以下のようになります。
トレースの可視化はJeagerで行います。Jeager は、マイクロサービスのデータフローなどを可視化するための分散トレーシングのプラットフォームです。メトリクスは、Grafana を Prometheus に連携して可視化します。
実装の詳細
実装の詳細は GitHub のリポジトリを参照してください。
OTel Collector + 各種サービスのデプロイ
実装の詳細は GitHub のリポジトリを参照してください。
Docker Compose
OTel Collector は、ML パイプラインとは独立したプロセスで、ベンダー依存ではない方法で ML パイプラインからのテレメトリデータを Recieve/Process/Export します。今回は各種サービスと一緒に docker でデプロイします。OTel Collector に関しては以下の記事が参考になります。
今回使用した docker-compose-monitor.yaml
は以下のようになります。各種モジュールの設定は公式ドキュメントを参考にしました。
- OpenTelemetry Docs - Install the Collector
- JEAGER - Getting Started
- Grafana - Monitoring a Linux host with Prometheus, Node Exporter, and Docker Compos
services:
otel-collector:
container_name: mlops-otel-collector
image: otel/opentelemetry-collector-contrib:0.111.0
ports:
- 8888:8888 # Prometheus metrics exposed by the Collector
- 8889:8889 # Prometheus exporter metrics
- 4317:4317 # OTLP gRPC receiver
- 4318:4318 # OTLP http receiver
volumes:
- ./docker/otel-collector/config/otel-collector-config.yaml:/etc/otelcol-contrib/config.yaml
networks:
- mlops-network
prometheus:
container_name: mlops-prometheus
env_file:
- .env
image: prom/prometheus:v2.54.1
volumes:
- ./docker/prometheus/config/prometheus.yaml:/etc/prometheus/prometheus.yml
- prometheus-data:/prometheus
ports:
- 9090:9090
networks:
- mlops-network
grafana:
container_name: mlops-grafana
labels:
prometheus-scrape.enabled: "false"
service: "grafana"
env_file:
- .env
image: grafana/grafana-oss:11.2.2
ports:
- "3000:3000"
environment:
GF_SECURITY_ADMIN_PASSWORD: admin
networks:
- mlops-network
jaeger:
image: jaegertracing/all-in-one:1.62.0
container_name: mlops-jaeger
ports:
- "16686:16686"
networks:
- mlops-network
volumes:
grafana-data: {}
prometheus-data: {}
networks:
mlops-network:
OTel Collector の設定
続いて、OTel Collector の設定ファイルです。以下のようになります。設定ファイルは、receivers
・exporters
・connectors
・service
の 4 つのセクションに分かれています。
receivers
では、ML Pipeline の OTel SDK から送られるデータを受け取れるようにするために OTLP Reciever の設定を記述しています。GRPC の場合は0.0.0.0:4317
がエンドポイントになります。exporters
には、OTel Collector で集約したデータの送信先を記述します。今回は Prometheus と Jeager 側の OTel Collector にデータを送る設定が記述されています。connector
については後述します。service
では、データの流れを設定します。
receivers:
otlp:
protocols:
grpc:
endpoint: 0.0.0.0:4317
http:
endpoint: 0.0.0.0:4318
exporters:
prometheus:
endpoint: "0.0.0.0:8000"
namespace: "otel"
otlp:
endpoint: "jaeger:4317"
tls:
insecure: true
connectors:
spanmetrics:
histogram:
disable: false
aggregation_temporality: "AGGREGATION_TEMPORALITY_CUMULATIVE"
metrics_flush_interval: 1s
metrics_expiration: 10s
service:
pipelines:
traces:
receivers: [otlp]
exporters: [spanmetrics, otlp]
metrics:
receivers: [otlp, spanmetrics]
exporters: [prometheus]
connector
では、Span Metrics Connector という、スパンをメトリクスに変換するモジュールの設定が記述されています。メトリクスとしては、各 Span の duration の sum, count などがメトリクスとして出力されます。Span Metrics Connector については、以下の記事が参考になりました。
Prometheus の設定
最後に、Prometheus が OTel Collector からデータを受け取る設定を、Prometheus の設定ファイルに追加します。
global:
scrape_interval: 15s
evaluation_interval: 15s
scrape_configs:
- job_name: prometheus
static_configs:
- targets: ["0.0.0.0:9090"]
- job_name: "otel-collector"
static_configs:
- targets: ["otel-collector:8000"]
パイプライン側の実装
前回の記事では W&B への Exporter を実装しましたが、今回はそこに OTel Collector を追加します。具体的には以下のコードを追加します。
from opentelemetry.sdk.trace.export import BatchSpanProcessor
from opentelemetry.exporter.otlp.proto.grpc.trace_exporter import OTLPSpanExporter
tracer_provider.add_span_processor(
span_processor=BatchSpanProcessor(
span_exporter=OTLPSpanExporter(endpoint="localhost:4317", insecure=True)
)
)
追加するのはこれだけです。Span の生成などは、W&B に出力する際に設定したものをそのまま使うことができます。実際には、モデル開発フェーズにおける W&B への結果の出力か、運用時の Prometheus への出力かのどちらかを使いますが、今回は動作確認のため両方同時に出力します。
いざ、実行
まずは、docker-compose で先ほど定義したモニタリングツールを起動します。
docker compose -f docker-compose-monitor.yaml up -d
続いて、パイプラインを実行します。
poetry run python services/monitoring/tools/run_otel_in_wandb.py -n 1000 -w online
ではここからモニタリングツールを確認していきます。 W&B については前回と同じなので省略します。
Jaeger
まず、Jaeger でトレースを見ます。http://localhost:16686
から GUI にアクセスできます。下の図のように検索をかけると、収集されたトレースが表示されます。Span の数はパイプライン全体の親 Span と、各モデル 3 個分で計 4 個を作成しているので、表示されているスパンの数と一致していることが確認できます。
Jaeger 検索画面
Jaeger トレース詳細
Prometheus
次に Prometheus で収集されたメトリクスを確認します。Prometheus には http://localhost:9090
からアクセスできます。otel_traces_span_metrics_duration_milliseconds_sum
などで検索すると、 SpanMetricsConnector で生成されたメトリクスを確認できます。
Grafana
最後に Grafana です。Grafana は起動後に、http://localhost:3000
にアクセスするとログイン画面が表示されますので、管理者アカウントでログインします。デフォルトのユーザー名とパスワードは admin
です。ログイン後、ダッシュボードを作成が必要になります。私の作成したものは、こちらの JSON (ml_pipeline_observability.json) を使ってインポートできます。SpanMetricsConnector で出力されるメトリクスはスパンの合計数 (sum
) と スパンの数 (count
) ですが、特定区間ごとに sum の変化量を count の変化量で割ることで、平均的な値を算出しています。詳細はダッシュボードを参照してください。
ダッシュボードを開くと、一番上にパイプラインの Latency、次に各モデルの Latency、最後に検出された人数のグラフが表示されています。W&B と見比べると変化がより滑らかみえると思います。W&B に表示されているのは全データポイントに対して、Grafana に表示されているのは集計値であるためです。
Grafana のダッシュボード
W&B のダッシュボード
まとめ
OTel を上手に活用することで、 モデル開発時と運用時でほぼ同じメトリクスを一つの実装で記録・確認することができました。モデル開発と運用のギャップを少しだけ減らせたのではないかと思っています。今後も同様に、開発と運用をシームレスにするためのアイデアを模索していきたいと思います。
Discussion