In-Cluster Prometheus

2022-07-27

Up until recently, I was running two separate Prometheus instances – one on a Raspberry Pi, and the other in my k3s cluster using kube-prometheus-stack. I wanted to unify them, ideally to simplify management and version control. The challenge here is in how to manage the scrape targets for out-of-cluster resources.

Thanks to my friend Justin, I was able to use a much more elegant solution.

Options

The basic way

When deploying kube-prometheus-stack, define additionalScrapeConfigs. Since I deployed via the Helm chart, that would mean doing a helm upgrade each time I needed to change things. Gross.

The documented way

If you search how to use an in-cluster Prometheus instance to monitor out-of-cluster things, the suggestion is straightforward but complicated. Essentially, define Endpoint resources for each out-of-cluster thing and then set up ServiceMonitors to get Prometheus to scrape things.

This is gross, and requires 2 resources for each scrape, plus adds extra overhead to your cluster.

The best way

Justin clued me into the fact that you can (ab)use Probes to do your bidding. Here’s a straightforward example you might use to scrape node_exporter on an external host called myserver.example.com:

---
apiVersion: monitoring.coreos.com/v1
kind: Probe
metadata:
  namespace: prometheus
  name: myserver-node-exporter
  labels:
    release: prometheus-kube-prometheus-stack
spec:
  jobName: myserver-node-exporter
  prober:
    url: prometheus-kube-prometheus-prometheus:9090
    path: /metrics
  targets:
    staticConfig:
      static:
        - myserver.example.com:9100
      relabelingConfigs:
        - sourceLabels:
            - instance
          targetLabel: "__address__"
          action: replace
        - regex: "__param_target"
          action: labeldrop

The only change you’d need to make here is the hostname/port in .spec.targets.staticConfig.static. All the rest should stay as is (assuming you don’t need to change the labels the operator listens on, the namespace, or the service name). In particular, the relabelingConfigs and the .spec.prober.url are written as intendend and shouldn’t be changed. Only change .spec.prober.path if your exporter is exporting at a different path.

If you need to adjust the scheme, that’s set on .spec.prober.scheme. If you need a bearer token, you set .spec.bearerTokenSecret like so:

spec:
  bearerTokenSecret:
    name: my-secret-name
    key: token

and then plop the bearer token in the given secret.

Limitations

The best way has at least one limitation: you can’t define params on the probe, so if you need those you’ll have to work around it.

Enter your instance's address