tutorial

How to Monitor Kepler with Vigilmon

Kepler (Kubernetes-based Efficient Power Level Exporter) is an eBPF-powered tool that exposes energy consumption metrics for Kubernetes pods and nodes. It ru...

Kepler (Kubernetes-based Efficient Power Level Exporter) is an eBPF-powered tool that exposes energy consumption metrics for Kubernetes pods and nodes. It runs as a DaemonSet, uses hardware performance counters and eBPF programs to measure CPU, DRAM, and uncore energy usage, and exports the data as Prometheus metrics. Platform and sustainability engineering teams use Kepler to attribute cloud infrastructure carbon footprint to specific workloads, enforce energy budgets, and identify inefficient applications consuming disproportionate power relative to their CPU usage.

But Kepler itself needs to be monitored. If the Kepler DaemonSet crashes on a subset of nodes, those nodes stop contributing energy metrics, and your dashboards silently show incomplete data — you may draw incorrect conclusions about per-workload energy consumption during a period when coverage was partial. In this tutorial you'll set up uptime and response-time monitoring for Kepler using Vigilmon — free tier, no credit card required.


Why Kepler needs external monitoring

Internal Kubernetes probes on Kepler pods verify container liveness, but miss operational failures that affect data quality:

  • eBPF program load failure — Kepler fails to load its eBPF programs when the node kernel lacks required features (BPF_PROG_TYPE_PERF_EVENT, BPF_MAP_TYPE_PERF_ARRAY); the pod may still be Running while emitting no energy metrics
  • Perf event access denied — Linux perf_event_paranoia or seccomp settings on hardened nodes prevent Kepler from reading hardware performance counters; DRAM and uncore energy metrics disappear without a pod restart
  • RAPL interface missing — on cloud VMs or nodes without Intel RAPL (Running Average Power Limit) support, Kepler falls back to model-based estimation; this fallback may produce zero or wildly inaccurate values
  • Metrics HTTP server crash — Kepler exposes /metrics on port 9102; if this server stops responding, Prometheus stops scraping energy data for that node while the DaemonSet pod reports Healthy
  • DaemonSet gaps from node taints — after node events, new taints may prevent Kepler from scheduling; unmonitored coverage gaps persist until someone queries an affected node's metrics explicitly

External monitoring from Vigilmon watches the Kepler metrics endpoint and alerts you the moment data collection fails on any reachable node.


What you'll need

  • Kepler deployed as a DaemonSet in your Kubernetes cluster
  • The Kepler metrics service reachable externally or via Ingress
  • A free Vigilmon account

Step 1: Expose the Kepler metrics endpoint

Kepler exposes Prometheus metrics on port 9102 by default. The standard Kepler Helm chart creates a ClusterIP Service — expose it for external monitoring via NodePort or Ingress:

# Check the existing Kepler service
kubectl get svc -n kepler

# If using the Helm chart default, patch to NodePort for direct access:
kubectl patch svc kepler -n kepler \
  -p '{"spec":{"type":"NodePort","ports":[{"port":9102,"targetPort":9102,"nodePort":30902}]}}'

# Verify
kubectl get svc kepler -n kepler
# NAME     TYPE       CLUSTER-IP     EXTERNAL-IP   PORT(S)          AGE
# kepler   NodePort   10.96.132.44   <none>        9102:30902/TCP   5m

For Ingress-based exposure:

# kepler-ingress.yaml
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: kepler-metrics-ingress
  namespace: kepler
  annotations:
    nginx.ingress.kubernetes.io/rewrite-target: /$2
spec:
  ingressClassName: nginx
  rules:
    - host: energy.yourdomain.com
      http:
        paths:
          - path: /kepler(/|$)(.*)
            pathType: Prefix
            backend:
              service:
                name: kepler
                port:
                  number: 9102
kubectl apply -f kepler-ingress.yaml

