AWS Distro for OpenTelemetryを試してみた
概要
OpenTelemetryの学習の一環としてAWS Distro for OpenTelemetryを試した内容をまとめました。OpenTelemetryについては初心者であるため、自分なりに理解した内容を記載しています。
ECS上にアプリケーションを起動し、トレースをX-Ray、メトリクスをCloudWatchで可視化してみました。アプリケーションはGoで実装し、AWS X-Ray SDK for Go、OpenTelemetry Go SDKを利用してトレースデータをX-Rayに送信しています。
OpenTelemetryやAWS Distro for OpenTelemetryについては勉強中であるため、不正確な情報があるかもしれないという点をご留意ください。
OpenTelemetryとは
OpenTelemetryは、Cloud Native Computing Foundation(CNCF)のプロジェクトです。
CNCFとは、CNCFにおけるOpenTelemetryの位置付け
OpenTelemetryの解説の前に、CNCFとCNCFにおけるOpenTelemetryの位置付けについて軽く触れたいと思います。
CNCFはLinux Foundationのプロジェクトの1つで、コンテナ技術の推進とその進化を取り巻くテクノロジー業界の足並みを揃えるために設立された非営利団体です。代表的なプロジェクトにはKubernetesなどがあります。
CNCFではプロジェクトを成熟度レベル別にSandbox、Incubating、Graduatedの3つに分類しており、 OpenTelemetryは成熟したプロジェクトとして認められたGraduatedとなっています。
Graduatedと認められたその他のプロジェクトについて下記のリンクより確認できます。
また以下のブログによるとOpenTelemetryはCNCFエコシステムのなかでKubernetesに続く2番目に高速なプロジェクトとなっています。
CNCF、Linux Foundation、およびトップ30のオープンソース プロジェクトの2022年のベロシティに関する考察 - The Linux Foundation
OpenTelemetry
OpenTelemetryはオブザーバビリティのフレームワークです。ベンダーに依存しない実装を提供し、テレメトリデータを収集してバックエンドのツールに転送するための方法を標準化することを目的としています。具体的には、トレース、メトリクス、ログといったテレメトリーデータを作成・管理するためのAPI、SDK、その他のツールを提供しています。 ベンダーに依存した実装では、バックエンドのツールを変更する際にコードの書き換えやツール固有のエージェントを変更する必要がありますが、 標準化されたOpenTelemetryではこういった作業が不要になります。
詳細については以下を参照してください。
OpenTelemetry
OpenTelemetry Collector
OpenTelemetryに収集したテレメトリーデータはSDKなどを利用してアプリケーションから直接バックエンドのツールにエクスポートできる他に、OpenTelemetry Collectorというプロキシも用意されています。
OpenTelemetry Collectorは、テレメトリーデータの収集、処理、バックエンドツールへのエクスポートついて実装を提供します。 以下の図にあるとおり、Receiver、Processor、Exporterの3つの主要コンポーネントから構成されています。
- Receiver
データをOpenTelemetry Collectorに取り込む方法です。
OpenTelemetryネイティブフォーマットをサポートするOTLレシーバーやトレースデータ向けのJaegarやメトリクスデータ向けのPrometheusなどのオープンソースフォーマットをサポートするレシーバーがあります。 - Processor
データをエクスポートする前にさまざまな処理を行なえます。フィルタリング、変換などの処理を実行します。 - Exporter
データの送信先を設定します。データは内部フォーマットから別の定義されたフォーマットに変換されます。
詳細は下記のページを参照してください。
AWS Distro for OpenTelemetry とは
AWS Distro for OpenTelemetryはOpenTelemetryのAWSのディストリビューションです。ディストリビューションとは、OpenTelemetryコンポーネントのカスタマイズされたバージョンのことです。
ディストリビューションの詳細については以下を参照してください。
Distributions | OpenTelemetry
ディストリビューションにはAWS以外にも複数のベンダーがあります。
Vendors | OpenTelemetry
AWS Distro for OpenTelemetryの特徴として、AWSリソースとマネージドサービスからメタデータを取得できます。そのためアプリケーションとアプリケーションが動作しているインフラストラクチャデータを関連付けることができ、問題解決までの時間を短縮できます。
詳細は以下のドキュメントを参照してください。
- Distributed Tracing – AWS Distro for OpenTelemetry – Amazon Web Services
- AWS Open Distro for OpenTelemetry
AWS Distro for OpenTelemetry Collector
AWS Distro for OpenTelemetry Collector (ADOT Collector) は、アップストリームのOpenTelemetry CollectorのAWSサポートバージョンです。
今回はECS Fargate上でサイドカーとして実行します。
ADOT CollectorをECS Fargateのサイドカーとして実行する
AWS Distro for OpenTelemetryのECSのチュートリアルを参考にECS FargateのサイドカーとしてADOT Collectorを実行します。
タスク定義
ECSタスクに3つのコンテナを定義しています。
- aws-otel-collector
- aws-otel-emitter
- aoc-statsd-emitter
{
"family": "adot-tracing",
"taskRoleArn": "{{ecsTaskRoleArn}}",
"executionRoleArn": "{{ecsTaskExecutionRoleArn}}",
"networkMode": "awsvpc",
"containerDefinitions": [
{
"name": "aws-otel-collector",
"image": "amazon/aws-otel-collector",
"essential": true,
"command": [
"--config=/etc/ecs/ecs-default-config.yaml"
],
"logConfiguration": {
"logDriver": "awslogs",
"options": {
"awslogs-group": "adot-tracing-otel-collector",
"awslogs-region": "ap-northeast-1",
"awslogs-stream-prefix": "ecs"
}
},
"healthCheck": {
"command": [
"/healthcheck"
],
"interval": 5,
"timeout": 6,
"retries": 5,
"startPeriod": 1
}
},
{
"name": "aws-otel-emitter",
"image": "{adotTracingServer}",
"essential": false,
"dependsOn": [
{
"containerName": "aws-otel-collector",
"condition": "START"
}
],
"logConfiguration": {
"logDriver": "awslogs",
"options": {
"awslogs-group": "adot-tracing-server",
"awslogs-region": "ap-northeast-1",
"awslogs-stream-prefix": "ecs"
}
}
},
{
"name": "aoc-statsd-emitter",
"image": "alpine/socat:latest",
"essential": false,
"entryPoint": [
"/bin/sh",
"-c",
"while true; do echo 'statsdTestMetric:1|c' | socat -v -t 0 - UDP:127.0.0.1:8125; sleep 1; done"
],
"dependsOn": [
{
"containerName": "aws-otel-collector",
"condition": "START"
}
],
"logConfiguration": {
"logDriver": "awslogs",
"options": {
"awslogs-group": "adot-tracing-statsd-emitter",
"awslogs-region": "ap-northeast-1",
"awslogs-stream-prefix": "ecs"
}
}
}
],
"requiresCompatibilities": [
"FARGATE"
],
"cpu": "256",
"memory": "512"
}
それぞれのコンテナの詳細は以下の通りです。
aws-otel-collector
AWSが提供しているイメージamazon/aws-otel-collector を利用します。
command
でContollerの設定ファイルを指定します。
今回設定しているファイル--config=/etc/ecs/ecs-default-config.yaml
の内容は以下で定義されています。
receivers、processors、exporters、serviceの設定内容はそれぞれ以下のようになっています。
-
receivers
データの受信についての設定です。- otlp: OpenTelemetry Go SDKからトレースデータを受信する
- awsxray: AWS X-Ray SDK for Goからトレースデータを受信する
- statsd: aoc-statsd-emitterコンテナから
StatsD
カスタムメトリクスを受信する
-
processors
データを圧縮し、データ送信に必要な発信接続数を削減するためのバッチ処理を行なうための設定です。 トレース(batch/traces
)とメトリクス(batch/metrics
)ごとに設定されています。
batch processorはすべてのCollectorに設定するが強く推奨されています。 詳細については以下のドキュメントを参照してください。
https://github.com/open-telemetry/opentelemetry-collector/blob/main/processor/batchprocessor/README.md -
exporters
データの送信先についての設定です。- awsxray: AWS X-Rayに送信
- awsemf: CloudWatch LogsにEMF (Embedded Metric Format)形式でログを出力することで、ログからCloudWatch Metricsに自動的に変化される
-
service
receivers、processors、exportersは設定しただけでは有効になりません。
serviceでpipelinesを設定することで有効になります。tracesとmetricsの2つのpipelineが設定されています。
aws-otel-emitter
HTTP Serverを動かすためのコンテナです。 今回はGoでHTTP Serverを実装しました。Goでの計装については後述します。
aoc-statsd-emitter
StatsD
を利用してカスタムメトリクスをCloudWatchに送ります。
送信されたメトリクスは名前空間「ECS/AWSOTel/Application」、メトリクス名「statsdTestMetric」でメトリクスを確認できます。
Goで計装する
トレースデータをCollectorに送る方法として、以下の2つのSDKを利用するパターンをそれぞれ試してみました。 今回は勉強のために実装方法の比較をしたかったのでこのような形にしています。
- AWS X-Ray SDK for Go
- OpenTelemetry Go SDK
実装したアプリケーションは簡単なHTTP Serverです。それぞれのSDKで以下の処理を行なっています。
- S3バケットをリストアップする
- aws.amazon.com にHTTPリクエストを行なう
リポジトリは以下です。
AWS X-Ray SDK for Go
aws-xray-sdk-goを利用したパターンです。 SDKはトレースデータをX-Rayに直接送信するのではなく、127.0.0.1:2000
に送信します。 ADOT CollectorはUDPポート2000をリッスンしているため、SDKからデータを受信できます。 なお、ADOT Collectorを利用しない場合、X-Rayデーモンを利用することでX-Rayにデータを送信できます。
詳細は以下を参考にしてください。
- Amazon ECS での X-Ray デーモンの実行 - AWS X-Ray
- Getting Started with X-Ray Receiver in AWS Distro for OpenTelemetry Collector | AWS Open Distro for OpenTelemetry
OpenTelemetry Go SDK
opentelemetry-goを利用したパターンです。
今回はGoを利用しますが、OpenTelemetryはGo以外の言語の計装もサポートしています。言語ごとにテレメトリーデータの対応状況が異なります。
2023年7月現在のGoの対応状況は以下のようになっています。
Language | Traces | Metrics | Logs |
---|---|---|---|
Go | Stable | Beta | Not yet implemented |
その他の言語や最新の情報は以下を確認してください。
Instrumentation | OpenTelemetry
またOpenTelemetryにおけるトレースについては、以下に記載されています。
Traces | OpenTelemetry
ドキュメントを参考に、前提知識としてトレースを計装するための要素をまとめました。
- Tracer Provider
Tracerを提供します。後述するResourceとExporterについても定義します。 - Tracer
spansを作成します。 - Trace Exporters
トレースデータをバックエンドに出力します。バックエンドには、標準出力、OpenTelemetry Collector、Jaegerのようなツールを設定できます。 - Context Propagation
複数のプロセスやサービス間でSpanの関連付けを行ないトレースを組み立てるためのコンセプト。 - Spans
トレースの構成要素。
上記の具体的な実装方法について解説した記事は他にもあると思うので、今回はX-Rayにデータを送信する際にポイントとなる部分のみ解説しようと思います。
また、ソースは以下のリポジトリにあります。
ID Generator
X-RayとOpenTelemetryのデフォルトのトレースIDのフォーマットは異なります。
- X-Ray: AWS X-Ray TraceID format
- OpenTelemetry: W3C Trace Context
OpenTelemetryのデフォルトのトレースIDのフォーマットではトレースデータをX-Rayに送信できないため、X-Rayのフォーマットに合わせたIDGeneratorを利用します。
TracerProviderを生成する際に設定します。
idg := xray.NewIDGenerator()
tp := sdktrace.NewTracerProvider(
sdktrace.WithIDGenerator(idg),
)
Propagator
X-Rayは複数のサービスへのリクエストをトレースするために、X-Amzn-Trace-Id
という名前のHTTPヘッダーを利用します。 X-Ray Headerのフォーマットは以下のようになっています。
X-Amzn-Trace-Id: Root={traceId};Parent={parentId};Sampled={samplingFlag}
X-Ray専用のPropagatorを利用することでX-Ray Headerのフォーマットでトレースすることが可能になります。以下のように設定できます。
otel.SetTracerProvider(tp)
otel.SetTextMapPropagator(xray.Propagator{})
propagatorの仕組みについては、以下のブログの解説がわかりやすかったので紹介させていただきます。
【OpenTelemetry】カスタムPropagatorでバッチや非同期処理のTraceを行う - ymtdzzz.dev
AWS resource Detector
AWS resource Detectorsを利用することで、アプリケーションを実行するECSの情報を取得できます。
取得した情報は以下の通りです(一部の情報は実際の値から変更しています)。
{
"default": {
"otel.resource.aws.ecs.task.arn": "<ECS TASK ARN>",
"otel.resource.service.version": "1.0.0",
"net.sock.peer.addr": "14.13.106.32",
"otel.resource.aws.log.stream.arns": [
"<LOG STREAM ARN>"
],
"http.flavor": "1.1",
"otel.resource.aws.log.group.names": [
"adot-tracing-server"
],
"otel.resource.aws.ecs.cluster.arn": "<ECS Cluster ARN>",
"otel.resource.aws.ecs.task.family": "adot-tracing",
"otel.resource.aws.ecs.launchtype": "fargate",
"otel.resource.aws.ecs.task.revision": "1",
"otel.resource.service.name": "adot-tracing-sample",
"otel.resource.aws.log.group.arns": [
"<LOG GROUP ARN>"
],
"otel.resource.container.id": "",
"otel.resource.aws.ecs.container.arn": "<ECS Container ARN>",
"http.wrote_bytes": 49,
"otel.resource.cloud.platform": "aws_ecs",
"net.sock.peer.port": 38294,
"otel.resource.aws.log.stream.names": [
"<LOG STREAM NAME>"
],
"otel.resource.cloud.provider": "aws",
"otel.resource.container.name": "ip-10-1-1-53.ap-northeast-1.compute.internal"
}
}
今回は、サービス名とサービスバージョンを設定したかったので以下のように実装しました。
resourceAttributes := []attribute.KeyValue{
semconv.ServiceName("adot-tracing-sample"),
semconv.ServiceVersion("1.0.0"),
}
ecsResourceDetector := ecs.NewResourceDetector()
ecsRes, err := ecsResourceDetector.Detect(ctx)
if err != nil {
return nil, err
}
if ecsRes.Attributes() != nil {
resourceAttributes = append(resourceAttributes, ecsRes.Attributes()...)
}
res := resource.NewWithAttributes(
semconv.SchemaURL,
resourceAttributes...,
)
tracerProvider := trace.NewTracerProvider(
trace.WithResource(res),
)
最後に
OpenTelemetryの学習を進めながら実際にAWS Distro for OpenTelemetryを試すことで、よりOpenTelemetryについて理解を深めることができました。 今後もOpenTelemetryだけでなくオブザーバビリティ、分散トレーシングについて理解を深めていきたいです。
実際にADOTを導入する予定はありませんが、OpenTelemetryの状況をふまえて必要なタイミングでプロダクトにも活かしていけたらと思います。
参考
- OpenTelemetry
- AWS Open Distro for OpenTelemetry
- AWS Distro for OpenTelemetry の Amazon EKS アドオンを使用したメトリクスとトレースの収集 | Amazon Web Services ブログ
- OpenTelemetryとは何か、そしてなぜそれが計装器の未来なのか? | New Relic
- OpenTelemetryを理解する 第2回: コアのコンポーネント | New Relic
- OpenTelemetryを理解する 第3回: データソース | New Relic
- OpenTelemetryを理解する 第4回: JavaアプリをOpenTelemetryで計装する | New Relic
- OpenTelemetryを理解する 第5回:コレクターの使用とデータの可視化 | New Relic
- Go support for AWS X-Ray now available in AWS Distro for OpenTelemetry | AWS Open Source Blog
- 【OpenTelemetry】カスタムPropagatorでバッチや非同期処理のTraceを行う - ymtdzzz.dev
- OpenTelemetry でトレースを収集・可視化する(Jaeger, AWS X-Ray)
Discussion