25.09.2020 von Aljosha Balitzki

Alerting für Alerting - Was, wenn eine Metrik verschwindet und keiner bekommt es mit?
Dieser Artikel beschreibt eine mögliche Lösung für dieses Problem.

Prometheus bietet uns viele Möglichkeiten unsere Systeme zu überwachen. Das Alerting hilft uns festzustellen, wenn diese Systeme in ihrer Funktionalität eingeschränkt sind.

Sobald viele verschiedene Exporter im Einsatz sind, hat man schnell eine große Menge verschiedener Metriken - einige davon werden im Alerting genutzt - und verliert leicht den Überblick.
Aktuell haben wir 38 Alerts, die sich über ca. 17 Bereiche unserer Infrastruktur erstrecken:

$ ls roles/prometheus/files/rules/alert*.yml | wc -l
17
$ grep 'expr:' roles/prometheus/files/rules/alert*.yml | wc -l
38

Jede dieser Dateien enthält Yaml, mit dem das Alerting konfiguriert wird. Darin enthalten sind eine oder mehrere Alerting-Gruppen mit jeweils einer oder mehreren Alerting-Regeln:

groups:
  - name: KubeStateMetrics
    rules:
      - alert: pod_frequently_restarting
        expr: increase(kube_pod_container_status_restarts_total{namespace!~"example"}[1h]) > 5
        labels:
          severity: warning
        annotations:
          description: Pod {{$labels.namespace}}/{{$labels.pod}} was restarted {{$value}} times within the last hour
          summary: Pod is restarting frequently

Sind Metriken aus verschiedenen Gründen nicht mehr verfügbar, kann es passieren, dass das Alerting unbemerkt verdächtig ruhig wird.
Diese Gründe müssen nichts mit einer Fehlfunktion des Systems zu tun haben. Änderungen an der eigenen Config oder umbenannte Metriken in neuen Versionen eines Exporters können Gründe dafür sein, dass Metriken nicht mehr verfügbar sind.

Meta Alerting

Unsere Lösung für dieses Problems ist es, für jede im Alerting benutzte Metrik einen weiteren Alert zu konfigurieren, der uns über eine nicht verfügbare Metrik informiert.
Die PromQL (Prometheus Query Language) bietet eine Funktion, die prüft, ob eine Metrik nicht verfügbar ist: absent.

Da wir große Fans davon sind Dinge zu automatisieren, liegt das auch hier nahe.
Unsere Idee: Für jede im Alerting verwendete Metrik einen weiteren absent-Alert zu generieren: Aus einem Alert mit einem Ausdruck wie

increase(kube_pod_container_status_restarts_total[1h]) > 5

soll

absent(kube_pod_container_status_restarts_total)

werden.

Als ersten Schritt benötigen wir also eine Liste aller im Alerting verwendeten Metriken.
Ein einfaches grep oder Regex-Matching mit z.B. Python ist nicht ausreichend, um Metriknamen zu extrahieren. Zum Einen müssen Yaml Syntax und mehrzeilige PromQL Ausdrücke berücksichtigt werden, zum Anderen müssen wir z.B. zwischen dem Namen einer Metrik und dem einer Funktion unterscheiden.
Also haben wir uns auf die Suche nach einer Bibliothek gemacht, die für uns die PromQL Ausdrücke unserer Alerts, wie das obige Beispiel, parst.

Die erste funktionierende Bibliothek in einer Sprache, die wir ausreichend gut sprechen, die wir gefunden haben ist das promql Crate in Rust. Diese Bibliothek parst PromQL in einen AST (Abstract Syntax Tree), der mit Rust enums abgebildet wird. So erhalten wir aus jeder PromQL Expression alle Informationen die wir benötigen.

Das Ergebnis ist in unserem GitLab.com Account zu finden: gitlab.com/neuland-bfi/prometheus-meta-alerting
In den Releases stehen precompiled binaries zum Download bereit und können z.B. in CI Pipelines benutzt werden.

Benutzung und Konfiguration

Das Tool erwartet eine oder mehrere Dateien mit Prometheus Alerts und eine Zieldatei als Command Line Argumente:

prometheus-meta-alerting roles/prometheus/files/rules/*.yml roles/prometheus/files/rules/meta-alerts.yml

Die erzeugten Meta Alerts werden bei erneutem Generieren ignoriert. So ist es kein Problem die generierten Alerts mit anderen Alerts zusammen zu speichern und dem Tool als Input zu geben.

Aus dem obigen Beispiel wird der folgende Meta Alert generiert:

groups:
  - name: AlertingMetaAlerts
    rules:
      - alert: metric_absent_kube_pod_container_status_restarts_total
        expr: absent(kube_pod_container_status_restarts_total) == 1
        for: 10m
        labels:
          severity: warning
          service: alerting
        annotations:
          description: "The metric `kube_pod_container_status_restarts_total` is `absent`! Go check the relevant exporter for updates"
          title: "Metric used in alert is `absent`!"

Natürlich kann das Tool auch konfiguriert werden, um beispielsweise eigene Annotationen und Labels an die erzeugten Alerts anzuhängen. So kann der eigentliche Alert im Alerting Mattermost-Channel formatiert werden.
Mit einem ignore_meta_alerting Label kann ein Alert explizit aus dem Meta Alerting ausgeschlossen werden. Dies ist nützlich, wenn ein Alert bereits absent verwendet.

Mehr zum Thema Konfiguration ist in der Readme des Projekts dokumentiert.

Deployment

In unserem Monitoring/Alerting Setup integrieren wir das prometheus-meta-alering, indem unsere CI in einem Vorbereitungsschritt die Meta Alerts erzeugt. Spätere CI Schritte verwenden diese dann, um sie mit allen anderen Alerts zusammen zu deployen.