AWS Distro for OpenTelemetry (ADOT) Collector の 運用
ADOT Collector は、CNCF によるオープンソースの OpenTelemetry Collector のダウンストリーム配布版です。
お客様は ADOT Collector を使用して、オンプレミス、AWS、その他のクラウドプロバイダーなど、さまざまな環境からメトリクスやトレースなどのシグナルを収集できます。
実際の環境で ADOT Collector を大規模に運用するためには、オペレーターは Collector の健全性を監視し、必要に応じてスケールする必要があります。このガイドでは、本番環境で ADOT Collector を運用するために取るべきアクションについて学びます。
デプロイメントアーキテクチャ
要件に応じて、検討すべきいくつかのデプロイメントオプションがあります。
- コレクターなし
- エージェント
- ゲートウェイ
これらの概念に関する追加情報については、OpenTelemetry のドキュメント をご確認ください。
コレクターなし
このオプションは、基本的にコレクターを完全に除外します。ご存知かもしれませんが、OTEL SDK から直接宛先サービスへの API 呼び出しを行い、シグナルを送信することが可能です。例えば、ADOT Collector のようなプロセス外エージェントにスパンを送信する代わりに、アプリケーションプロセスから直接 AWS X-Ray の PutTraceSegments API を呼び出すことを想像してみてください。
このアプローチに関する AWS 固有の側面はないため、より詳細な情報については、上流のドキュメントのセクションを参照することを強くお勧めします。
エージェント
このアプローチでは、コレクターを分散方式で実行し、シグナルを各送信先に収集します。No Collector
オプションとは異なり、ここでは関心事を分離し、アプリケーションがリソースを使用してリモート API コールを行う必要性を切り離し、代わりにローカルでアクセス可能なエージェントと通信します。
基本的に、Amazon EKS 環境では以下のように コレクターを Kubernetes のサイドカーとして実行します:
上記のアーキテクチャでは、コレクターがアプリケーションコンテナと同じ Pod で実行されているため、localhost
からターゲットをスクレイピングすることになります。そのため、スクレイプ設定でサービスディスカバリーメカニズムを使用する必要はありません。
同じアーキテクチャはトレースの収集にも適用されます。ここに示されているように、OTEL パイプラインを作成するだけです。
メリットとデメリット
-
この設計を支持する一つの論点は、ターゲットがローカルホストのソースに限定されているため、Collector が作業を行うために特別に多くのリソース(CPU、メモリ)を割り当てる必要がないことです。
-
このアプローチを使用することのデメリットは、Collector Pod の設定の多様性が、クラスター上で実行しているアプリケーションの数に比例することです。 つまり、各 Pod の予想されるワークロードに応じて、CPU、メモリ、その他のリソース割り当てを個別に管理する必要があります。これに注意を払わないと、Collector Pod に対してリソースを過剰に割り当てたり、過少に割り当てたりする可能性があり、その結果、パフォーマンスが低下したり、ノード内の他の Pod が使用できるはずの CPU サイクルやメモリを占有してしまう可能性があります。
また、ニーズに応じて、Deployment、DaemonSet、StatefulSet などの他のモデルで Collector をデプロイすることもできます。
Amazon EKS で Collector を Daemonset として実行する
Collector の負荷(メトリクスのスクレイピングと Amazon Managed Service for Prometheus ワークスペースへの送信)を EKS ノード全体に均等に分散させたい場合は、Collector を Daemonset として実行することができます。
Collector が自身のホスト/ノードからのみターゲットをスクレイピングするように、keep
アクションを設定していることを確認してください。
参考として以下のサンプルをご覧ください。より詳細な設定についてはこちらをご覧ください。
scrape_configs:
- job_name: kubernetes-apiservers
bearer_token_file: /var/run/secrets/kubernetes.io/serviceaccount/token
kubernetes_sd_configs:
- role: endpoints
relabel_configs:
- action: keep
regex: $K8S_NODE_NAME
source_labels: [__meta_kubernetes_endpoint_node_name]
scheme: https
tls_config:
ca_file: /var/run/secrets/kubernetes.io/serviceaccount/ca.crt
insecure_skip_verify: true
同じアーキテクチャはトレースの収集にも使用できます。この場合、Collector がエンドポイントに接続して Prometheus メトリクスをスクレイピングする代わりに、アプリケーション Pod からトレーススパンが Collector に送信されます。
長所と短所
利点
- スケーリングの懸念が最小限
- 高可用性の設定が課題
- 使用中の Collector のコピーが多すぎる
- ログのサポートが容易になる可能性がある
欠点
- リソース利用の観点から最適ではない
- リソース割り当てが不均衡
Amazon EC2 上でのコレクターの実行
EC2 上でコレクターを実行する場合、サイドカーアプローチはありません。EC2 インスタンス上でエージェントとしてコレクターを実行することになります。以下のような静的なスクレイプ設定を行うことで、インスタンス内のメトリクスをスクレイプするターゲットを発見できます。
以下の設定は、localhost 上のポート 9090
と 8081
のエンドポイントをスクレイプします。
このトピックについて実践的な深い経験を得るには、One Observability Workshop の EC2 に焦点を当てたモジュールをご覧ください。
global:
scrape_interval: 15s # デフォルトでは、15 秒ごとにターゲットをスクレイプします。
scrape_configs:
- job_name: 'prometheus'
static_configs:
- targets: ['localhost:9090', 'localhost:8081']
Amazon EKS 上で Deployment としてコレクターを実行する
コレクターを Deployment として実行することは、コレクターの高可用性を提供したい場合に特に有用です。ターゲットの数、スクレイプ可能なメトリクスなどに応じて、コレクターのリソースを調整する必要があります。これにより、コレクターがリソース不足に陥り、シグナル収集に問題が生じることを防ぎます。
このトピックについて、ガイドでさらに詳しく読むことができます。
以下のアーキテクチャは、メトリクスとトレースを収集するために、ワークロードノードとは別のノードにコレクターがデプロイされる様子を示しています。
メトリクス収集の高可用性をセットアップするには、詳細な手順を提供するドキュメントをお読みください
Amazon ECS でメトリクス収集のためにコレクターを中央タスクとして実行する
ECS Observer 拡張機能 を使用して、ECS クラスター内または複数のクラスター間で異なるタスクの Prometheus メトリクスを収集できます。
拡張機能のサンプルコレクター設定:
extensions:
ecs_observer:
refresh_interval: 60s # フォーマットは https://golang.org/pkg/time/#ParseDuration
cluster_name: 'Cluster-1' # クラスター名は手動で設定する必要があります
cluster_region: 'us-west-2' # リージョンは直接設定するか、AWS_REGION 環境変数を使用します
result_file: '/etc/ecs_sd_targets.yaml' # ファイルのディレクトリは既に存在している必要があります
services:
- name_pattern: '^retail-.*$'
docker_labels:
- port_label: 'ECS_PROMETHEUS_EXPORTER_PORT'
task_definitions:
- job_name: 'task_def_1'
metrics_path: '/metrics'
metrics_ports:
- 9113
- 9090
arn_pattern: '.*:task-definition/nginx:[0-9]+'
長所と短所
- このモデルの利点は、自身で管理するコレクターと設定が少ないことです。
- クラスターが非常に大きく、スクレイピングするターゲットが数千にも及ぶ場合、負荷がコレクター間で均等に分散されるようにアーキテクチャを慎重に設計する必要があります。
- 高可用性の理由から、ほぼ同じコレクターを複数実行する必要がありますが、運用上の問題を避けるために注意深く行う必要があります。
ゲートウェイ
コレクターの健全性管理
OTEL コレクターは、その健全性とパフォーマンスを監視するためのいくつかのシグナルを公開しています。以下のような是正措置を講じるために、コレクターの健全性を綿密に監視することが不可欠です。
- コレクターを水平方向にスケーリングする
- コレクターが望ましい機能を発揮できるように追加リソースをプロビジョニングする
コレクターからのヘルスメトリクスの収集
OTEL コレクターは、service
パイプラインに telemetry
セクションを追加するだけで、Prometheus Exposition フォーマットでメトリクスを公開するように設定できます。コレクターは自身のログを標準出力にも公開できます。
テレメトリー設定の詳細については、OpenTelemetry のドキュメントをご覧ください。
コレクターのテ レメトリー設定のサンプルは以下の通りです。
service:
telemetry:
logs:
level: debug
metrics:
level: detailed
address: 0.0.0.0:8888
設定が完了すると、コレクターは http://localhost:8888/metrics
で以下のようなメトリクスのエクスポートを開始します。
# HELP otelcol_exporter_enqueue_failed_spans 送信キューに追加できなかったスパンの数。
# TYPE otelcol_exporter_enqueue_failed_spans counter
otelcol_exporter_enqueue_failed_spans{exporter="awsxray",service_instance_id="523a2182-539d-47f6-ba3c-13867b60092a",service_name="aws-otel-collector",service_version="v0.25.0"} 0
# HELP otelcol_process_runtime_total_sys_memory_bytes OS から取得したメモリの総バイト数('go doc runtime.MemStats.Sys' を参照)
# TYPE otelcol_process_runtime_total_sys_memory_bytes gauge
otelcol_process_runtime_total_sys_memory_bytes{service_instance_id="523a2182-539d-47f6-ba3c-13867b60092a",service_name="aws-otel-collector",service_version="v0.25.0"} 2.4462344e+07
# HELP otelcol_process_memory_rss 合計物理メモリ(常駐セットサイズ)
# TYPE otelcol_process_memory_rss gauge
otelcol_process_memory_rss{service_instance_id="523a2182-539d-47f6-ba3c-13867b60092a",service_name="aws-otel-collector",service_version="v0.25.0"} 6.5675264e+07
# HELP otelcol_exporter_enqueue_failed_metric_points 送信キューに追加できなかったメトリクスポイントの数。
# TYPE otelcol_exporter_enqueue_failed_metric_points counter
otelcol_exporter_enqueue_failed_metric_points{exporter="awsxray",service_instance_id="d234b769-dc8a-4b20-8b2b-9c4f342466fe",service_name="aws-otel-collector",service_version="v0.25.0"} 0
otelcol_exporter_enqueue_failed_metric_points{exporter="logging",service_instance_id="d234b769-dc8a-4b20-8b2b-9c4f342466fe",service_name="aws-otel-collector",service_version="v0.25.0"} 0
上記のサンプル出力では、コレクターが otelcol_exporter_enqueue_failed_spans
というメトリクスを公開しているのが分かります。これは送信キューに追加できなかったスパンの数を示しています。このメトリクスは、コレクターが設定された送信先にトレースデータを送信する際に問題が発生しているかどうかを理解するために注視すべきものです。この場合、exporter
ラベルの値が awsxray
となっており、使用中のトレース送信先を示しています。
もう 1 つのメトリクス otelcol_process_runtime_total_sys_memory_bytes
は、コレクターが使用しているメモリ量を理解するための指標です。このメモリ使用量が otelcol_process_memory_rss
メトリクスの値に近づきすぎると、コレクターがプロセスに割り当てられたメモリを使い果たす寸前であることを示しています。その場合、問題を回避するためにコレクターにより多くのメモリを割り当てるなどの対策を講じる時期かもしれません。
同様に、otelcol_exporter_enqueue_failed_metric_points
という別のカウンターメトリクスがあります。これはリモート送信先に送信できなかったメトリクスの数を示しています。
コレクターのヘルスチェック
コレクターが稼働しているかどうかを確認するために、コレクターはライブネスプローブを公開しています。このエンドポイントを使用して、定 期的にコレクターの可用性をチェックすることをお勧めします。
healthcheck
拡張機能を使用して、コレクターにエンドポイントを公開させることができます。以下にサンプル設定を示します:
extensions:
health_check:
endpoint: 0.0.0.0:13133
完全な設定オプションについては、こちらの GitHub リポジトリを参照してください。
❯ curl -v http://localhost:13133
* Trying 127.0.0.1:13133...
* Connected to localhost (127.0.0.1) port 13133 (#0)
> GET / HTTP/1.1
> Host: localhost:13133
> User-Agent: curl/7.79.1
> Accept: */*
>
* Mark bundle as not supporting multiuse
< HTTP/1.1 200 OK
< Date: Fri, 24 Feb 2023 19:09:22 GMT
< Content-Length: 0
<
* Connection #0 to host localhost left intact
壊滅的な障害を防ぐための制限設定
どの環境でもリソース(CPU、メモリ)には限りがあるため、予期せぬ状況による障害を避けるためにコレクターコンポーネントに制限を設定する必要があります。
特に、Prometheus メトリクスを収集するために ADOT Collector を運用する場合は重要です。 次のシナリオを考えてみましょう。あなたは DevOps チームの一員で、Amazon EKS クラスターに ADOT Collector をデプロイし運用する責任があります。アプリケーションチームは、いつでも自由にアプリケーションの Pod をデプロイでき、それらの Pod から公開されるメトリクスが Amazon Managed Service for Prometheus ワークスペースに収集されることを期待しています。
このパイプラインが問題なく機能することを確保するのはあなたの責任です。この問題を解決するには、大きく分けて 2 つの方法があります:
- コレクターを無限にスケーリングする(必要に応じてクラスターにノードを追加する)ことで、この要件をサポートする
- メトリクス収集に制限を設定し、上限をアプリケーションチームに周知する
どちらのアプローチにもメリットとデメリットがあります。コストやオーバーヘッドを考慮せずに、成長し続けるビジネスニーズを完全にサポートすることを約束するなら、オプション 1 を選択することもできます。無限にスケーラブルなクラウドの観点からは、ビジネスニーズの無限の成長をサポートすることは魅力的に聞こえるかもしれません。しかし、これは多くの運用上のオーバーヘッドをもたらし、無限の時間と人的リソースを投入して継続的な中断のない運用を確保しない限り、より壊滅的な状況につながる可能性があります。これは多くの場合、現実的ではありません。
より実用的で賢明なアプローチは、オプション 2 を選択することです。ここでは、運用の境界を明確にするために、任意の時点で上限を設定し(そして必要に応じて徐々に増やしていく)ます。
以下は、ADOT Collector で Prometheus レシーバーを使用してこれを実現する方法の例です。
Prometheus の scrape_config では、特定のスクレイプジョブに対していくつかの制限を設定できます。以下のような制限を設定できます:
- スクレイプの総ボディサイズ
- 受け入れるラベルの数を制限する(この制限を超えるとスクレイプは破棄され、コレクターのログで確認できます)
- スクレイプするターゲットの数を制限する
- ...その他
利用可能なすべてのオプションは Prometheus のドキュメント で確認できます。
メモリ使用量の制限
Collector パイプラインは、memorylimiterprocessor
を使用するように設定でき、プロセッサコンポーネントが使用するメモリ量を制限できます。顧客が、大量のメモリと CPU リソースを必要とする複雑な操作を Collector に実行させたいと考えるのはよくあることです。
redactionprocessor
、filterprocessor
、spanprocessor
などのプロセッサの使用は魅力的で非常に有用ですが、一般的にプロセッサはデータ変換タスクを扱い、タスクを完了するためにデータをメモリ内に保持する必要があることも覚えておく必要があります。これにより、特定のプロセッサが Collector 全体を破壊したり、Collector が自身のヘルスメトリクスを公開するのに十分なメモリを持てなくなる可能性があります。
memorylimiterprocessor
を使用して、Collector が使用できるメモリ量を制限することでこれを回避できます。この推奨事項は、Collector がヘルスメトリクスの公開や他のタスクの実行に使用するためのバッファメモリを提供し、プロセッサが割り当てられたメモリをすべて使用しないようにすることです。
例えば、EKS Pod のメモリ制限が 10Gi
の場合、memorylimitprocessor
を 10Gi
未満、例えば 9Gi
に設定し、1Gi
のバッファをヘルスメトリクスの公開、レシーバーやエクスポーターのタスクなど、他の操作の実行に使用できるようにします。
バックプレッシャー管理
以下に示すようなアーキテクチャパターン(ゲートウェイパターン)は、コンプライアンス要件を維持するためにシグナルデータから機密データをフィルタリングするなど(ただしこれに限定されない)、いくつかの運用タスクを一元化するために使用できます。
しかし、ゲートウェイコレクターに多くの_処理_タスクを負わせすぎると問題が発生する可能性があります。推奨されるアプローチは、プロセス/メモリを多く使用するタスクを個々のコレクターとゲートウェイの間で分散させ、ワークロードを共有することです。
例えば、resourceprocessor
を使用してリソース属性を処理し、transformprocessor
を使用して、シグナル収集が行われるとすぐに個々のコレクター内からシグナルデータを変換することができます。
その後、filterprocessor
を使用してシグナルデータの特定の部分をフィルタリングし、redactionprocessor
を使用してクレジットカード番号などの機密情報を編集することができます。
高レベルのアーキテクチャ図は以下のようになります:
すでにお気づきかもしれませんが、ゲートウェイコレクターはすぐに単一障害点になる可能性があります。明らかな選択肢の 1 つは、複数のゲートウェイコレクターをスピンアップし、以下に示すように AWS Application Load Balancer (ALB) のようなロードバランサーを通じてリクエストをプロキシすることです。