Dashboard #
Visualisasi data adalah jembatan yang menghubungkan deretan angka metrik di database dengan pemahaman manusia saat memantau sistem. Dashboard Grafana yang dibuat secara manual lewat antarmuka grafis (UI) sering kali menjadi hutang teknis yang tidak terdokumentasi — server Grafana yang mengalami crash, migrasi ke instance baru, atau kegagalan pemulihan dari backup dapat melenyapkan seluruh visualisasi penting yang telah kita susun. Pendekatan dashboard-as-code menawarkan solusi dengan memperlakukan definisi dashboard sebagai kode deklaratif (baik dalam format JSON mentah maupun template Grafonnet) yang dikelola versinya di repositori Git dan dideploy secara otomatis menggunakan Ansible. Dengan metode ini, kita dapat memastikan konsistensi visualisasi di seluruh lingkungan kerja, mempermudah pelacakan perubahan, serta mempercepat proses pemulihan bencana.
Konsep Dashboard as Code: JSON vs Grafonnet #
Sebelum menerapkan otomatisasi, kita perlu memahami dua format utama dalam merepresentasikan dashboard Grafana sebagai kode: JSON mentah dan Grafonnet.
JSON Mentah (Raw JSON) #
Secara default, Grafana menyimpan definisi dashboard dalam satu file JSON raksasa yang berisi ribuan baris. Di dalam file JSON ini, setiap panel, query, koordinat tata letak grid, threshold warna, hingga konfigurasi sumbu grafik didefinisikan secara eksplisit.
Kelebihan format JSON mentah adalah kemudahan ekspor langsung dari antarmuka web Grafana. Namun, kekurangannya sangat menonjol saat kita bekerja dalam tim:
- Duplikasi Tinggi: Konfigurasi panel yang serupa harus ditulis berulang kali untuk setiap metrik.
- Sulit di-Review: Perubahan satu posisi koordinat panel di UI akan memicu perubahan ratusan baris koordinat
x,y,w, danhdi file JSON, sehingga proses pull request review menjadi sangat sulit dibaca. - Rentang Kesalahan: Mengedit file JSON raksasa secara manual sering kali menyebabkan kesalahan sintaksis tanda kurung atau koma yang merusak seluruh dashboard.
Grafonnet (Jsonnet Template) #
Grafonnet adalah pustaka (library) berbasis Jsonnet (bahasa data templating dari Google) yang dirancang khusus untuk menghasilkan JSON dashboard Grafana. Dibandingkan JSON mentah, Grafonnet bertindak seperti bahasa pemrograman tingkat tinggi yang memungkinkan kita mendefinisikan komponen dashboard secara modular dan reusable.
Mari kita bandingkan representasi pembuatan dashboard sederhana menggunakan Grafonnet:
// file: dashboard.jsonnet
local grafana = import 'github.com/grafana/grafonnet-lib/grafonnet/grafana.libsonnet';
local dashboard = grafana.dashboard;
local row = grafana.row;
local prometheus = grafana.prometheus;
local graphPanel = grafana.graphPanel;
dashboard.new(
title='Production API Overview',
tags=['production', 'api'],
editable=false,
)
.addRow(
row.new(title='System Resources')
)
.addPanel(
graphPanel.new(
title='CPU Usage per Host',
datasource='Prometheus',
span=12,
)
.addTarget(
prometheus.target(
expr='sum(rate(node_cpu_seconds_total{mode!="idle"}[5m])) by (instance)',
legendFormat='{{instance}}',
)
),
gridPos={ x: 0, y: 0, w: 24, h: 8 }
)
Untuk mengompilasi file di atas menjadi JSON mentah yang dipahami oleh Grafana, kita harus menggunakan utilitas jsonnet setelah memasang dependensi pustakanya:
# Pasang paket jsonnet-bundler untuk mengelola pustaka Grafonnet
go install github.com/jsonnet-bundler/jsonnet-bundler/cmd/jb@latest
# Inisialisasi jb di direktori proyek kita
jb init
# Pasang pustaka grafonnet-lib ke direktori vendor/
jb install github.com/grafana/grafonnet-lib/grafonnet@master
# Kompilasi file .jsonnet menjadi file JSON Grafana
jsonnet -I vendor dashboard.jsonnet > files/dashboards/production-api-overview.json
Dengan beralih dari pengeditan JSON mentah ke penulisan Grafonnet, tingkat duplikasi baris kode dapat kita pangkas hingga lebih dari 70 persen, karena kita dapat mendefinisikan template grafik standar sekali saja dan menggunakannya kembali di puluhan dashboard yang berbeda.
Otomatisasi Provisioning Datasource dan Dashboard dengan Ansible #
Grafana mendukung pemuatan konfigurasi secara deklaratif melalui folder provisioning saat layanan dimulai (startup). Kita dapat mengotomatisasi penempatan file konfigurasi ini menggunakan Ansible agar Grafana langsung terhubung ke sumber data (datasource) dan memuat dashboard tanpa intervensi manual.
Struktur folder provisioning yang kita targetkan di server Grafana adalah sebagai berikut:
/etc/grafana/provisioning/
├── datasources/
│ ├── datasources.yml # Konfigurasi koneksi ke Prometheus dan Loki
└── dashboards/
├── provider.yml # Mengatur direktori pembacaan file JSON
Berikut adalah task Ansible untuk menyiapkan direktori dan menyalin file konfigurasi provisioning secara dinamis ke server Grafana:
# roles/grafana_provisioning/tasks/main.yml
---
- name: Pastikan direktori provisioning Grafana memiliki permission yang tepat
file:
path: "{{ item }}"
state: directory
owner: grafana
group: grafana
mode: '0750'
loop:
- /etc/grafana/provisioning/datasources
- /etc/grafana/provisioning/dashboards
- /var/lib/grafana/dashboards
- name: Deploy file konfigurasi datasource
template:
src: datasources.yml.j2
dest: /etc/grafana/provisioning/datasources/datasources.yml
owner: grafana
group: grafana
mode: '0640'
notify: Restart Grafana
- name: Deploy konfigurasi dashboard provider
template:
src: provider.yml.j2
dest: /etc/grafana/provisioning/dashboards/provider.yml
owner: grafana
group: grafana
mode: '0640'
notify: Reload Grafana dashboards
- name: Sinkronisasikan file JSON dashboard ke folder target
copy:
src: "{{ item }}"
dest: "/var/lib/grafana/dashboards/{{ item | basename }}"
owner: grafana
group: grafana
mode: '0640'
with_fileglob:
- "files/dashboards/*.json"
notify: Reload Grafana dashboards
Berikut adalah template untuk file datasource J2:
{# roles/grafana_provisioning/templates/datasources.yml.j2 #}
apiVersion: 1
datasources:
- name: Prometheus
type: prometheus
access: proxy
url: "{{ prometheus_internal_url }}"
isDefault: true
jsonData:
httpMethod: POST
timeInterval: 15s
editable: false
- name: Loki
type: loki
access: proxy
url: "{{ loki_internal_url }}"
jsonData:
maxLines: 1000
editable: false
Dan template untuk konfigurasi dashboard provider:
{# roles/grafana_provisioning/templates/provider.yml.j2 #}
apiVersion: 1
providers:
- name: "Ansible Managed Dashboards"
orgId: 1
folder: "Infrastructure"
type: file
disableDeletion: true
updateIntervalSeconds: 10
allowUiUpdates: false
options:
path: /var/lib/grafana/dashboards
foldersFromFilesStructure: true
Kita menggunakan handler untuk memicu pemuatan ulang dashboard tanpa harus melakukan restart layanan Grafana secara penuh. Hal ini menjaga agar sesi pengguna yang sedang aktif tidak terputus:
# roles/grafana_provisioning/handlers/main.yml
---
- name: Restart Grafana
systemd:
name: grafana-server
state: restarted
- name: Reload Grafana dashboards
uri:
url: "http://localhost:3000/api/admin/provisioning/dashboards/reload"
method: POST
user: "{{ grafana_admin_username }}"
password: "{{ vault_grafana_admin_password }}"
force_basic_auth: true
status_code: 200
Implementasi Monitoring Template #
Untuk menghindari keharusan membuat dashboard baru setiap kali sebuah server atau instans baru ditambahkan ke dalam kluster, kita harus memanfaatkan fitur variabel (templating) di Grafana. Variabel memungkinkan pengguna memilih instans secara dinamis dari menu drop-down di bagian atas dashboard.
Di dalam file JSON dashboard, struktur variabel didefinisikan pada blok templating. Kita menggunakan query Prometheus untuk mengisi pilihan variabel tersebut secara otomatis berdasarkan label data yang masuk:
{
"templating": {
"list": [
{
"current": {},
"datasource": "Prometheus",
"definition": "label_values(node_cpu_seconds_total, instance)",
"hide": 0,
"includeAll": true,
"multi": true,
"name": "instance",
"options": [],
"query": {
"query": "label_values(node_cpu_seconds_total, instance)",
"refId": "PrometheusTemplateQuery"
},
"refresh": 1,
"regex": "",
"skipUrlSync": false,
"sort": 1,
"type": "query"
},
{
"current": {},
"datasource": "Prometheus",
"definition": "label_values(node_cpu_seconds_total, env)",
"hide": 0,
"includeAll": false,
"multi": false,
"name": "env",
"options": [],
"query": {
"query": "label_values(node_cpu_seconds_total, env)",
"refId": "PrometheusEnvQuery"
},
"refresh": 1,
"regex": "",
"skipUrlSync": false,
"sort": 1,
"type": "query"
}
]
}
}
Ansible mengelola penyebaran template JSON ini dengan memastikan variabel environment (env) disesuaikan secara otomatis saat proses sinkronisasi file berlangsung. Dengan memanfaatkan template variabel, satu file JSON dashboard dapat memantau ribuan server sekaligus secara terstruktur.
Audit dan Access Control di Grafana #
Di lingkungan perusahaan dengan banyak tim, kita tidak ingin seluruh anggota organisasi memiliki hak akses yang sama. Kebijakan keamanan mengharuskan penerapan akses berbasis peran (Role-Based Access Control atau RBAC) serta proses audit berkala terhadap aktivitas akses.
Ansible menyediakan modul terintegrasi untuk mengotomatisasi pembuatan organisasi, tim, dan pengaturan hak akses folder:
# playbooks/grafana_rbac.yml
---
- name: Konfigurasikan Tim dan Hak Akses Grafana
hosts: localhost
connection: local
vars:
grafana_api_url: "http://grafana.internal.zone:3000"
grafana_token: "{{ vault_grafana_admin_token }}"
tasks:
- name: Buat Organisasi Baru untuk Tim Finansial
community.grafana.grafana_organization:
url: "{{ grafana_api_url }}"
api_key: "{{ grafana_token }}"
name: "Finance Department"
state: present
- name: Buat Tim Pengembang Core di Organisasi Utama
community.grafana.grafana_team:
url: "{{ grafana_api_url }}"
api_key: "{{ grafana_token }}"
name: "Core Developers"
email: "[email protected]"
state: present
register: core_dev_team
- name: Batasi Hak Akses Folder Infrastructure
uri:
url: "{{ grafana_api_url }}/api/folders/infra_folder_uid/permissions"
method: POST
headers:
Authorization: "Bearer {{ grafana_token }}"
Content-Type: "application/json"
body_format: json
body:
items:
- role: Viewer
permission: 1
- role: Editor
permission: 2
- teamId: "{{ core_dev_team.team_id }}"
permission: 2 # Berikan hak akses Edit untuk Tim Developer
status_code: 200
Untuk kebutuhan audit, kita mengonfigurasi Grafana agar mencatat setiap aktivitas login, perubahan dashboard, dan ekspor data ke file log sistem terpusat. Kita mengelola konfigurasi ini melalui Ansible task yang memodifikasi /etc/grafana/grafana.ini:
- name: Aktifkan pencatatan log audit di Grafana
ini_file:
path: /etc/grafana/grafana.ini
section: log
option: level
value: info
notify: Restart Grafana
- name: Pastikan log audit ditulis ke file terpisah
ini_file:
path: /etc/grafana/grafana.ini
section: log.audit
option: enabled
value: "true"
notify: Restart Grafana
Pencatatan log audit yang aktif membantu tim keamanan mendeteksi perubahan konfigurasi yang mencurigakan atau akses ilegal ke metrik bisnis yang sensitif.
Alur Eksplorasi Insiden: Dari Alert Ke Root Cause #
Saat insiden terjadi di lingkungan produksi, kecepatan reaksi tim on-call sangat bergantung pada bagaimana dashboard kita dirancang. Alur visualisasi harus memandu teknisi dari tingkat ringkasan sistem (high-level overview) ke analisis akar masalah secara terarah.
Berikut adalah diagram alur yang menggambarkan navigasi tim on-call saat merespons sebuah peringatan sistem (alert):
flowchart TD
A["Mulai: Notifikasi Alert Masuk ke Slack"] --> B["Klik Link Runbook / Dashboard di Pesan Alert"]
B --> C["Buka Dashboard Overview (High-Level)"]
C --> D{"Apakah Metrik CPU / Memori Host Normal?"}
D -- "Tidak (Resource Exhausted)" --> E["Buka Dashboard Detail Node via Variabel Instance"]
D -- "Ya (Normal)" --> F["Buka Dashboard Performa Aplikasi (Latency/Error Rate)"]
E --> G["Identifikasi Proses Terganggu via SSH / Node Exporter"]
F --> H{"Apakah Error Terlokalisir pada Satu Service?"}
H -- "Ya" --> I["Buka Dashboard Khusus Service (Drilldown)"]
H -- "Tidak" --> J["Buka Dashboard Distributed Tracing (Tempo)"]
I --> K["Cari Log Terkait via Integrasi Loki"]
J --> L["Lacak Span Terlambat & Database Queries"]
K --> M["Akar Masalah Ditemukan"]
L --> M
M --> N["Terapkan Perbaikan via Ansible Playbook"]
N --> O["Selesai: Verifikasi Dashboard Kembali Hijau"]
Dan berikut adalah sequence diagram yang memperlihatkan bagaimana interaksi antar-layanan observability terjadi di latar belakang ketika pengguna mengeksplorasi visualisasi:
sequenceDiagram
participant U as "Teknisi On-Call"
participant G as "Grafana Gateway"
participant P as "Prometheus TSDB"
participant L as "Loki Log Engine"
participant T as "Tempo Trace Engine"
U->>G: "Buka Dashboard Utama"
G->>P: "Ambil metrik ketersediaan layanan"
P-->>G: "Metrik availability drop ke 94.2%"
G-->>U: "Tampilkan grafik merah (Incident Mode)"
U->>G: "Klik panel Error Rate (Drilldown)"
G->>P: "Ambil kontribusi error per endpoint"
P-->>G: "Endpoint /payment menghasilkan 500 Internal Server Error"
G-->>U: "Tampilkan tabel detail endpoint terganggu"
U->>G: "Klik tombol 'Explore Logs'"
G->>L: "Cari log dengan filter trace ID yang bermasalah"
L-->>G: "Log: 'Connection timeout to payment gateway'"
G-->>U: "Tampilkan raw log di panel sisi kanan"
U->>G: "Klik tombol 'Inspect Trace'"
G->>T: "Ambil waterfall span dari trace ID"
T-->>G: "Trace Span: DB query = 50ms, HTTP call external = 5000ms"
G-->>U: "Tampilkan visualisasi timeline trace (Tempo)"
Anti-Pattern dan Solusi Manajemen Dashboard #
Dalam mengadopsi konsep dashboard sebagai kode, terdapat beberapa praktik buruk yang sering kali dilakukan oleh tim operasional yang baru bermigrasi.
1. Modifikasi Langsung di UI Grafana #
- Anti-Pattern: Ketika terjadi insiden, tim on-call mengedit panel atau query langsung dari UI Grafana, kemudian menyimpannya. Saat pipeline Ansible berjalan kembali, perubahan darurat tersebut terhapus secara otomatis oleh sistem sinkronisasi.
- Solusi: Nonaktifkan fitur pengeditan UI secara permanen di lingkungan produksi dengan mengatur
allowUiUpdates: falsedi file provider. Perubahan harus dibuat di lingkungan staging, diekspor menjadi JSON, dimasukkan ke repositori Git melalui Pull Request, lalu dideploy secara otomatis oleh Ansible.
2. Hardcode ID Sumber Data (Datasource UID) #
- Anti-Pattern: Menggunakan UID statis unik untuk datasource di dalam panel JSON, seperti
"uid": "prometheus-prod-xyz123". Ketika dashboard dideploy ke lingkungan staging, visualisasi rusak karena staging menggunakan UID yang berbeda. - Solusi: Gunakan referensi variabel global di seluruh panel JSON, seperti
"datasource": "${DS_PROMETHEUS}". Grafana akan secara otomatis memetakan variabel ini ke datasource default yang terpasang di masing-masing lingkungan.
3. Mengunggah JSON Tanpa Format yang Jelas (Unformatted JSON) #
- Anti-Pattern: Menyimpan file JSON dashboard dalam format satu baris panjang (minified) atau dengan urutan kunci (keys) yang tidak konsisten di Git. Hal ini membuat perintah
git difftidak berguna karena seluruh file terdeteksi sebagai satu baris perubahan. - Solusi: Gunakan skrip pembersih dan pemformat otomatis sebelum melakukan commit file JSON ke repositori. Kita dapat menerapkan *pre-commit hook
yang memformat file JSON secara konsisten menggunakan modul pythonjson.toolatau programjq`.
Berikut adalah contoh skrip pembersih JSON dashboard yang harus kita jalankan sebelum memasukkan kode ke Git:
#!/usr/bin/env python3
# scripts/cleanup_dashboard.py
import json
import sys
def cleanup(file_path):
with open(file_path, 'r') as f:
data = json.load(f)
# Hapus data spesifik instans sumber
data.pop('id', None)
data.pop('version', None)
data.pop('iteration', None)
# Normalisasi interval refresh default
data['refresh'] = '30s'
# Simpan kembali dengan indentasi rapi dan kunci terurut
with open(file_path, 'w') as f:
json.dump(data, f, indent=2, sort_keys=True)
f.write('\n')
if __name__ == '__main__':
if len(sys.argv) < 2:
print("Penggunaan: cleanup_dashboard.py <path_ke_file_json>")
sys.exit(1)
cleanup(sys.argv[1])
Versioning dan Rollback Dashboard #
Karena file JSON dashboard disimpan di dalam Git, kita memiliki kendali penuh atas riwayat perubahan. Jika sebuah perubahan query menyebabkan dashboard memuat data secara lambat atau menampilkan informasi yang menyesatkan, kita dapat melakukan rollback ke versi sebelumnya dengan cepat.
Langkah-langkah untuk melakukan rollback dashboard menggunakan kombinasi Git dan Ansible:
# 1. Cari commit hash terakhir yang stabil untuk file dashboard
git log --oneline -- files/dashboards/api-performance.json
# 2. Kembalikan file ke kondisi pada commit tersebut
git checkout a1b2c3d4 -- files/dashboards/api-performance.json
# 3. Jalankan playbook Ansible untuk menyebarkan perubahan
ansible-playbook -i inventories/production playbooks/sync_dashboards.yml
# 4. Buat tag baru di Git untuk menandai rilis dashboard yang stabil
git tag -a dashboards-v1.4.2 -m "Rollback dashboard api-performance ke versi stabil"
Dengan mengintegrasikan repositori Git ke dalam siklus hidup pengelolaan dashboard, kita memperlakukan infrastruktur pemantauan dengan tingkat disiplin yang sama seperti kode aplikasi.
Ringkasan #
- Dashboard-as-Code adalah praktik terbaik yang memastikan semua visualisasi tersimpan di Git secara deklaratif, sehingga aman dari risiko kehilangan data akibat crash server.
- Grafonnet memecahkan masalah duplikasi pada file JSON mentah dengan menyediakan pustaka berbasis Jsonnet untuk menghasilkan file dashboard secara dinamis dan modular.
- Konfigurasi Provisioning secara file-based dengan parameter
disableDeletion: truemencegah penghapusan dashboard yang tidak disengaja oleh pengguna melalui antarmuka web.- Variabel Dinamis di dalam query dashboard sangat penting untuk memastikan satu file template dapat memantau banyak server tanpa perlu membuat duplikasi panel.
- Akses Berbasis Peran (RBAC) dan log audit wajib diterapkan di tingkat produksi untuk menjamin kepatuhan keamanan dan membatasi hak edit dashboard.
- Korelasi Data Observability yang baik memungkinkan teknisi berpindah secara mulus dari metrik abnormal di Prometheus ke baris log di Loki dan trace span di Tempo.
- Format JSON Konsisten yang dijaga oleh skrip pembersih atau hook sebelum masuk ke Git mempermudah proses review perubahan visualisasi saat code review.
- Skema Rollback Cepat berbasis Git checkout dan pemanggilan ulang task Ansible meminimalkan waktu pemulihan jika terjadi kesalahan konfigurasi dashboard.