Grafana Grafana

Dashboards aus JSON-Dateien provisionieren

Lade Dashboards von der Platte, bilde Ordner aus Verzeichnissen ab und erstelle sie, ohne Änderungen zu verlieren.

12 min Fortgeschritten

Dashboards sind JSON-Dokumente, die sich leicht in Git ablegen lassen. Ein Dashboard-Provider sagt Grafana, welchen Ordner es beobachten soll, und Grafana lädt jede dort gefundene JSON-Datei und prüft danach weiter auf Änderungen. Diese Anleitung behandelt die Provider-Datei, die Ordnerzuordnung, das Dashboard-JSON selbst und einen Workflow, um Dashboards zu erstellen, ohne bei reinen UI-Bearbeitungen zu landen.

Die Provider-Datei

provisioning/dashboards/providers.yaml:

apiVersion: 1

providers:
  - name: default
    orgId: 1
    type: file
    disableDeletion: false
    updateIntervalSeconds: 30
    allowUiUpdates: false
    options:
      path: /var/lib/grafana/dashboards
      foldersFromFilesStructure: true

Was jedes Feld tut:

  • name ist erforderlich und muss über alle Provider eindeutig sein.
  • options.path ist das Verzeichnis, das Grafana nach JSON-Dateien scannt. Es muss im Container existieren.
  • updateIntervalSeconds gibt an, wie oft Grafana den Pfad neu scannt. Alle Dashboards werden beim Start geladen, und Änderungen werden danach ohne Neustart aufgegriffen.
  • allowUiUpdates: Bei false kann ein provisioniertes Dashboard nicht aus der UI gespeichert werden, was Git maßgeblich hält. Bei true bleiben UI-Speicherungen in der Datenbank erhalten, bis sich die Quelldatei ändert, woraufhin die Datei sie überschreibt.
  • disableDeletion: Bei true entfernt das Löschen einer Datei das Dashboard nicht aus Grafana. Lass es auf false, wenn Git die Wahrheit sein soll.
  • folder oder folderUid legen alle Dashboards in einen benannten Ordner. Ohne sie landen Dashboards im Stammverzeichnis.
  • foldersFromFilesStructure: true bildet Unterverzeichnisse von path auf Grafana-Ordner ab. Lass folder und folderUid leer, wenn du es nutzt. Verschachtelte Ordner werden nicht unterstützt, eine Ebene Unterverzeichnis ist also das Limit.

Das Repository nach Ordnern organisieren

Mit foldersFromFilesStructure: true erzeugt dieses Layout:

dashboards/
├── infrastructure/
│   ├── node-exporter.json
│   └── kubernetes.json
└── applications/
    └── webapp.json

zwei Grafana-Ordner, infrastructure und applications, mit den Dashboards darin. Nutze stattdessen separate Provider, wenn du pro Gruppe unterschiedliche Einstellungen brauchst, etwa einen Provider mit allowUiUpdates: true für einen „Scratch“-Bereich.

Grundlagen des Dashboard-JSON

Eine provisionierte Datei muss das Dashboard-Modell selbst enthalten, mit uid und title auf oberster Ebene. Verpacke es nicht in den Umschlag {"dashboard": {...}, "overwrite": true}, den die HTTP-API nutzt, was eine häufige Ursache für den Fehler „Dashboard title cannot be empty“ ist.

{
  "uid": "webapp-overview",
  "title": "Webapp Overview",
  "tags": ["webapp", "provisioned"],
  "schemaVersion": 39,
  "version": 1,
  "refresh": "30s",
  "time": { "from": "now-6h", "to": "now" },
  "templating": {
    "list": [
      {
        "name": "namespace",
        "type": "query",
        "datasource": { "type": "prometheus", "uid": "prometheus" },
        "query": "label_values(kube_pod_info, namespace)"
      }
    ]
  },
  "panels": [
    {
      "id": 1,
      "type": "timeseries",
      "title": "Request rate",
      "gridPos": { "h": 8, "w": 12, "x": 0, "y": 0 },
      "datasource": { "type": "prometheus", "uid": "prometheus" },
      "targets": [
        {
          "refId": "A",
          "expr": "sum(rate(http_requests_total{namespace=\"$namespace\"}[5m]))",
          "datasource": { "type": "prometheus", "uid": "prometheus" }
        }
      ]
    }
  ]
}

