Dashboard #
Dashboard Grafana yang dibuat secara manual lewat UI adalah hutang teknis yang menunggu waktu untuk hilang — server Grafana yang crash, migration ke instance baru, atau restore dari backup yang tidak sempurna bisa membuat semua dashboard lenyap. Dashboard as code adalah praktik menyimpan definisi dashboard sebagai file JSON yang di-manage versinya di Git dan dideploy otomatis oleh Ansible. Dengan pendekatan ini, dashboard bisa dibuat ulang kapan saja, konsisten di semua lingkungan, dan perubahannya bisa di-review seperti perubahan kode.
Provisioning Grafana via File #
Grafana mendukung provisioning otomatis melalui file konfigurasi di direktori /etc/grafana/provisioning/. Ansible mendeploy file-file ini dan Grafana memuat ulang konfigurasinya:
# roles/grafana/tasks/provisioning.yml
---
- name: Buat direktori provisioning
file:
path: "{{ item }}"
state: directory
owner: grafana
group: grafana
mode: '0755'
loop:
- /etc/grafana/provisioning/datasources
- /etc/grafana/provisioning/dashboards
- /etc/grafana/provisioning/notifiers
- /var/lib/grafana/dashboards
- name: Deploy datasource configuration
template:
src: "{{ item }}"
dest: "/etc/grafana/provisioning/datasources/{{ item | basename | replace('.j2', '') }}"
owner: grafana
group: grafana
mode: '0640'
with_fileglob:
- "templates/datasources/*.j2"
notify: Restart Grafana
- name: Deploy dashboard provider configuration
template:
src: dashboard-provider.yml.j2
dest: /etc/grafana/provisioning/dashboards/provider.yml
owner: grafana
group: grafana
mode: '0640'
notify: Restart Grafana
- name: Deploy dashboard JSON files
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
Konfigurasi Dashboard Provider #
Provider mendefinisikan dari mana Grafana memuat dashboard dan apa yang boleh dilakukan pengguna terhadapnya:
{# templates/dashboard-provider.yml.j2 #}
apiVersion: 1
providers:
- name: Ansible Managed Dashboards
orgId: 1
folder: "Infrastructure"
type: file
disableDeletion: true # Cegah penghapusan dari UI
updateIntervalSeconds: 30 # Cek perubahan setiap 30 detik
allowUiUpdates: false # Cegah edit dari UI (dashboard as code)
options:
path: /var/lib/grafana/dashboards
foldersFromFilesStructure: true # Folder dari struktur direktori
allowUiUpdates: falsemencegah siapapun mengedit dashboard dari UI Grafana. Ini adalah trade-off yang disengaja dalam pendekatan dashboard as code — perubahan hanya boleh masuk melalui Git dan pipeline Ansible. Jika tim belum siap dengan workflow ini, gunakanallowUiUpdates: truedulu dan migrasi secara bertahap.
Mengekspor Dashboard dari Grafana #
Untuk dashboard yang sudah dibuat di UI dan ingin dikelola sebagai kode, export dulu sebagai JSON:
# Export via Grafana API
curl -s \
-H "Authorization: Bearer $GRAFANA_API_TOKEN" \
"https://grafana.company.com/api/dashboards/uid/YOUR_DASHBOARD_UID" \
| python3 -m json.tool > files/dashboards/my-dashboard.json
Atau gunakan Ansible untuk mengekspor semua dashboard sekaligus:
# playbooks/export-grafana-dashboards.yml
---
- name: Ekspor semua dashboard dari Grafana
hosts: localhost
vars:
grafana_url: "https://grafana.company.com"
tasks:
- name: Ambil daftar semua dashboard
uri:
url: "{{ grafana_url }}/api/search?type=dash-db"
headers:
Authorization: "Bearer {{ vault_grafana_api_token }}"
return_content: true
register: dashboard_list
no_log: true
- name: Export setiap dashboard ke file JSON
uri:
url: "{{ grafana_url }}/api/dashboards/uid/{{ item.uid }}"
headers:
Authorization: "Bearer {{ vault_grafana_api_token }}"
return_content: true
register: dashboard_content
loop: "{{ dashboard_list.json }}"
loop_control:
label: "{{ item.title }}"
no_log: true
- name: Simpan dashboard ke file
copy:
content: "{{ item.json.dashboard | to_nice_json }}"
dest: "files/dashboards/{{ item.item.title | lower | replace(' ', '-') }}.json"
loop: "{{ dashboard_content.results }}"
loop_control:
label: "{{ item.item.title }}"
Struktur Dashboard JSON yang Bersih #
Dashboard JSON yang di-export langsung dari Grafana biasanya berisi ID dan metadata yang spesifik untuk instance tersebut. Bersihkan sebelum commit ke Git:
#!/usr/bin/env python3
# scripts/clean-dashboard-json.py
import json
import sys
with open(sys.argv[1]) as f:
data = json.load(f)
# Ambil hanya bagian dashboard, bukan wrapper
dashboard = data.get('dashboard', data)
# Hapus field yang instance-specific
for field in ['id', 'version', 'iteration']:
dashboard.pop(field, None)
# Reset timestamps
dashboard['time'] = {"from": "now-1h", "to": "now"}
print(json.dumps(dashboard, indent=2))
# Bersihkan setelah export
python3 scripts/clean-dashboard-json.py \
files/dashboards/raw-export.json \
> files/dashboards/node-overview.json
Sinkronisasi Dashboard Antar Lingkungan #
Dengan dashboard as code, mendeploy dashboard yang sama ke staging dan production semudah menjalankan playbook:
# playbooks/sync-dashboards.yml
---
- name: Sinkronisasi dashboard ke semua lingkungan
hosts: grafana_servers
tasks:
- name: Deploy dashboard files
copy:
src: "{{ item }}"
dest: "/var/lib/grafana/dashboards/{{ item | basename }}"
owner: grafana
group: grafana
mode: '0640'
with_fileglob:
- "{{ playbook_dir }}/files/dashboards/*.json"
notify: Reload Grafana dashboards
- name: Verifikasi dashboard dimuat oleh Grafana
uri:
url: "http://localhost:3000/api/search?type=dash-db"
user: "{{ grafana_admin_user }}"
password: "{{ vault_grafana_admin_password }}"
force_basic_auth: true
return_content: true
register: loaded_dashboards
changed_when: false
- name: Hitung dan tampilkan dashboard yang aktif
debug:
msg: "{{ loaded_dashboards.json | length }} dashboard aktif di {{ inventory_hostname }}"
Mengelola Grafana Organizations dan Users #
# Buat organisasi Grafana via API
- name: Buat organisasi untuk setiap tim
uri:
url: "http://localhost:3000/api/orgs"
method: POST
user: "{{ grafana_admin_user }}"
password: "{{ vault_grafana_admin_password }}"
force_basic_auth: true
body_format: json
body:
name: "{{ item.name }}"
status_code: [200, 409] # 409 = sudah ada, tidak apa-apa
loop: "{{ grafana_organizations }}"
loop_control:
label: "{{ item.name }}"
# Tambahkan user ke organisasi
- name: Tambahkan user ke organisasi yang tepat
community.grafana.grafana_user:
url: "http://localhost:3000"
url_username: "{{ grafana_admin_user }}"
url_password: "{{ vault_grafana_admin_password }}"
name: "{{ item.name }}"
email: "{{ item.email }}"
login: "{{ item.username }}"
password: "{{ vault_grafana_default_password }}"
state: present
loop: "{{ grafana_users }}"
loop_control:
label: "{{ item.username }}"
no_log: true
Ringkasan #
- Dashboard as code via provisioning file Grafana adalah praktik terbaik — dashboard tersimpan di Git, bisa di-review, dan bisa di-deploy ulang kapan saja.
disableDeletion: truedanallowUiUpdates: falsedi provider config memastikan Git adalah single source of truth — tidak ada perubahan yang terjadi di luar playbook.- Export dashboard yang sudah ada dari UI via API, bersihkan field instance-specific (
id,version), lalu commit ke repository.- Provisioning Grafana memuat ulang dashboard secara otomatis saat file berubah — tidak perlu restart Grafana untuk update dashboard.
- Sinkronisasi dashboard ke semua lingkungan (staging, production) dengan satu playbook — konsistensi dijamin tanpa klik manual di setiap instance Grafana.
- Gunakan
community.grafanacollection untuk manajemen user, organisasi, dan datasource secara deklaratif dari Ansible.