Kodeloka
  1. Kodeloka
  2. Kelas Grafana
  3. Materi

Modul 15 · Modul 15 dari 15

Dashboard as Code: Provisioning, Backup, dan Upgrade

Memindahkan seluruh konfigurasi Grafana ke Git, sehingga instansi baru bisa dibangun ulang dari nol dan setiap perubahan bisa direview.

Kenapa Klik-klik di UI Tidak Cukup

Dashboard yang dibuat lewat UI tidak punya riwayat yang bisa dibaca, tidak bisa direview sebelum berubah, tidak bisa diuji, dan tidak bisa dibangun ulang bila basis datanya hilang. Untuk dashboard eksperimen itu tidak masalah; untuk dashboard yang dipakai saat insiden, itu risiko.

Struktur Repositori yang Terbukti

strukturtext
grafana-config/
├── provisioning/
│   ├── datasources/
│   │   └── sumber.yml
│   ├── dashboards/
│   │   └── pemuat.yml            # aturan pemuatan, bukan dashboard
│   ├── alerting/
│   │   ├── rules.yml
│   │   ├── notifikasi.yml
│   │   └── templates.yml
│   └── plugins/
│       └── app.yml
├── dashboards/
│   ├── platform/
│   │   ├── ikhtisar.json
│   │   └── node.json
│   └── pembayaran/
│       ├── ledger.json
│       └── slo.json
├── grafana.ini
├── docker-compose.yml
└── .github/workflows/validasi.yml

Pemuat Dashboard

provisioning/dashboards/pemuat.ymlyaml
apiVersion: 1

providers:
  - name: platform
    orgId: 1
    folder: Platform
    folderUid: platform
    type: file
    disableDeletion: true         # jangan hapus bila berkas hilang
    allowUiUpdates: false         # UI tidak boleh menimpa Git
    updateIntervalSeconds: 30
    options:
      path: /etc/grafana/dashboards/platform
      foldersFromFilesStructure: false

  - name: pembayaran
    orgId: 1
    folder: Tim Pembayaran
    folderUid: tim-pembayaran
    type: file
    disableDeletion: true
    allowUiUpdates: false
    updateIntervalSeconds: 30
    options:
      path: /etc/grafana/dashboards/pembayaran
⚠️ allowUiUpdates: false adalah keputusan penting

Dengan false, tombol Save di UI dinonaktifkan untuk dashboard tersebut — satu-satunya jalan mengubahnya adalah lewat Git. Ini melindungi dashboard produksi dari perubahan tak sengaja.

Sisi lainnya: orang tidak bisa lagi bereksperimen langsung. Pola yang enak dipakai: dashboard produksi dikunci lewat provisioning, dan sediakan satu folder "Eksperimen" yang bebas diedit. Perubahan yang terbukti berguna dipindahkan ke Git lewat pull request.

Ekspor yang Benar untuk Git

ekspor.shbash
#!/usr/bin/env bash
set -euo pipefail
G="http://localhost:3000"
TOKEN="$GRAFANA_TOKEN"

for uid in $(curl -sS -H "Authorization: Bearer $TOKEN" \
              "$G/api/search?type=dash-db&limit=5000" | jq -r '.[].uid'); do

  curl -sS -H "Authorization: Bearer $TOKEN" \
       "$G/api/dashboards/uid/$uid" \
  | jq '.dashboard
        | .id = null
        | del(.version)
        | del(.iteration)' \
  > "dashboards/$uid.json"

  echo "diekspor: $uid"
done

Tiga pembersihan itu wajib: id harus null agar tidak menimpa dashboard lain, sementara version dan iteration hanya menimbulkan diff berisik di setiap commit tanpa arti apa pun.

Validasi di CI

.github/workflows/validasi.ymlyaml
name: Validasi Konfigurasi Grafana
on: [pull_request]

jobs:
  validasi:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v5

      - name: JSON dashboard harus valid
        run: |
          for f in dashboards/**/*.json; do
            jq empty "$f" || { echo "JSON rusak: $f"; exit 1; }
          done

      - name: id harus null dan uid harus ada
        run: |
          for f in dashboards/**/*.json; do
            [ "$(jq -r '.id')"  = "null" ] || { echo "id tidak null: $f"; exit 1; }
            [ "$(jq -r '.uid')" != "null" ] || { echo "uid kosong: $f";   exit 1; }
          done

      - name: UID data source harus yang dikenal
        run: |
          DIKENAL=$(yq -r '.datasources[].uid' provisioning/datasources/sumber.yml | sort -u)
          DIPAKAI=$(jq -r '.. | .datasource? // empty | .uid? // empty' dashboards/**/*.json | sort -u)
          for u in $DIPAKAI; do
            case "$u" in
              '${DS_*'|'__expr__') continue ;;
            esac
            echo "$DIKENAL" | grep -qx "$u" || { echo "UID tidak dikenal: $u"; exit 1; }
          done

      - name: Uji muat di Grafana sementara
        run: |
          docker compose -f docker-compose.test.yml up -d --wait
          sleep 15
          GAGAL=$(docker compose logs grafana | grep -ci "provisioning error" || true)
          [ "$GAGAL" = "0" ] || { docker compose logs grafana; exit 1; }

