Grafana Grafana

Grafana mit dem Grafana Operator verwalten

Deklariere Instanzen, Ordner, Dashboards, Datenquellen und Alert-Regeln als Kubernetes Custom Resources.

15 min Experte

Der Grafana Operator verwandelt jede Grafana-Ressource in ein Kubernetes-Objekt. Ein GrafanaDashboard ist ein Manifest, eine GrafanaDatasource ist ein Manifest, und der Operator gleicht sie über die API mit einer Grafana-Instanz ab. Das passt natürlich zu GitOps-Werkzeugen wie Flux und Argo CD, und es funktioniert für Grafana-Instanzen innerhalb oder außerhalb des Clusters, einschließlich Grafana Cloud. Alle Ressourcen verwenden apiVersion: grafana.integreatly.org/v1beta1.

Den Operator installieren

Installiere ihn mit Helm aus seinem OCI-Chart (prüfe die Installationsdokumentation des Operators auf den aktuellen Chart-Ort und die Version und pinne sie):

helm upgrade -i grafana-operator \
  oci://ghcr.io/grafana/helm-charts/grafana-operator \
  --namespace grafana --create-namespace \
  --version <operator-version>

Die Dokumentation des Operators beschreibt auch eine Kustomize-Installation. Mit Flux packst du dasselbe Chart in ein HelmRelease.

Die Instanz beschreiben

Eine Instanz kann verwaltet sein (der Operator deployt Grafana) oder extern (er verbindet sich mit einer bereits existierenden). Eine verwaltete Instanz:

apiVersion: grafana.integreatly.org/v1beta1
kind: Grafana
metadata:
  name: grafana
  namespace: grafana
  labels:
    dashboards: "grafana"
spec:
  config:
    log:
      mode: console
    security:
      admin_user: root
      admin_password: secret     # nur ein Beispiel; siehe unten

Eine externe Instanz, etwa ein Grafana-Cloud-Stack, zeigt auf eine URL und nimmt ein Token aus einem Secret:

apiVersion: grafana.integreatly.org/v1beta1
kind: Grafana
metadata:
  name: my-stack
  namespace: grafana
  labels:
    dashboards: my-stack
spec:
  external:
    url: https://my-stack.grafana.net/
    apiKey:
      name: grafana-cloud-credentials
      key: GRAFANA_CLOUD_INSTANCE_TOKEN

Der Block spec.config nimmt dieselben Sections und Schlüssel wie grafana.ini. Schreibe kein echtes Passwort in ein Manifest. Die Dokumentation des Operators zeigt, wie du Admin-Zugangsdaten aus einem Secret liest.

Die Labels der Instanz sind wichtig: Jede andere Ressource wählt ihre Ziel-Instanz mit instanceSelector.matchLabels über diese Labels aus.

Ordner

apiVersion: grafana.integreatly.org/v1beta1
kind: GrafanaFolder
metadata:
  name: applications
spec:
  instanceSelector:
    matchLabels:
      dashboards: "grafana"
  title: Applications
  uid: applications     # setze eine stabile UID, damit andere Ressourcen darauf verweisen können

Wird title weggelassen, ist der Standard metadata.name. Setze spec.uid, wann immer andere Ressourcen mit folderRef auf den Ordner verweisen, denn ohne sie kann der Reconciler einen bestehenden Ordner mit demselben Namen übernehmen.

Datenquellen

apiVersion: grafana.integreatly.org/v1beta1
kind: GrafanaDatasource
metadata:
  name: prometheus
spec:
  instanceSelector:
    matchLabels:
      dashboards: "grafana"
  datasource:
    name: prometheus
    type: prometheus
    access: proxy
    url: http://prometheus-service:9090
    isDefault: true
    jsonData:
      timeInterval: 5s

Zugangsdaten kommen aus einem Secret im selben Namespace über valuesFrom, sodass sie nie im Manifest erscheinen:

apiVersion: v1
kind: Secret
metadata:
  name: credentials
type: Opaque
stringData:
  PROMETHEUS_USERNAME: grafana
  PROMETHEUS_PASSWORD: change-me
---
apiVersion: grafana.integreatly.org/v1beta1
kind: GrafanaDatasource
metadata:
  name: prometheus-secured
