Snippets · Sources
Collect Kubernetes cluster metrics (one replica)
Cluster state - desired versus available replicas, pod phases, container restarts, node conditions, quotas - lives in the Kubernetes API, not on any node. The k8s_cluster receiver watches it from a single-replica Deployment. In our kind cluster the ClusterRole in this manifest was enough for 76 metrics per collection and no permission errors.
- Signals
- Metrics
- Category
- CollectionKubernetes
- Source
- Kubernetes API (cluster state)
- Components
- batchmemory_limiterotlphttp
- Destination
- OTLP/HTTP endpoint
OpenTelemetry Collector
receivers:
k8s_cluster:
auth_type: serviceAccount
collection_interval: 30s
node_conditions_to_report: [Ready, MemoryPressure, DiskPressure]
allocatable_types_to_report: [cpu, memory]
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: [k8s_cluster]
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-cluster-metrics --from-file=config.yaml.
apiVersion: v1
kind: ServiceAccount
metadata:
name: k8s-cluster-metrics
namespace: observability
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
name: k8s-cluster-metrics
rules:
- apiGroups: [""]
resources: [events, namespaces, namespaces/status, nodes, nodes/spec, pods, pods/status, replicationcontrollers, replicationcontrollers/status, resourcequotas, services]
verbs: [get, list, watch]
- apiGroups: [apps]
resources: [daemonsets, deployments, replicasets, statefulsets]
verbs: [get, list, watch]
- apiGroups: [batch]
resources: [jobs, cronjobs]
verbs: [get, list, watch]
- apiGroups: [autoscaling]
resources: [horizontalpodautoscalers]
verbs: [get, list, watch]
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
name: k8s-cluster-metrics
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: ClusterRole
name: k8s-cluster-metrics
subjects:
- kind: ServiceAccount
name: k8s-cluster-metrics
namespace: observability
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: k8s-cluster-metrics
namespace: observability
spec:
replicas: 1
strategy:
type: Recreate
selector:
matchLabels:
app: k8s-cluster-metrics
template:
metadata:
labels:
app: k8s-cluster-metrics
spec:
serviceAccountName: k8s-cluster-metrics
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-cluster-metrics
LinkMesh
Library › Sources › Kubernetes Cluster Metrics
- Collection Interval
- 30s
- Auth Type
- serviceAccount
- Node Conditions To Report
- ["Ready","MemoryPressure","DiskPressure","PIDPressure","NetworkUnavailable"]
- Allocatable Types To Report
- ["cpu","memory","pods","ephemeral-storage"]
- Optional Metrics
- ["k8s.service.endpoint.count","k8s.service.load_balancer.ingress.count","k8s.container.status.reason","k8s.pod.status_reason","k8s.persistentvolumeclaim.status.phase"]
What LinkMesh then pushes to the collector, under receivers:
k8s_cluster/input_src_00000501:
allocatable_types_to_report:
- cpu
- memory
- pods
- ephemeral-storage
auth_type: serviceAccount
collection_interval: 30s
metrics:
k8s.container.status.reason:
enabled: true
k8s.persistentvolumeclaim.status.phase:
enabled: true
k8s.pod.status_reason:
enabled: true
k8s.service.endpoint.count:
enabled: true
k8s.service.load_balancer.ingress.count:
enabled: true
node_conditions_to_report:
- Ready
- MemoryPressure
- DiskPressure
- PIDPressure
- NetworkUnavailable
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": {
"allocatable_types_to_report": [
"cpu",
"memory",
"pods",
"ephemeral-storage"
],
"auth_type": "serviceAccount",
"collection_interval": "30s",
"enabled_metrics": [
"k8s.service.endpoint.count",
"k8s.service.load_balancer.ingress.count",
"k8s.container.status.reason",
"k8s.pod.status_reason",
"k8s.persistentvolumeclaim.status.phase"
],
"node_conditions_to_report": [
"Ready",
"MemoryPressure",
"DiskPressure",
"PIDPressure",
"NetworkUnavailable"
]
},
"name": "Kubernetes Cluster Metrics",
"receiverType": "k8s_cluster"
}'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
Run exactly one replica. Every replica watches the whole cluster: in our test, with two replicas, each one reported the same 79 metrics per collection, so every series arrived twice. The Deployment uses replicas: 1 and the Recreate strategy so a rollout never runs two at once.
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 cAdvisor container metrics from the kubeletThe container_* metrics Kubernetes dashboards expect, scraped from each node's kubelet with the pod's service account token.
- 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.
- 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.