# Verify the metrics endpoint
curl https://energy.yourdomain.com/kepler/metrics | head -20
# # HELP kepler_container_joules_total Aggregated RAPL Package/DRAM/GPU/Other in joules
# # TYPE kepler_container_joules_total counter
# kepler_container_joules_total{...} 1234.5

Step 2: Add a dedicated Kepler health endpoint

Kepler's /metrics endpoint returns data when the exporter is healthy but provides no explicit /healthz path. Add a sidecar or init check that verifies Kepler is actually collecting energy data (not just serving empty metrics):

# kepler-healthcheck-configmap.yaml
apiVersion: v1
kind: ConfigMap
metadata:
  name: kepler-health-script
  namespace: kepler
data:
  check.sh: |
    #!/bin/sh
    # Verify Kepler is exporting non-zero energy metrics
    METRICS=$(curl -sf http://localhost:9102/metrics 2>/dev/null)
    if [ -z "$METRICS" ]; then
      echo "ERROR: metrics endpoint not responding" >&2
      exit 1
    fi
    # Check that at least one energy counter is present
    if echo "$METRICS" | grep -q "kepler_node_core_joules_total\|kepler_container_joules_total"; then
      echo "ok: energy metrics present"
      exit 0
    else
      echo "WARN: Kepler running but no energy metrics exported (check RAPL/eBPF support)"
      exit 1
    fi

Apply and exec as a scheduled probe using a CronJob or use a separate lightweight probe Deployment that calls the Kepler service and exposes a /healthz for Vigilmon:

# kepler-health-probe.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: kepler-health-probe
  namespace: kepler
spec:
  replicas: 1
  selector:
    matchLabels:
      app: kepler-health-probe
  template:
    metadata:
      labels:
        app: kepler-health-probe
    spec:
      containers:
        - name: probe
          image: curlimages/curl:latest
          command:
            - /bin/sh
            - -c
            - |
              while true; do
                METRICS=$(curl -sf http://kepler.kepler.svc.cluster.local:9102/metrics 2>/dev/null)
                if echo "$METRICS" | grep -q "kepler_node_core_joules_total\|kepler_container_joules_total"; then
                  echo "healthy" > /tmp/status
                else
                  echo "unhealthy" > /tmp/status
                fi
                sleep 30
              done &
              while true; do
                STATUS=$(cat /tmp/status 2>/dev/null || echo "starting")
                CODE="200"; [ "$STATUS" != "healthy" ] && CODE="503"
                printf "HTTP/1.1 $CODE OK\r\nContent-Length: ${#STATUS}\r\n\r\n$STATUS" | nc -l -p 8888 -q 1
              done
          ports:
            - containerPort: 8888
              name: healthz
---
apiVersion: v1
kind: Service
metadata:
  name: kepler-health-probe
  namespace: kepler
spec:
  type: ClusterIP
  selector:
    app: kepler-health-probe
  ports:
    - port: 8888
      targetPort: 8888

Step 3: Set up HTTP monitoring in Vigilmon

With your endpoints accessible, add them to Vigilmon:

  1. Log in to vigilmon.online and go to Monitors → New Monitor
  2. Choose HTTP / HTTPS
  3. Add a monitor for each endpoint:

| Monitor name | URL | Expected status | |---|---|---| | Kepler metrics endpoint | https://energy.yourdomain.com/kepler/metrics | 200 | | Kepler energy data health | https://energy.yourdomain.com/kepler/healthz | 200 |

  1. Set the check interval to 1 minute
  2. For the metrics monitor, under Expected response, match kepler_node_core_joules_total in the response body — this confirms actual energy data is present, not just an empty Prometheus endpoint
  3. Save each monitor

Vigilmon probes from multiple geographic regions simultaneously. If your Kepler Ingress is unreachable from outside your cluster, Vigilmon will detect this even when internal scraping appears healthy.


Step 4: Monitor Kepler TCP port

Add a TCP-layer monitor to detect network-level failures independently of the metric content:

  1. Go to Monitors → New Monitor
  2. Choose TCP Port
  3. Enter your Kepler service hostname and port 9102 (or NodePort 30902)
  4. Save the monitor

A TCP failure when Kepler pods are Running usually indicates a Service selector mismatch, NetworkPolicy blocking, or a NodePort conflict introduced during a cluster upgrade.


Step 5: Configure alert channels

Energy data gaps silently corrupt your carbon attribution dashboards and workload efficiency reports. Alert on Kepler health proactively.

Email alerts

  1. Go to Alert Channels → Add Channel → Email
  2. Enter your platform engineering or sustainability team address
  3. Assign the channel to all Kepler monitors

Webhook alerts for incident routing

  1. Go to Alert Channels → Add Channel → Webhook
  2. Enter your incident management webhook URL (PagerDuty, Opsgenie, Slack)
  3. The payload Vigilmon sends:
{
  "monitor_name": "Kepler metrics endpoint",
  "status": "down",
  "url": "https://energy.yourdomain.com/kepler/metrics",
  "started_at": "2024-01-15T10:23:00Z",
  "duration_seconds": 120
}

Wire this alert to a runbook that identifies which nodes have lost Kepler coverage and marks the corresponding time window as having incomplete energy data in your dashboards.


Step 6: Correlate Vigilmon alerts with Kepler diagnostics

When you receive a Kepler downtime alert, run these checks:

# 1. Check Kepler DaemonSet pod status across all nodes
kubectl get pods -n kepler -l app.kubernetes.io/name=kepler -o wide

# 2. Check for pods missing on specific nodes (gaps in DaemonSet coverage)
kubectl describe ds kepler -n kepler | grep -E "Desired|Ready|Available|Scheduled"

# 3. Check Kepler logs for eBPF or RAPL errors
kubectl logs -n kepler -l app.kubernetes.io/name=kepler --tail=100 \
  | grep -i "error\|fail\|RAPL\|BPF\|perf"

# 4. Verify the metrics endpoint is reachable from inside the cluster
kubectl run curl-test --image=curlimages/curl:latest --restart=Never -it --rm \
  -- curl -sf http://kepler.kepler.svc.cluster.local:9102/metrics | head -5

# 5. Check whether RAPL is available on affected nodes
kubectl debug node/<node-name> -it --image=ubuntu -- ls /sys/class/powercap/intel-rapl

# 6. Check perf_event_paranoia setting
kubectl debug node/<node-name> -it --image=ubuntu -- cat /proc/sys/kernel/perf_event_paranoid

# 7. Verify Kepler exports non-zero energy values
kubectl exec -n kepler <pod-name> -- curl -sf http://localhost:9102/metrics \
  | grep kepler_node_core_joules_total | head -5

If Vigilmon shows the metrics endpoint returning 200 but your health check fails the body match, Kepler is running but not collecting energy data — check RAPL availability and perf_event_paranoid settings on the affected nodes.


Step 7: Create a status page for energy observability

Energy monitoring data feeds sustainability reporting and workload efficiency reviews that leadership depends on. Make Kepler's health transparent:

  1. Go to Status Pages → New Status Page
  2. Name it: "Energy Observability (Kepler)"
  3. Add your monitors: Kepler metrics, Kepler health probe, TCP port
  4. Share the URL with sustainability engineering, platform, and finance teams who rely on energy attribution data

Summary

| What you set up | What it catches | |---|---| | HTTP monitor on /metrics + body match | eBPF/RAPL failures, empty metrics export | | HTTP monitor on health probe | End-to-end data collection validation | | TCP monitor on metrics port | Service selector drift, NetworkPolicy blocks | | Email + webhook alert channels | Immediate notification when energy data gaps appear | | Status page | Team visibility into energy observability health |

Kepler transforms raw hardware counters into actionable per-workload energy data — but only when every DaemonSet pod is healthy and its eBPF programs are loaded. External monitoring is the only reliable way to know when that data has silently stopped flowing.

Get started at vigilmon.online — free tier, no credit card required.

Monitor your app with Vigilmon

Free plan — 5 monitors, no credit card required. Up and running in 60 seconds.

Start free →