Skip to content

EAI-5958: Stamp workload_id on per-pod CPU/memory metrics - #806

Closed
johnl-amd wants to merge 1 commit into
mainfrom
eai-5958-pod-cpu-mem-metrics
Closed

EAI-5958: Stamp workload_id on per-pod CPU/memory metrics#806
johnl-amd wants to merge 1 commit into
mainfrom
eai-5958-pod-cpu-mem-metrics

Conversation

@johnl-amd

Copy link
Copy Markdown
Contributor

Summary

  • Adds a dedicated kubeletstats daemonset collector (otel-collector-metrics-kubelet) to the otel-lgtm-stack chart that emits per-pod CPU and memory metrics labeled with workload_id (plus project_id, pod, namespace, container).
  • Enables per-workload CPU/system-memory observability for EPYC (CPU-only) AIMs in the AIWB dashboard, scoped the same stable way GPU/VRAM metrics already are.

Why

EPYC-based AIMs (parent initiative EAI-2244) hold model weights in RAM, so the AIWB dashboard needs CPU + system-memory panels alongside the existing GPU/VRAM panels. The AIWB metrics backend scopes every panel by a workload_id="<AIM-uuid>" label. GPU metrics carry that label (stamped by the device-metrics-exporter); vLLM metrics carry it (vLLM collector relabel). Container CPU/memory metrics do not.

Root cause of the gap

otel-collector-metrics-k8s scrapes cAdvisor via a role: node scrape (/metrics/cadvisor). Those metrics are node-scoped, so pod/namespace arrive as datapoint labels. The k8sattributes processor associates pods at the resource level, so it cannot enrich node-scoped cAdvisor series — leaving container_cpu_usage_seconds_total / container_memory_working_set_bytes without workload_id.

Approach

Use the kubeletstats receiver instead, which emits pod/container-scoped metrics with k8s.pod.* as resource attributes:

  1. kubeletstats (daemonset, per-node kubelet at :10250) → pod + container metric groups.
  2. k8sattributes looks up the pod and extracts airm.silogen.ai/workload-idworkload.id (and project-id).
  3. transform/workload-id-labels copies the identity resource attributes down to datapoint attributes, so they survive OTLP→Mimir ingestion as series labels (resource attributes otherwise land in target_info, not on the series).

Reuses the existing otel-collector ServiceAccount — RBAC already grants nodes/stats, nodes/proxy, pods, namespaces, so no RBAC change is needed.

Risk

Low–medium. Additive: a new collector; existing pipelines are untouched. New series (k8s_pod_cpu_*, k8s_pod_memory_*, k8s_container_*) with per-pod cardinality (comparable to existing cAdvisor series). Uses the same collector image and OTLP endpoint as the other collectors.

Test plan / validation status

  • Config review against the deployed v1.0.7 collectors and the working k8sattributes/workload-id pattern already used by the logs collector and the vLLM collector.
  • Runtime validation deferred — app-dev is currently unreachable and the local kind cluster is degraded (host inotify limit exhausted → kube-proxy/CoreDNS/OTel-operator crashlooping, so the operator webhook can't admit the new collector). Kept as draft until validated on a live cluster.
  • On deploy, verify: k8s_pod_cpu_usage{workload_id="<aim-uuid>"} and k8s_pod_memory_working_set_bytes{workload_id="<aim-uuid>"} return series for a labeled workload pod, and that workload_id/namespace/pod appear as labels.

Follow-ups (separate work, not in this PR)

  • AIWB backend: add MetricName entries + PromQL builders for CPU usage / CPU availability / system memory.
  • AIWB frontend: CPU/memory panels + accelerator-type gating.
  • CPU/memory availability denominator sourced from the K8s object (resolved-profile requests/limits) rather than kube-state-metrics.

…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).

Runtime validation deferred: app-dev is unreachable and the local kind
cluster is degraded (host inotify limit exhausted).
@johnl-amd

Copy link
Copy Markdown
Contributor Author

Superseded by #807, which is the same kubeletstats collector change based on bump_version_envoy_and_ai_gateway (the branch currently on app-dev) instead of main, so it lands on top of the gateway bump without reverting it.

@johnl-amd johnl-amd closed this Aug 12, 2026
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.

1 participant