Info
License: Free — part of the universal collection tier.
Kubernetes¶
The kubernetes probe monitors a Kubernetes cluster via the API server,
collecting node health and capacity, pod and container state, every workload
kind, storage, quotas and autoscaling — and publishing the cluster's own
Events on the log rail, which is where Kubernetes explains why a metric
moved.
Quick start¶
In-cluster (agent runs as a Pod with a ServiceAccount):
# probes.d/10-kubernetes.yaml — each file under probes.d/ is a YAML array of probes
- name: kubernetes
type: kubernetes
Out-of-cluster (agent runs outside the cluster):
# probes.d/10-kubernetes.yaml
- name: kubernetes
type: kubernetes
params:
kubeconfig: /home/agent/.kube/config
Parameters¶
| Parameter | Default | Description |
|---|---|---|
kubeconfig |
— | Path to a kubeconfig file. When empty, the probe uses the in-cluster ServiceAccount token |
namespaces.include |
all | List of namespaces to monitor |
namespaces.exclude |
[kube-system] |
Namespaces to skip |
collect.nodes |
true |
Node health, capacity and pressure conditions |
collect.pods |
true |
Pod phase, readiness, restarts and resource requests |
collect.containers |
true |
Per-container state, waiting reason and resources |
collect.deployments |
true |
Deployment replica health |
collect.statefulsets |
true |
StatefulSet replica health |
collect.daemonsets |
true |
DaemonSet scheduling health |
collect.replicasets |
false |
Off by default: a Deployment owns one ReplicaSet per revision, so they multiply series without adding a fact the Deployment does not carry. Turn on while chasing a stuck rollout, where the previous ReplicaSet staying non-zero is exactly the symptom |
collect.jobs |
true |
Job active/succeeded/failed counts |
collect.cronjobs |
true |
CronJob active jobs and suspended state |
collect.storage |
true |
PersistentVolumes and Claims |
collect.quotas |
true |
ResourceQuota hard limits and use |
collect.autoscalers |
true |
HorizontalPodAutoscaler replica counts |
collect.events |
true |
Cluster Events, published on the log rail |
Metrics¶
| Metric | Unit | Description |
|---|---|---|
senhub.kubernetes.up |
1 | 1 when the API server is reachable |
k8s.node.ready |
1 | 1 when the node reports the Ready condition, tagged with k8s.node.name |
k8s.node.cpu.allocatable |
{cpu} | Allocatable CPU on the node |
k8s.node.memory.allocatable |
By | Allocatable memory on the node |
k8s.pod.phase |
1 | 1 for each pod in each phase (Running/Pending/Succeeded/Failed/Unknown), tagged with k8s.pod.name/k8s.namespace.name/phase |
k8s.container.restarts |
{restart} | Container restart count, tagged with k8s.container.name/k8s.pod.name |
k8s.deployment.available |
{pod} | Available replicas per deployment, tagged with k8s.deployment.name/k8s.namespace.name |
k8s.deployment.desired |
{pod} | Desired replicas per deployment |
k8s.node.condition.disk_pressure |
1 | 1 means the pressure IS present — the opposite polarity to ready. A node under disk pressure is still Ready right up to the moment it is not: the kubelet begins evicting pods while readiness stays true. Same shape for memory_pressure, pid_pressure, network_unavailable |
k8s.pod.cpu.request / .memory.request |
1 / By | What the scheduler committed for this pod, summed over its containers. With the limits below, this is what answers "is this cluster over-committed" — a question that cannot be answered retroactively without the series |
k8s.pod.cpu.limit / .memory.limit |
1 / By | The ceiling before throttling or an OOM kill. Absent when no limit is set: a pod with no limit is unbounded, which is a different fact from a limit of zero |
k8s.container.waiting |
1 | 1 while the container waits, tagged with the reason. ready=0 with rising restarts describes both CrashLoopBackOff and an image still pulling — the reason is what separates them, and they call for opposite reactions |
k8s.statefulset.*, k8s.daemonset.*, k8s.job.*, k8s.cronjob.* |
1 | Desired against actual per workload, tagged k8s.workload.name / k8s.workload.kind |
k8s.persistentvolumeclaim.phase |
1 | One series per phase. A claim stuck Pending is why the pod that wants it never starts — indistinguishable, from the pod's own metrics, from a cluster simply out of CPU |
k8s.resourcequota.hard / .used |
1 | The pair is the point: a quota at 99 % is why a deployment will not scale, and neither number alone says so |
k8s.hpa.current_replicas / .max_replicas |
1 | An autoscaler pinned at max is the cluster refusing to grow, invisible from the workload's own replica counts |
Cluster Events¶
Events ride the log rail, not the metric rail: they are timestamped
sentences — 0/5 nodes are available: 5 Insufficient cpu, Failed to pull
image, Liveness probe failed — and counting them would keep the number
while throwing away the diagnosis.
A Kubernetes Warning is recorded at Error severity. Kubernetes has only
two event types and no error level, so a failed mount and an OOM kill arrive
at the same level as routine notices; recording them as warnings buries them.
Each record carries the identity label its subject's metrics already carry —
an event about a pod carries k8s.pod.name — so you can go from "why did
this pod restart" to that pod's series without parsing the message.
Only new events are published. The API returns its whole retention window on every call, and the first cycle only sets the cursor: replaying that window at start would deliver an hour of past incidents as though they were happening now.
Operational notes¶
- For in-cluster operation, create a
ClusterRolegrantingget/list/watchonnodes,pods,deployments,statefulsets,daemonsets,replicasets,jobs,cronjobs,persistentvolumes,persistentvolumeclaims,resourcequotas,horizontalpodautoscalersandevents, and bind it to the agent's ServiceAccount. A missing permission disables that collector only — the others keep reporting, and the gap is logged rather than presenting as an empty cluster. - Also grant
geton thekube-systemnamespace. The cluster entity is keyed on that namespace's UID, which is the only identifier the cluster reports about itself. Without it the identity falls back to the API server address, which re-keys the cluster in the graph on any endpoint change and collides between clusters sharing an address. - The
kube-systemnamespace is excluded by default. Override withnamespaces.include/namespaces.excludeas needed. - Metric names follow the
k8s.*OTel semantic conventions aligned with the OpenTelemetry Kubernetes specification.