Alternatif Menulis Dashboard

CaraKelebihanKekurangan
JSON langsungTanpa alat tambahan, persis seperti yang dilihat GrafanaVerbose, sulit menghindari duplikasi
Grafonnet (Jsonnet)Fungsi, loop, komponen yang dapat dipakai ulangPerlu belajar Jsonnet
Terraform providerSatu alat dengan infrastruktur lain, state terkelolaDiff JSON besar sulit dibaca
Grafana Foundation SDKType-safe di Go/TypeScript/PythonRelatif baru
Git Sync (Grafana 12)Sinkronisasi dua arah dengan repositoriFitur baru, perlu penyiapan
contoh Grafonnettext
local g = import 'g.libsonnet';

local panelLatency(layanan) =
  g.panel.timeSeries.new(layanan + ' — latency p95')
  + g.panel.timeSeries.queryOptions.withTargets([
      g.query.prometheus.new('prom-utama',
        'layanan:latency_p95:5m{layanan="' + layanan + '"}')
    ])
  + g.panel.timeSeries.standardOptions.withUnit('s');

g.dashboard.new('SLO per Layanan')
+ g.dashboard.withUid('slo-layanan')
+ g.dashboard.withPanels([
    panelLatency(l)
    for l in ['api', 'ledger', 'notifikasi', 'rekonsiliasi']
  ])

Empat panel identik dari satu definisi. Menambah layanan kelima berarti menambah satu kata di dalam daftar — inilah alasan utama orang beralih ke Grafonnet.

Backup: Apa yang Benar-benar Perlu Disimpan

backup.shbash
#!/usr/bin/env bash
set -euo pipefail
TGL=$(date +%F)

# 1. basis data — berisi dashboard, user, alert state, anotasi
pg_dump -h pg.internal -U grafana grafana | gzip > "backup/grafana-db-$TGL.sql.gz"

# 2. secret_key — TANPA INI kredensial data source tidak bisa didekripsi
grep -E '^secret_key' /etc/grafana/grafana.ini > "backup/secret-$TGL.txt"

# 3. berkas provisioning (idealnya sudah di Git)
tar czf "backup/provisioning-$TGL.tar.gz" /etc/grafana/provisioning

# 4. plugin yang dipasang manual
tar czf "backup/plugins-$TGL.tar.gz" /var/lib/grafana/plugins

# uji pemulihan sekali sebulan — backup yang belum pernah diuji
# bukan backup, hanya harapan
⛔ Backup tanpa secret_key tidak lengkap

Memulihkan basis data ke server dengan secret_key berbeda membuat seluruh password data source tidak bisa didekripsi. Anda akan punya semua dashboard, tetapi tidak satu pun yang menampilkan data — dan setiap kredensial harus dimasukkan ulang manual.

Prosedur Upgrade

  1. Baca catatan rilis, khususnya bagian breaking changes dan deprecations.
  2. Backup basis data. Migrasi skema Grafana tidak bisa dibalik.
  3. Pulihkan salinan basis data ke instansi staging dengan versi baru.
  4. Periksa: dashboard penting, alert rule, integrasi OAuth, dan plugin.
  5. Upgrade produksi. Naik satu versi mayor pada satu waktu, jangan melompat.
  6. Amati /metrics dan log selama beberapa jam setelahnya.
💡 Plugin adalah penyebab kegagalan upgrade paling umum

Plugin komunitas sering tertinggal beberapa versi di belakang Grafana. Panel yang memakai plugin tak kompatibel akan tampil sebagai kotak kesalahan setelah upgrade.

Inventarisasi plugin Anda sebelum upgrade, dan periksa kompatibilitasnya. Bila sebuah plugin tidak lagi dirawat, pindahkan panelnya ke tipe panel bawaan sebelum upgrade — bukan setelahnya, saat dashboard sudah rusak.

Penutup: Tanda Instalasi Grafana yang Dewasa

  • Seluruh dashboard produksi ada di Git dan dimuat lewat provisioning.
  • Instansi baru bisa dibangun dari nol dengan satu perintah.
  • Setiap deploy tercatat sebagai anotasi otomatis.
  • Setiap alert punya runbook, pemilik, dan tautan dashboard.
  • Kueri berat sudah menjadi recording rule.
  • Grafana memantau dirinya sendiri, termasuk keterlambatan evaluasi alert.
  • Backup diuji pemulihannya secara berkala, bukan hanya dijalankan.