Snippets · Sources
Scrape cAdvisor container metrics from the kubelet
Many Kubernetes dashboards are built on cAdvisor's container_* metrics. Each kubelet serves them at /metrics/cadvisor on port 10250; a DaemonSet collector scrapes its own node with the prometheus receiver, authenticated with the pod's service-account token. In our kind cluster it produced 88 metrics per scrape.
- Signals
- Metrics
- Category
- CollectionKubernetes
- Source
- Kubernetes kubelet (/metrics/cadvisor)
- Components
- prometheusbatchmemory_limiterotlphttp
- Destination
- OTLP/HTTP endpoint
OpenTelemetry Collector
receivers:
prometheus/cadvisor:
config:
scrape_configs:
- job_name: kubelet-cadvisor
scrape_interval: 30s
scheme: https
metrics_path: /metrics/cadvisor
authorization:
credentials_file: /var/run/secrets/kubernetes.io/serviceaccount/token
tls_config:
insecure_skip_verify: true
static_configs:
- targets: ['${env:K8S_NODE_NAME}:10250']
processors:
memory_limiter:
check_interval: 1s
limit_percentage: 80
spike_limit_percentage: 25
batch:
timeout: 5s
exporters:
otlphttp:
endpoint: https://your-backend:4318
service:
pipelines:
metrics:
receivers: [prometheus/cadvisor]
processors: [memory_limiter, batch]
exporters: [otlphttp]
The collector needs this to run in a cluster: a ServiceAccount, the RBAC it uses and the workload. Create the config first with kubectl -n observability create configmap k8s-cadvisor-scrape --from-file=config.yaml.
apiVersion: v1
kind: ServiceAccount
metadata:
name: k8s-cadvisor-scrape
namespace: observability
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
name: k8s-cadvisor-scrape
rules:
- apiGroups: [""]
resources: [nodes/metrics]
verbs: [get]
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
name: k8s-cadvisor-scrape
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: ClusterRole
name: k8s-cadvisor-scrape
subjects:
- kind: ServiceAccount
name: k8s-cadvisor-scrape
namespace: observability
---
apiVersion: apps/v1
kind: DaemonSet
metadata:
name: k8s-cadvisor-scrape
namespace: observability
spec:
selector:
matchLabels:
app: k8s-cadvisor-scrape
template:
metadata:
labels:
app: k8s-cadvisor-scrape
spec:
serviceAccountName: k8s-cadvisor-scrape
containers:
- name: otelcol
image: otel/opentelemetry-collector-contrib:0.161.0
args: ["--config=/conf/config.yaml"]
env:
- name: K8S_NODE_NAME
valueFrom:
fieldRef:
fieldPath: spec.nodeName
volumeMounts:
- name: config
mountPath: /conf
volumes:
- name: config
configMap:
name: k8s-cadvisor-scrape
LinkMesh
Library › Sources › Kubernetes Container Metrics (cAdvisor)
- Scrape Targets
- ["${env:K8S_NODE_NAME}:10250"]
- Scheme
- https
- Scrape Interval
- 30s
- Metrics Path
- /metrics/cadvisor
- Bearer Token File
- /var/run/secrets/kubernetes.io/serviceaccount/token
- TLS
- {"ca_file":"/var/run/secrets/kubernetes.io/serviceaccount/ca.crt"}
- Sample Limit
- 50000
- Metric Filters
- [{"action":"drop","regex":"container_.*;","source_labels":["__name__","image"]},{"action":"drop","regex":"container_(?:[^n;]|n[^e;]|ne[^t;])[^;]*;","source_labels":["__name__","container"]}]
What LinkMesh then pushes to the collector, under receivers:
prometheus/input_src_00000501:
config:
scrape_configs:
- bearer_token_file: /var/run/secrets/kubernetes.io/serviceaccount/token
job_name: input_src_00000501
metric_relabel_configs:
- action: drop
regex: container_.*;
source_labels:
- __name__
- image
- action: drop
regex: container_(?:[^n;]|n[^e;]|ne[^t;])[^;]*;
source_labels:
- __name__
- container
- action: keep
regex: container_cpu_cfs_periods_total|container_cpu_cfs_throttled_periods_total|container_cpu_cfs_throttled_seconds_total|container_cpu_usage_seconds_total|container_fs_reads_bytes_total|container_fs_reads_total|container_fs_writes_bytes_total|container_fs_writes_total|container_memory_cache|container_memory_rss|container_memory_swap|container_memory_usage_bytes|container_memory_working_set_bytes|container_network_receive_bytes_total|container_network_receive_packets_dropped_total|container_network_receive_packets_total|container_network_transmit_bytes_total|container_network_transmit_packets_dropped_total|container_network_transmit_packets_total|machine_cpu_cores|machine_memory_bytes
source_labels:
- __name__
metrics_path: /metrics/cadvisor
sample_limit: 50000
scheme: https
scrape_interval: 30s
static_configs:
- targets:
- ${env:K8S_NODE_NAME}:10250
tls_config:
ca_file: /var/run/secrets/kubernetes.io/serviceaccount/ca.crt
The same, as an API call (see automation recipes):
curl -X POST "$LINKMESH/sources" \
-H "Authorization: Bearer $LMSAT" \
-H "Content-Type: application/json" \
-d '{
"defaultConfig": {
"bearer_token_file": "/var/run/secrets/kubernetes.io/serviceaccount/token",
"metric_relabel_configs": [
{
"action": "drop",
"regex": "container_.*;",
"source_labels": [
"__name__",
"image"
]
},
{
"action": "drop",
"regex": "container_(?:[^n;]|n[^e;]|ne[^t;])[^;]*;",
"source_labels": [
"__name__",
"container"
]
}
],
"metric_selection": {
"catalog_source": "prometheus/cadvisor",
"preset": "minimum"
},
"metrics_path": "/metrics/cadvisor",
"sample_limit": 50000,
"scheme": "https",
"scrape_interval": "30s",
"targets": [
"${env:K8S_NODE_NAME}:10250"
],
"tls_config": {
"ca_file": "/var/run/secrets/kubernetes.io/serviceaccount/ca.crt"
}
},
"name": "Kubernetes Container Metrics (cAdvisor)",
"receiverType": "prometheus"
}'The rest of the collector config above (memory_limiter, batch, otlphttp) is not part of this template: in LinkMesh those are separate sources, processors and destinations on the route.
Generated from LinkMesh's template catalogue by linkmesh-server snippet-render 1.464.3, not written by hand.
Download config.yamlValidated with otelcol-contrib v0.161.0 on 2026-10-26
Complete config files, each checked with the collector's own validate command (otelcol-contrib v0.161.0). Replace https://your-backend:4318 with your own OTLP endpoint before you run it.
The gotcha
The ClusterRole needs get on nodes/metrics, and without it the collector still looks healthy. In our test, with only nodes/stats in the role, the scrape failed with a warning, not an error, and the collector still emitted 5 metrics instead of 88 - the scrape bookkeeping series, no container data.
Manage this across a fleet
LinkMesh manages collector configuration across a fleet from one self-hosted control plane, so a change like this is made once rather than host by host. The first 25 Collectors are free after a no-card registration in the OpenSight Customer Portal (5 without one).
Doing this in LinkMesh: Install LinkMesh on Kubernetes
Related snippets
- Scrape pods by prometheus.io annotationsScrape every pod that asks for it with prometheus.io/scrape annotations, each node's collector only its own pods.
- Collect Kubernetes cluster metrics (one replica)Deployment replicas, pod phases, node conditions, restarts and resource quotas from the Kubernetes API - one collector for the whole cluster.
- Collect Kubernetes events as logsScheduling failures, image pull errors, OOM kills and probe failures as log records - kept after Kubernetes deletes the events.
- Kubernetes pod logs to Loki, enriched with deployment and clusterEvery pod's logs into Loki over OTLP, labelled with namespace, pod, container, deployment, node and cluster.