spec:
  instanceSelector:
    matchLabels:
      dashboards: "grafana"
  valuesFrom:
    - targetPath: basicAuthUser
      valueFrom:
        secretKeyRef:
          name: credentials
          key: PROMETHEUS_USERNAME
    - targetPath: secureJsonData.basicAuthPassword
      valueFrom:
        secretKeyRef:
          name: credentials
          key: PROMETHEUS_PASSWORD
  datasource:
    name: prometheus-secured
    type: prometheus
    access: proxy
    basicAuth: true
    url: http://prometheus-service:9090
    secureJsonData:
      basicAuthPassword: ${PROMETHEUS_PASSWORD}

Schreibe keine Zugangsdaten in die url. Verschlüssele das Secret oder synchronisiere es mit SOPS oder External Secrets, wie bei jedem Kubernetes-Secret.

Dashboards

Ein GrafanaDashboard kann sein JSON aus mehreren Quellen beziehen, was die größte Stärke des Operators ist:

# Inline-JSON
apiVersion: grafana.integreatly.org/v1beta1
kind: GrafanaDashboard
metadata:
  name: simple
spec:
  resyncPeriod: 30s
  instanceSelector:
    matchLabels:
      dashboards: "grafana"
  folderRef: applications
  json: >
    { "uid": "simple", "title": "Simple Dashboard", "panels": [] }
# aus einer ConfigMap
spec:
  instanceSelector:
    matchLabels:
      dashboards: "grafana"
  configMapRef:
    name: dashboard-definition
    key: json
# aus einer URL, etwa grafana.com oder eine rohe Git-Datei
spec:
  instanceSelector:
    matchLabels:
      dashboards: "grafana"
  url: "https://grafana.com/api/dashboards/1860/revisions/30/download"
# aus einem OCI-Artefakt
spec:
  instanceSelector:
    matchLabels:
      dashboards: "grafana"
  oci:
    reference: ghcr.io/team-a/dashboards:v1.4.7
    path: board.json
    pullSecretRef:
      name: ghcr-pull

Weitere Felder, die du nutzen wirst:

  • folderRef verweist per Namen auf ein GrafanaFolder, und folderUID auf die UID eines bestehenden Ordners.
  • gzipJson hält das Dashboard als Base64-kodiertes Gzip (cat dashboard.json | gzip | base64 -w0), für Dashboards, die für ein Manifest zu groß sind.
  • variables überschreibt Template-Variablen, zum Beispiel um pro Umgebung einen namespace oder eine Datenquelle zu setzen.
  • resyncPeriod und contentCacheDuration steuern, wie oft Inhalte erneut angewendet werden und wie lange abgerufene URLs im Cache bleiben.

Alert-Regeln

apiVersion: grafana.integreatly.org/v1beta1
kind: GrafanaAlertRuleGroup
metadata:
  name: webapp-alerts
spec:
  folderRef: applications
  instanceSelector:
    matchLabels:
      dashboards: "grafana"
  interval: 5m
  rules:
    - uid: webapp-down
      title: Webapp targets down
      condition: C
      for: 5m
      noDataState: NoData
      execErrState: Error
      data:
        - refId: A
          # der Prometheus-Query-Schritt, wie in der Alerting-Anleitung
        - refId: C
          # der Threshold-Ausdrucksschritt

Jede Regelgruppe braucht einen Ordner und mindestens eine Regel. Die Daten-Schritte nutzen dieselbe Struktur wie Provisioning-Dateien, du kannst also das YAML aus der Alerting-Anleitung wiederverwenden. Der Operator hat auch Ressourcen für Contact Points; die aktuelle Liste findest du in seinen Beispielen.

Betrieb

kubectl get grafanas,grafanafolders,grafanadatasources,grafanadashboards -A
kubectl describe grafanadashboard simple -n grafana

describe zeigt die Status-Bedingungen der Ressource, die sagen, ob der letzte Sync nach Grafana erfolgreich war und warum nicht, falls er fehlschlug. Häufige Ursachen: ein instanceSelector, der keine Instanz trifft, ein falsches Token oder ein Secret im falschen Namespace.

Operator oder Datei-Provisioning?

Nutze den Operator, wenn du bereits GitOps auf Kubernetes betreibst, jedes Team seine Dashboards und Alerts als eigene Objekte besitzen soll oder du Grafana Cloud oder mehrere Instanzen verwaltest. Nutze die Provisioning-Values des Helm-Charts, wenn du ein Grafana und eine Handvoll Ressourcen hast, da das keinen zusätzlichen Controller hinzufügt. Beide können nebeneinander existieren, aber lass ein Werkzeug jede Ressource besitzen.

Beliebte Anleitungen