OpenTelemetry の k8sattributesprocessor を検証する
はじめに
OpenTelemetry を使ってテレメトリデータを取得する際、kubernetes オブジェクトの情報をテレメトリデータに付与したい場合があると思います。
例えば、Pod名
,Namedpace
,Deployment名
などなど。
これらのメタデータをテレメトリデータに付与できれば、例えば「Deployment ごとにメトリクスを集計したい」「特定のコンテナごとに集計したい」といった要望も満たせると思います。
そんな要望を満たしてくれる、Kubernetes Attributes Processor を使用して、どのような動きになるのかを検証してみます。
k8sattributesprocessorについて
github リポジトリはこちらです。
README.md より
Kubernetes attributes processor allow automatic setting of spans, metrics and logs resource attributes with k8s metadata.
The processor automatically discovers k8s resources (pods), extracts metadata from them and adds the extracted metadata to the relevant spans, metrics and logs as resource attributes. The processor uses the kubernetes API to discover all pods running in a cluster, keeps a record of their IP addresses, pod UIDs and interesting metadata. The rules for associating the data passing through the processor (spans, metrics and logs) with specific Pod Metadata are configured via "pod_association" key. It represents a list of associations that are executed in the specified order until the first one is able to do the match.
k8sattributesprocessor は、Pod を自動的に検出し、そこからメタデータを抽出します。
抽出したメタデータをテレメトリデータ(Span,Metrics,Logs)を Resource Attributes
として付与する役割を担います。
付与するルールは、pod_association
キーで構成します。
Which metadata to collect is determined by metadata configuration that defines list of resource attributes to be added. Items in the list called exactly the same as the resource attributes that will be added. The following attributes are added by default:
k8s.namespace.name
k8s.pod.name
k8s.pod.uid
k8s.pod.start_time
k8s.deployment.name
k8s.node.nameYou can change this list with metadata configuration.
デフォルトでは上記の属性が追加されるようです。
追加するメタデータは metadata
で設定できるようです。
当然ながら、Pod のメタデータに上記がなければテレメトリデータにも追加はされないでしょう。
事前準備
kubernetes 環境と OpenTelemetry の環境を準備します。
kubernetes オブジェクトのメトリクス情報は prometheus メトリクスとして公開されているため、Prometheus メトリクスを確認できる環境を用意します。
EKS でクラスターを作成、ADOT を利用して Amazon Managed Service for Prometheus(AMP) へメトリクスを送信 → Amazon Managed Grafana (AMG)で可視化するデモは別記事で紹介しております。
こちらの環境ができている前提で進めていきます。
OpenTelemetry の設定ファイル(初期状態)
さて、まずは processor を使用せず、できる限り小さい構成でメトリクスを取得してみます。
以下が OpenTelemetryCollector の manifest ファイルです。
apiVersion: opentelemetry.io/v1alpha1
kind: OpenTelemetryCollector
metadata:
name: custom-collector
spec:
mode: deployment
serviceAccount: adot-collector
podAnnotations:
prometheus.io/scrape: 'true'
prometheus.io/port: '8888'
resources:
requests:
cpu: "1"
limits:
cpu: "1"
env:
- name: CLUSTER_NAME
value: observability-cluster
config: |
extensions:
sigv4auth:
region: ap-northeast-1
service: "aps"
receivers:
prometheus:
config:
global:
scrape_interval: 15s
scrape_timeout: 10s
scrape_configs:
- job_name: kubernetes-pods-compared
kubernetes_sd_configs:
- role: pod
relabel_configs:
- action: keep
regex: true
source_labels:
- __meta_kubernetes_pod_annotation_prometheus_io_scrape
- action: drop
regex: Pending|Succeeded|Failed|Completed
source_labels:
- __meta_kubernetes_pod_phase
processors:
batch/metrics:
timeout: 60s
exporters:
prometheusremotewrite:
endpoint: https://<AMGのURL>/api/v1/remote_write
auth:
authenticator: sigv4auth
resource_to_telemetry_conversion:
enabled: true // テレメトリデータのResourceAttributeをPrometheusラベルに変換する
service:
extensions: [sigv4auth]
pipelines:
metrics:
receivers: [prometheus]
processors: [batch/metrics]
exporters: [prometheusremotewrite]
Pod に prometheus.io/scrape=true
のアノテーションが付与されている Running
状態のPod のみをスクレイプします。
今回色々と比較してみたいメトリクスですが、OpenTelemetry Collector Pod 自身のメトリクスから、otelcol_receiver_accepted_metric_points
について確認していきます。
取得できたメトリクス(初期状態)
上記の設定ファイルで取得できた Prometheus メトリクスとラベルは以下でした。
otelcol_receiver_accepted_metric_points_total
{
http_scheme="http",
instance="172.31.135.161:8888",
job="kubernetes-pods-compared",
k8s_container_name="otc-container",
k8s_namespace_name="aws-otel-eks",
k8s_node_name="ip-172-31-137-76.ap-northeast-1.compute.internal",
k8s_pod_name="custom-collector-collector-6fdd47449c-vrksx",
k8s_pod_uid="ad1a7326-0952-4f9b-acce-9407f11301bb",
k8s_replicaset_name="custom-collector-collector-6fdd47449c",
net_host_name="172.31.135.161",
net_host_port="8888",
receiver="prometheus",
service_instance_id="172.31.135.161:8888;25fc6aa3-2d8f-4b91-b747-16fba4a01875",
service_name="kubernetes-pods-compared;aws-otel-collector",
service_version="v0.38.1",
transport="http"
}
それではここから k8sattributesprocessor を使って取得できる Prometheus メトリクスのラベルがどう変化するかをみていきます。
検証
1. pod_association - sources の追加 : k8s.namespace.name
まずは k8s.namespace.name
のみを sources
として追加します。
pod_association:
- sources:
- from: resource_attribute
name: k8s.namespace.name
これだと次のラベルが取得できました。
container_image_name
,container_image_tag
,k8s_deployment_name
,k8s_pod_start_time
が取得できていることがわかります。
otelcol_receiver_accepted_metric_points_total{
+ container_image_name="public.ecr.aws/aws-observability/aws-otel-collector",
+ container_image_tag="v0.38.1",
http_scheme="http",
instance="172.31.158.71:8888",
job="kubernetes-pods-compared",
k8s_container_name="otc-container",
+ k8s_deployment_name="custom-collector-collector",
k8s_namespace_name="aws-otel-eks",
k8s_node_name="ip-172-31-145-116.ap-northeast-1.compute.internal",
k8s_pod_name="custom-collector-collector-547778589d-pj6w8",
+ k8s_pod_start_time="2024-07-01T12:41:26Z",
k8s_pod_uid="31cc3bda-0fda-4090-b409-2bc6db57ee49",
k8s_replicaset_name="custom-collector-collector-547778589d",
net_host_name="172.31.158.71",
net_host_port="8888",
receiver="prometheus",
service_instance_id="172.31.158.71:8888;21f18314-0fdc-4ca5-a250-b78603c83cea",
service_name="kubernetes-pods-compared;aws-otel-collector",
service_version="v0.38.1",
transport="http"
}
2. pod_association - sources の追加 : k8s.pod.name, k8s.namespace.name
続いて. k8sattributesprocessor の github に例が掲載されています。こちらを試します。
# below association matches for pair `k8s.pod.name` and `k8s.namespace.name`
- sources:
- from: resource_attribute
name: k8s.pod.name
- from: resource_attribute
name: k8s.namespace.name
1 の結果と変わりませんね。
変わらなかった理由について、「考察」で記載しました。(先にお伝えしておきますが、明確な理由を特定できませんでした...)
otelcol_receiver_accepted_metric_points_total{
+ container_image_name="public.ecr.aws/aws-observability/aws-otel-collector",
+ container_image_tag="v0.38.1",
http_scheme="http",
instance="172.31.137.167:8888",
job="kubernetes-pods-compared",
k8s_container_name="otc-container",
+ k8s_deployment_name="custom-collector-collector",
k8s_namespace_name="aws-otel-eks",
k8s_node_name="ip-172-31-137-76.ap-northeast-1.compute.internal",
k8s_pod_name="custom-collector-collector-789965869-kcgrx",
+ k8s_pod_start_time="2024-07-01T04:44:30Z",
k8s_pod_uid="71e611bc-51d5-4b21-9142-decf76c3c84c",
k8s_replicaset_name="custom-collector-collector-789965869",
net_host_name="172.31.137.167",
net_host_port="8888",
receiver="prometheus",
service_instance_id="172.31.137.167:8888;3afdeb73-c533-40fd-b718-6df0824e10f4",
service_name="kubernetes-pods-compared;aws-otel-collector",
service_version="v0.38.1",
transport="http"
}
3. extract の追加
続いて extract
を追加して、k8s.pod.name
,k8s.pod.uid
,k8s.deployment.name
のみを指定してみます。
これにより、取得するメタデータを絞ることが可能になると思われます。
pod_association
は 1 と同じです。
k8sattributes:
extract:
metadata:
- k8s.pod.name
- k8s.pod.uid
- k8s.deployment.name
pod_association:
- sources:
- from: resource_attribute
name: k8s.namespace.name
1, 2 の結果と比較し、container_image_name
,container_image_tag
,k8s_pod_start_time
が消えていることがわかりました。
otelcol_receiver_accepted_metric_points_total{
http_scheme="http",
instance="172.31.152.36:8888",
job="kubernetes-pods-compared",
k8s_container_name="otc-container",
k8s_namespace_name="aws-otel-eks",
k8s_node_name="ip-172-31-145-116.ap-northeast-1.compute.internal",
k8s_pod_name="custom-collector-collector-755bc6c94f-5tspz",
k8s_pod_uid="d51dae90-7b16-4723-a008-391934f37bf3",
k8s_replicaset_name="custom-collector-collector-755bc6c94f",
net_host_name="172.31.152.36",
net_host_port="8888",
receiver="prometheus",
service_instance_id="172.31.152.36:8888;5d505adc-7587-4eb2-8e58-737b1bc870cd",
service_name="kubernetes-pods-compared;aws-otel-collector",
service_version="v0.38.1",
transport="http",
+ k8s_deployment_name="custom-collector-collector"
}
4. pod_association - sources と extract の指定 : k8s_pod_start_time のみ
次に以下のような設定にしてみました。
k8sattributes:
extract:
metadata:
- k8s.pod.start_time
pod_association:
- sources:
- from: resource_attribute
name: k8s.pod.start_time
この結果、初期状態と同じラベルであることがわかりました。
otelcol_receiver_accepted_metric_points_total{
http_scheme="http",
instance="172.31.158.71:8888",
job="kubernetes-pods-compared",
k8s_container_name="otc-container",
k8s_namespace_name="aws-otel-eks",
k8s_node_name="ip-172-31-145-116.ap-northeast-1.compute.internal",
k8s_pod_name="custom-collector-collector-7ffbc748dc-btfjq",
k8s_pod_uid="d27f920a-36d7-4207-a340-bf7f1ba59c98",
k8s_replicaset_name="custom-collector-collector-7ffbc748dc",
net_host_name="172.31.158.71",
net_host_port="8888",
receiver="prometheus",
service_instance_id="172.31.158.71:8888;ba4a24b9-9531-4125-bc87-70b9dd5e9824",
service_name="kubernetes-pods-compared;aws-otel-collector",
service_version="v0.38.1",
transport="http"
}
5. pod_association - sources の追加 : k8s.pod.ip
github の README.md にある例がもう一つあります。こちらをやってみます。
pod_association:
# below association takes a look at the datapoint's k8s.pod.ip resource attribute and tries to match it with
# the pod having the same attribute.
- sources:
- from: resource_attribute
name: k8s.pod.ip
この結果、こちらも初期状態と同じラベルであることがわかりました。
otelcol_receiver_accepted_metric_points_total{
http_scheme="http",
instance="172.31.54.182:8888",
job="kubernetes-pods-compared",
k8s_container_name="otc-container",
k8s_namespace_name="aws-otel-eks",
k8s_node_name="ip-172-31-56-255.ap-northeast-1.compute.internal",
k8s_pod_name="custom-collector-collector-6c6df6896f-2925l",
k8s_pod_uid="f4f34e2b-0054-492b-8504-b8f5b1596e8b",
k8s_replicaset_name="custom-collector-collector-6c6df6896f",
net_host_name="172.31.54.182",
net_host_port="8888",
receiver="prometheus",
service_instance_id="172.31.54.182:8888;ee63eb00-4fb8-4747-a784-608ed91619ab",
service_name="kubernetes-pods-compared;aws-otel-collector",
service_version="v0.38.1",
transport="http"
}
よくよく調べたら、k8s.pod.ip
は opentelemetry-collector-contrib:v0.104.0 にてリリースされた新しいメタデータのようです。
自分の環境の ADOT を見ると、public.ecr.aws/aws-observability/aws-otel-collector:v0.38.1
でありこちらは最新版でもないために k8s.pod.ip
は対応していないようです。
アクティブに開発が進んでいるため、バージョンを見ないとハマる可能性があります(なぜ上手くいかないのかわからず、半日費やした)。
% k describe po custom-collector-collector-547778589d-pj6w8
Name: custom-collector-collector-547778589d-pj6w8
...
Containers:
otc-container:
...
Image: public.ecr.aws/aws-observability/aws-otel-collector:v0.38.1
...
考察
ここまでやってみて「extract
で指定することで、抽出するメタデータを絞ることができる」ことはよくわかりました。
一方、pod_association - sources で指定する値により、取得できたラベルに差異が生じる理由がいまいちわかりませんでした。
再度 github を読んでみます。
The processor automatically discovers k8s resources (pods), extracts metadata from them and adds the extracted metadata to the relevant spans, metrics and logs as resource attributes. The processor uses the kubernetes API to discover all pods running in a cluster, keeps a record of their IP addresses, pod UIDs and interesting metadata. The rules for associating the data passing through the processor (spans, metrics and logs) with specific Pod Metadata are configured via "pod_association" key. It represents a list of associations that are executed in the specified order until the first one is able to do the match.
「プロセッサを通過するデータ (スパン、メトリック、ログ) を特定のポッド メタデータに関連付けるルールは、「pod_association」キーによって構成されます。これは、最初のものが一致できるようになるまで、指定された順序で実行される関連付けのリストを表します。」と書かれています。
例えば 2 の設定ファイルで考えてみます。
- sources:
- from: resource_attribute
name: k8s.pod.name
- from: resource_attribute
name: k8s.namespace.name
- まず
k8s.pod.name
で Pod とテレメトリデータを関連づけようと試みる - 一致する Pod が無い場合、
k8s.namespace.name
を使用して関連づけようとする - 一致する Pod がある場合、テレメトリデータに対して、一致した Pod からメタデータを付与する
以下の部分でも捕捉されています。
通常のテレメトリデータ(datapoint)では、そのデータを取得した IP と Pod の IP を関連づけるようです。それをk8s.pod.name
やk8s.namespace.name
などで関連づけるようにルールを設定しているものと理解しました。
When it sees a datapoint (log, trace or metric), it will try to associate the datapoint to the pod from where the datapoint originated, so we can add the relevant pod metadata to the datapoint. By default, it associates the incoming connection IP to the Pod IP. But for cases where this approach doesn't work (sending through a proxy, etc.), a custom association rule can be specified.
となると、4 で pod_association
のルールで k8s.pod.start_time
のみを指定した場合にメトリクスのラベルにメタデータが付与されなかったのはなぜなのでしょうか?
単純に考えると、「メトリクスデータと Pod が関連づけられなかった」ということになるかと思います。
この「関連づけ」のロジックについては実装をみてみないとわからなそうだったため、次回の宿題にしたいと思います。
その他
今回はデフォルトに付与される Resource Attributes のみを使用しましたが、他にもあるようです。
こちらにリストがありました。
また、Pod に付与されている label
, annotation
を抽出してテレメトリデータに付与することも可能なようです。
ADOT の公式 docs にも、こちらの processor に関するページがありますので、こちらも読んでみたいと思います。
最後に
時間をかけて検証した割に、理解しきれない部分が多くて残念です...
しかし、「k8s の Deployment, Namespace ごとに OpenTelemetry でメトリクスを収集したい」という要望はこちらの processor を使用することで満たせそうな雰囲気を感じました。
Discussion