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
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.ymlPemuat Dashboard
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/pembayaranallowUiUpdates: false adalah keputusan pentingDengan 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
#!/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"
doneTiga 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
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
| Cara | Kelebihan | Kekurangan |
|---|---|---|
| JSON langsung | Tanpa alat tambahan, persis seperti yang dilihat Grafana | Verbose, sulit menghindari duplikasi |
| Grafonnet (Jsonnet) | Fungsi, loop, komponen yang dapat dipakai ulang | Perlu belajar Jsonnet |
| Terraform provider | Satu alat dengan infrastruktur lain, state terkelola | Diff JSON besar sulit dibaca |
| Grafana Foundation SDK | Type-safe di Go/TypeScript/Python | Relatif baru |
| Git Sync (Grafana 12) | Sinkronisasi dua arah dengan repositori | Fitur baru, perlu penyiapan |
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
#!/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 harapansecret_key tidak lengkapMemulihkan 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
- Baca catatan rilis, khususnya bagian breaking changes dan deprecations.
- Backup basis data. Migrasi skema Grafana tidak bisa dibalik.
- Pulihkan salinan basis data ke instansi staging dengan versi baru.
- Periksa: dashboard penting, alert rule, integrasi OAuth, dan plugin.
- Upgrade produksi. Naik satu versi mayor pada satu waktu, jangan melompat.
- Amati
/metricsdan log selama beberapa jam setelahnya.
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.