Skip to content

EAI-5958: Add kubeletstats collector to stamp workload_id on pod CPU/mem metrics - #810

Merged
johnl-amd merged 5 commits into
mainfrom
EAI-5958-kubeletstats-workload-id
Aug 17, 2026
Merged

EAI-5958: Add kubeletstats collector to stamp workload_id on pod CPU/mem metrics#810
johnl-amd merged 5 commits into
mainfrom
EAI-5958-kubeletstats-workload-id

Conversation

@johnl-amd

Copy link
Copy Markdown
Contributor

Summary

Adds the EAI-5958 kubeletstats collector that stamps workload_id (and project_id) onto per-pod CPU/memory metrics, so per-workload CPU/mem observability matches how GPU and vLLM metrics already carry workload_id. Cherry-picked from #808 (still open) and ported to the deployed chart version.

Why a dedicated kubeletstats daemonset

The cAdvisor metrics scraped by otel-collector-metrics-k8s are node-scoped, so pod/namespace arrive as datapoint labels and can't be associated to a pod by the k8sattributes processor (which works at the resource level). This daemonset uses the kubeletstats receiver, which emits pod/container-scoped metrics with k8s.pod.* as resource attributes — so k8sattributes can look up the pod and stamp its airm.silogen.ai/workload-id label. A transform then copies those identity resource attributes down to datapoint attributes so they survive OTLP ingestion as series labels (workload_id, project_id, pod, namespace, container).

Contents

  • v1.0.7 — the four original EAI-5958 commits from Envoy collector scale to zero #808, cherry-picked unchanged (kubeletstats collector; kubelet addressed by node IP status.hostIP not node name; review fixes — pod-only metric group + tolerations: Exists; node-scoped k8sattributes watch).
  • v1.0.8 — the same collector ported to the active version. root/values.yaml deploys otel-lgtm-stack/v1.0.8, so the v1.0.7 change alone would be inert (as it is on Envoy collector scale to zero #808 today). The port wires memory_limiter and the kubelet collection_interval to the .Values keys v1.0.8 parameterized (collectors.memoryLimiter.*, collectors.scrapeInterval), matching every other collector in that chart.

Non-obvious decisions

  • Kubelet by node IP, not node name — cluster DNS (CoreDNS) does not resolve node hostnames here, so ${env:K8S_NODE_NAME}:10250 fails with "no such host"; status.hostIP is always routable. K8S_NODE_NAME is kept only as the local k8sattributes watch filter (not for DNS).
  • Node-scoped k8sattributes watch — each daemonset pod watches only its own node's pods (node_from_env_var: K8S_NODE_NAME), so cache cost scales with pods-per-node instead of pods × nodes.
  • Pod-level metric group only — the container group would roughly double per-container cardinality (overlapping the existing cAdvisor scrape) for series nothing reads.
  • tolerations: Exists — runs on every node including tainted GPU/inference nodes, so per-workload CPU/mem is collected fleet-wide (matches the node-exporter daemonset's scheduling).

Test plan

  • helm template sources/otel-lgtm-stack/v1.0.8 renders clean (exit 0); parameterized values substitute correctly in the new collector.
  • helm lint sources/otel-lgtm-stack/v1.0.8 passes.
  • Deploy verification: confirm the otel-collector-metrics-kubelet daemonset runs on every node and that k8s_pod_* CPU/memory series carry the workload_id / project_id labels in Grafana/LGTM.

…mem metrics

Per-pod CPU and memory time-series are needed for EPYC (CPU-only) AIM
observability in the AIWB dashboard, scoped the same way GPU/VRAM metrics
already are: by a stable workload_id label.

cAdvisor metrics (scraped by otel-collector-metrics-k8s via a node-role
scrape) are node-scoped, so pod/namespace arrive as datapoint labels and
cannot be associated to a pod by the k8sattributes processor, which works
at the resource level. As a result container_cpu_usage_seconds_total /
container_memory_working_set_bytes carry no workload_id.

Add a dedicated daemonset collector using the kubeletstats receiver, which
emits pod/container-scoped metrics with k8s.pod.* as resource attributes.
k8sattributes then looks up the pod and stamps its airm.silogen.ai/workload-id
(and project-id) label; a transform copies the identity resource attributes
down to datapoint attributes so they survive OTLP ingestion as series labels
(workload_id, project_id, pod, namespace, container). Reuses the existing
otel-collector ServiceAccount (RBAC already grants nodes/stats).
The kubeletstats receiver used endpoint https://${env:K8S_NODE_NAME}:10250 with
K8S_NODE_NAME from spec.nodeName. Cluster DNS does not resolve node hostnames, so
every scrape failed with 'no such host' and the collector produced no metrics.
Use status.hostIP (always routable) instead.
…rations

- Drop the 'container' kubeletstats metric group: consumers query the k8s_pod_* series,
  so the container group only added per-container cardinality (overlapping cAdvisor).
- Add catch-all tolerations so the daemonset runs on tainted nodes (GPU/inference),
  collecting per-workload CPU/memory fleet-wide like the node-exporter daemonset.
Add filter.node_from_env_var so each daemonset pod's k8sattributes processor watches only
its own node's pods (cost scales with pods-per-node, not pods x nodes). kubeletstats already
scrapes only the local node, so local pod metadata is all the enrichment needs. Re-adds a
K8S_NODE_NAME env used solely as the local watch-filter key (not for DNS).
…ersion)

The kubeletstats collector added in the preceding commits landed in
otel-lgtm-stack/v1.0.7, but root/values.yaml deploys v1.0.8 — so the
per-pod CPU/memory workload_id enrichment was inert. Add the same
daemonset collector to v1.0.8, wiring memory_limiter and the kubelet
collection_interval to the .Values keys v1.0.8 parameterized (rather
than the hardcoded settings v1.0.7 used) so it matches the other
collectors in this chart.
@johnl-amd
johnl-amd requested a review from a team as a code owner August 17, 2026 06:47

@woojae-siloai woojae-siloai left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

@johnl-amd
johnl-amd merged commit 989aa7a into main Aug 17, 2026
7 checks passed
@johnl-amd
johnl-amd deleted the EAI-5958-kubeletstats-workload-id branch August 17, 2026 13:07
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants