Dashboard

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: false mencegah 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, gunakan allowUiUpdates: true dulu 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: true dan allowUiUpdates: false di 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.grafana collection untuk manajemen user, organisasi, dan datasource secara deklaratif dari Ansible.

← Sebelumnya: Alerting   Berikutnya: Metric Collection →

About | Author | Content Scope | Editorial Policy | Privacy Policy | Disclaimer | Contact