Richtlinien für Dashboards, die sich als Code gut verhalten:

  • Setze eine stabile uid. Grafana aktualisiert bei jedem Laden das Dashboard mit dieser UID. Vermeide es, eine UID über Dateien hinweg wiederzuverwenden oder denselben Titel zweimal in einem Ordner zu haben.
  • Lass die numerische id weg. Sie wird von der Datenbank vergeben und unterscheidet sich zwischen Instanzen.
  • Referenziere Datenquellen per UID, passend zur uid, die du provisioniert hast ({"type": "prometheus", "uid": "prometheus"}). Zum Teilen exportierte Dashboards können Platzhalter wie ${DS_PROMETHEUS} enthalten, die du durch die echte UID oder eine Template-Variable für Datenquellen ersetzen musst.
  • Interpolation von Umgebungsvariablen funktioniert innerhalb von Dashboard-JSON nicht. Nutze Dashboard-Template-Variablen für Unterschiede pro Umgebung.
  • Grafana 13 speichert Dashboards intern in einem neueren Schema und migriert älteres JSON beim Laden, bestehende Dateien funktionieren also weiter.

In der UI erstellen, dann das Ergebnis zurück nach Git bringen

Panel-JSON von Hand zu schreiben ist langsam, daher entwerfen die meisten Teams in der UI und exportieren. Ein Workflow, der Dateien als Wahrheit hält:

  1. Betreibe eine Scratch-Instanz (das Compose-Setup aus der früheren Anleitung), in der ein Provider allowUiUpdates: true hat.
  2. Baue oder verfeinere das Dashboard in der UI.
  3. Exportiere es als JSON aus dem Teilen- oder Export-Menü des Dashboards. Wähle die Option, die zum externen Teilen exportiert, wenn Datenquellen durch Eingaben ersetzt werden sollen, und setze dann die echten UIDs.
  4. Entferne die numerische id, setze eine stabile uid und committe die Datei in den Repository-Ordner.
  5. Öffne einen Pull Request. Das Produktions-Grafana greift sie nach dem Merge auf, und allowUiUpdates: false verhindert dort, dass jemand abweicht.

Du kannst ein bestehendes Dashboard auch über die API abrufen:

curl -s -u admin:$GRAFANA_ADMIN_PASSWORD \
  http://localhost:3000/api/dashboards/uid/webapp-overview \
  | jq '.dashboard | del(.id)' > dashboards/applications/webapp.json

Formatiere das JSON einheitlich (zum Beispiel jq -S .), damit Diffs echte Änderungen zeigen.

Dashboards aus Code erzeugen

Wenn viele Dashboards eine Form teilen, erzeuge das JSON, statt es zu kopieren. Das Grafana Foundation SDK (Go, TypeScript, Python, PHP, Java) baut Dashboards aus typisiertem Code, und seine Ausgabe ist dasselbe JSON, das du in den Provider-Ordner legst. Die Anleitung zum Foundation SDK in dieser Reihe behandelt es.

In CI validieren

Fange Fehler ab, bevor sie Grafana erreichen:

# gültiges JSON?
jq empty dashboards/**/*.json
# hat jede Datei eine uid und einen title?
for f in dashboards/*/*.json; do
  jq -e '.uid and .title' "$f" >/dev/null || echo "missing uid/title: $f"
done
# doppelte uids?
jq -r '.uid' dashboards/*/*.json | sort | uniq -d

Anwenden und prüfen

Änderungen werden innerhalb von updateIntervalSeconds aufgegriffen. Um einen Rescan zu erzwingen:

curl -X POST -u admin:$GRAFANA_ADMIN_PASSWORD \
  http://localhost:3000/api/admin/provisioning/dashboards/reload

curl -s -u admin:$GRAFANA_ADMIN_PASSWORD \
  "http://localhost:3000/api/search?type=dash-db" | jq '.[] | {title, uid, folderTitle}'

Erscheint ein Dashboard nicht, prüfe die Logzeilen von Start und Rescan aus dem Provisioning-Logger. Die üblichen Ursachen sind ein Pfad, der im Container nicht existiert, JSON im API-Umschlag oder zwei Dateien mit derselben uid.

Beliebte Anleitungen