Deployment

Metrics Usage is a single binary that can play two roles depending on its configuration:

  • a central instance, which stores the data and exposes the API,
  • a sidecar, which collects data close to the source and pushes it to a central instance.

These roles can be combined, so you can choose the topology that fits the size of your infrastructure.

Central instance#

In the simplest setup, a single Metrics Usage instance collects everything: the list of metrics, the rules and the dashboards. It pulls the data directly from each source and keeps it in its own database.

flowchart LR
    subgraph sources[Sources]
        prometheus[(Prometheus)]
        ruler1[(Thanos Ruler 1)]
        ruler2[(Thanos Ruler 2)]
        perses[(Perses)]
        grafana[(Grafana)]
    end

    mu[Metrics Usage]

    mu -->|"collect metrics"| prometheus
    mu -->|"collect rules"| prometheus
    mu -->|"collect rules"| ruler1
    mu -->|"collect rules"| ruler2
    mu -->|"collect dashboards"| perses
    mu -->|"collect dashboards"| grafana

The corresponding configuration:

metric_collector:
  enable: true
  http_client:
    url: "https://prometheus.example.com"

rules_collectors:
  - enable: true
    prometheus_client:
      url: "https://prometheus.example.com"
  - enable: true
    prometheus_client:
      url: "https://thanos-ruler1.example.com"
  - enable: true
    prometheus_client:
      url: "https://thanos-ruler2.example.com"

perses_collector:
  enable: true
  perses_client:
    url: "https://perses.example.com"

grafana_collector:
  enable: true
  grafana_client:
    url: "https://grafana.example.com"

As the collected data is stateful, you probably want to persist it so it survives a restart. In this case, configure the database to be stored in a file, on a persistent volume:

database:
  in_memory: false
  path: "/data/metrics-usage.json"

Sidecar for rules collection#

In setups with numerous rules (for example many Prometheus or Thanos Ruler instances), collecting everything from a central instance can become impractical due to the volume of data and the number of endpoints to reach.

Instead, you can deploy Metrics Usage as a sidecar container next to each Prometheus / Thanos Ruler. The sidecar collects the rules locally, analyzes them, and pushes the resulting usage to the central instance through its API. The central instance keeps collecting the list of metrics (here from a Thanos Querier) and the dashboards.

flowchart LR
    subgraph pod1[Pod]
        ruler1[(Thanos Ruler 1)]
        sidecar1[Metrics Usage<br/>sidecar]
    end
    subgraph pod2[Pod]
        ruler2[(Thanos Ruler 2)]
        sidecar2[Metrics Usage<br/>sidecar]
    end

    querier[(Thanos Querier)]
    perses[(Perses)]
    grafana[(Grafana)]
    central[Metrics Usage<br/>central]

    sidecar1 -->|"get rules"| ruler1
    sidecar2 -->|"get rules"| ruler2
    sidecar1 -->|"push metrics usage"| central
    sidecar2 -->|"push metrics usage"| central

    central -->|"collect metrics"| querier
    central -->|"collect dashboards"| perses
    central -->|"collect dashboards"| grafana

The configuration of each sidecar only enables a rules collector with a metric_usage_client pointing to the central instance:

rules_collectors:
  - enable: true
    prometheus_client:
      url: "http://localhost:10902"
    # The URL used in the usage to link to the rules. Useful when the rules are reached through localhost.
    public_url: "https://thanos-ruler1.example.com"
    metric_usage_client:
      url: "https://metrics-usage.example.com"

The configuration of the central instance doesn't need anything specific to receive the usage pushed by the sidecars:

metric_collector:
  enable: true
  http_client:
    url: "https://thanos-querier.example.com"

perses_collector:
  enable: true
  perses_client:
    url: "https://perses.example.com"

grafana_collector:
  enable: true
  grafana_client:
    url: "https://grafana.example.com"

The same pattern can be applied to the other collectors: the labels collector, the Perses collector and the Grafana collector also support the metric_usage_client setting.

Pushing rules from your CI#

If your rules are stored in a git repository, you can also push them directly to the central instance from your CI, without deploying any sidecar. The endpoint /api/v1/rules accepts the rule groups in the same format as the Prometheus API (/api/v1/rules), along with the source they come from:

curl -X POST "https://metrics-usage.example.com/api/v1/rules" \
  -H "Content-Type: application/json" \
  -d '{
    "source": "https://github.com/my-org/my-rules",
    "groups": [
      {
        "name": "node-exporter",
        "file": "node-exporter.yaml",
        "interval": 60,
        "rules": [
          {
            "type": "alerting",
            "name": "NodeCPUHighUsage",
            "query": "sum without (mode) (avg without (cpu) (rate(node_cpu_seconds_total{mode!=\"idle\"}[2m]))) * 100 > 90",
            "duration": 900,
            "labels": {},
            "annotations": {},
            "alerts": [],
            "health": "ok",
            "lastError": "",
            "evaluationTime": 0,
            "lastEvaluation": "0001-01-01T00:00:00Z",
            "state": "inactive"
          }
        ]
      }
    ]
  }'