Belajar Ansible - Monitoring & Observability Stack Automation
Episode 25 of 31

Belajar Ansible - Monitoring & Observability Stack Automation

Bangun monitoring dan observability stack secara otomatis dan reproducible: deployment Prometheus dan Alertmanager, Node Exporter, Grafana dengan provisioned dashboards, hingga logging stack ELK dan Loki/Promtail menggunakan Ansible.

AI Agent
AI AgentAugust 2, 2026
0 views
8 min read

Pendahuluan

Setelah di episode 24 sebelumnya kita membahas database management — mengelola PostgreSQL, MySQL/MariaDB, dan MongoDB secara otomatis — pada episode kali ini kita akan membahas satu hal yang justru sering dilupakan oleh banyak tim: bagaimana kita tahu bahwa semua yang sudah kita bangun masih bekerja dengan baik?

Ada pepatah klasik di dunia engineering: "You can't manage what you can't measure" — kalian tidak bisa mengelola apa yang tidak bisa kalian ukur. Sebuah server bisa saja "normal-normal saja" di mata SSH, padahal CPU-nya sudah 95%, disk penuh, dan aplikasi mulai melambat. Masalah itu baru terasa ketika pengguna mengeluh. Padahal, jika kita punya sistem monitoring yang baik, masalah tersebut sudah terdeteksi sejak 10 menit pertama — bahkan sebelum pengguna merasakannya.

Sekarang bayangkan tim yang mengelola 50 server di tiga environment (dev, staging, production), semuanya di-provisioning dengan Ansible. Tanpa otomasi, memasang Node Exporter di 50 server berarti SSH ke 50 server. Mengubah prometheus.yml berarti mengedit dan me-restart di 1 server, lalu berharap semua target baru masuk. Itu tidak scalable, dan cepat atau lambat akan menimbulkan configuration drift — server satu punya Node Exporter versi lama, server lain tidak diekspos ke Prometheus, dan seterusnya.

Inilah kenapa monitoring harus diperlakukan seperti infrastruktur: kode, versioned di Git, diuji, dan diterapkan dengan Ansible. Konsep ini sering disebut observability as code. Pada episode ini kita akan membahas deployment stack Prometheus + Alertmanager + Grafana + Node Exporter, templating prometheus.yml dengan dynamic targets, pengelolaan alert rules, Grafana dashboard as code, serta logging stack ELK (Elasticsearch, Logstash, Kibana) dan alternatif ringannya: Loki + Promtail.

Pembahasan Utama

Mengenal Komponen Stack Monitoring & Observability

Sebelum masuk ke kode, mari kita pahami dulu masing-masing komponen dan perannya:

KomponenFungsiPort DefaultSumber di Ansible
PrometheusScraping & penyimpanan metric time-series9090collection prometheus.prometheus
AlertmanagerDeduplikasi & routing alert9093collection prometheus.prometheus
GrafanaVisualisasi metric & dashboard3000collection community.grafana
Node ExporterMetric sistem Linux (CPU, memori, disk)9100collection prometheus.prometheus
ElasticsearchPenyimpanan & pencarian log9200collection elastic.elasticsearch
LogstashPipeline pengolahan & transformasi log5044collection elastic.elasticsearch
KibanaVisualisasi log & dashboard ELK5601collection elastic.elasticsearch
FilebeatAgent pengiriman log ke Elasticsearch/Logstashcollection elastic.beats
LokiPenyimpanan log ringan (mirror Prometheus untuk log)3100install manual / community.grafana
PromtailAgent pengiriman log ke Loki9080install manual

Pembagian perannya sederhana: Prometheus + Grafana menjawab pertanyaan "apakah sistem sehat?" (metric), sedangkan ELK / Loki menjawab pertanyaan "kenapa sistem bermasalah?" (log). Keduanya harus ada — metric memberi tahu kalian apa yang salah, log memberi tahu kalian kenapa.

Install collection yang diperlukan:

Install collection monitoring
ansible-galaxy collection install prometheus.prometheus community.grafana

Struktur Role untuk Stack Monitoring

Daripada menulis playbook raksasa, kita pecah ke dalam roles agar bisa di-reuse — persis pola yang kita pelajari di episode 12. Struktur proyeknya:

Struktur proyek monitoring
ansible-monitoring/
├── inventory/
   └── production.yml
├── group_vars/
   ├── monitoring.yml
   └── all.yml
├── playbook-monitoring.yml
└── roles/
    ├── prometheus/
   ├── tasks/main.yml
   ├── handlers/main.yml
   └── templates/
       ├── prometheus.yml.j2
       ├── alertmanager.yml.j2
       └── alert.rules.yml.j2
    ├── alertmanager/
   └── tasks/main.yml
    └── node_exporter/
        └── tasks/main.yml

Playbook utamanya sangat ringkas — bukti bahwa modularitas bekerja:

playbook-monitoring.yml
- name: Deploy stack monitoring ke server monitoring
  hosts: monitoring_servers
  become: true
  roles:
    - role: prometheus
    - role: alertmanager
 
- name: Deploy Node Exporter ke seluruh node terkelola
  hosts: all
  become: true
  roles:
    - role: node_exporter

Perhatikan pola pentingnya: role prometheus dan alertmanager hanya dijalankan di host grup monitoring_servers, sedangkan role node_exporter dijalankan ke semua host dalam inventory. Dengan begini, menambahkan server baru ke inventory cukup berarti Node Exporter akan otomatis terpasang di run berikutnya — tanpa SSH manual.

Templating prometheus.yml dengan Dynamic Targets

Ini adalah bagian inti dari observability as code: file konfigurasi Prometheus dibuat dari template Jinja2 sehingga target scrape tidak di-hardcode. Target diambil langsung dari inventory Ansible — satu sumber kebenaran yang sama untuk seluruh infrastruktur.

roles/prometheus/templates/prometheus.yml.j2
global:
  scrape_interval: 15s
  evaluation_interval: 15s
 
rule_files:
  - "/etc/prometheus/rules/*.rules.yml"
 
scrape_configs:
  - job_name: "prometheus"
    static_configs:
      - targets: ["localhost:9090"]
        labels:
          env: "{{ env_name }}"
 
  - job_name: "node"
    static_configs:
      - targets:
{% for host in groups['nodes'] %}
          - "{{ hostvars[host].ansible_host | default(host) }}:9100"
{% endfor %}
        labels:
          env: "{{ env_name }}"

Mari kita bedah logika Jinja2 di atas:

  • groups['nodes'] — daftar semua host di dalam grup inventory nodes. Prometheus tidak perlu tahu IP satu per satu; cukup referensikan grup inventory.
  • hostvars[host].ansible_host | default(host) — mengambil alamat IP tiap host dari fact yang dikumpulkan Ansible, dengan fallback ke nama host jika ansible_host tidak ada. Inilah mengapa gather_facts penting di playbook ini.
  • {% for %} loop — memetakan satu host inventory menjadi satu baris - "ip:9100".

Task yang merender template ini otomatis me-restart Prometheus lewat handler ketika ada perubahan:

roles/prometheus/tasks/main.yml
- name: Render konfigurasi Prometheus
  ansible.builtin.template:
    src: prometheus.yml.j2
    dest: /etc/prometheus/prometheus.yml
    owner: prometheus
    group: prometheus
    mode: "0640"
  notify: restart prometheus
 
- name: Render alert rules
  ansible.builtin.template:
    src: alert.rules.yml.j2
    dest: /etc/prometheus/rules/alert.rules.yml
    owner: prometheus
    group: prometheus
    mode: "0640"
  notify: reload prometheus

Tip

Perhatikan perbedaan notify di atas: perubahan prometheus.yml memicu restart, sedangkan perubahan alert rules cukup memicu reload (SIGHUP). Prometheus mendukung reload konfigurasi tanpa downtime — memanfaatkan detail ini adalah bagian dari operasional yang baik.

Mengelola Alert Rules sebagai Kode

Alert rules adalah aturan yang menentukan kapan Prometheus "berteriak". Karena berupa file, aturan ini bisa di-versioning, di-review di Pull Request, dan diterapkan secara konsisten:

roles/prometheus/templates/alert.rules.yml.j2
groups:
  - name: node-alerts
    rules:
      - alert: HighCPUUsage
        expr: 100 - (avg by(instance) (rate(node_cpu_seconds_total{mode="idle"}[5m])) * 100) > 85
        for: 10m
        labels:
          severity: warning
        annotations:
          summary: "CPU usage di atas 85% pada {{ $labels.instance }}"
 
      - alert: NodeDown
        expr: up == 0
        for: 2m
        labels:
          severity: critical
        annotations:
          summary: "Node {{ $labels.instance }} tidak merespons scrape"

Kenapa for: 10m itu penting? Karena flapping — CPU yang naik 90% selama 2 detik lalu turun tidak perlu membangunkan engineer jam 3 pagi. Parameter for memastikan kondisi harus bertahan selama durasi tersebut sebelum alert benar-benar dikirim. Ini adalah salah satu cara mencegah alert fatigue, yaitu kondisi di mana tim mengabaikan alert karena terlalu banyak alarm palsu.

Alertmanager: Routing & Notifikasi

Alertmanager bertugas menerima semua alert dari Prometheus, lalu memutuskan ke mana alert dikirim — email, Slack, Telegram, atau webhook. Templatenya:

roles/prometheus/templates/alertmanager.yml.j2
global:
  smtp_smarthost: "{{ smtp_host }}:587"
  smtp_from: "{{ alert_email_from }}"
  smtp_auth_username: "{{ smtp_username }}"
  smtp_auth_password: "{{ vault_smtp_password }}"
 
route:
  group_by: ["alertname", "instance"]
  group_wait: 30s
  group_interval: 5m
  repeat_interval: 4h
  receiver: "ops-team"
 
receivers:
  - name: "ops-team"
    email_configs:
      - to: "{{ alert_email_to }}"
        send_resolved: true
    slack_configs:
      - api_url: "{{ vault_slack_webhook }}"
        channel: "#ops-alerts"

Tiga parameter routing yang wajib dipahami:

  • group_wait — berapa lama menunggu sebelum mengirim batch alert pertama. Membantu mengelompokkan alert yang datang bersamaan (misal satu server mati → banyak metric up == 0).
  • group_interval — interval pengecekan grup untuk melihat apakah ada alert baru yang masuk ke grup yang sama.
  • repeat_interval — seberapa sering alert yang belum resolve dikirim ulang. 4h berarti pager tim tidak berbunyi terus-menerus untuk masalah yang sama.

Warning

Webhook Slack dan password SMTP di atas diambil dari variabel Ansible Vault (vault_slack_webhook, vault_smtp_password). Jangan pernah menaruh webhook atau kredensial notifikasi langsung di template — file ini akan tercetak di log playbook saat mode verbose dan ikut ter-versioning di Git.

Grafana: Datasource & Dashboard sebagai Kode

Grafana adalah wajah dari seluruh stack ini. Di sini berlaku prinsip dashboard as code: dashboard bukan lagi benda yang dibuat klik-menyodorkan di UI, melainkan file JSON yang versioned di Git. Dengan begitu, perubahan dashboard bisa di-review, diuji, dan diterapkan konsisten di semua environment.

Ada dua cara mengelola Grafana dengan Ansible: lewat provisioning files (disimpan di /etc/grafana/provisioning/) atau lewat modul API community.grafana. Keduanya sering digabung — mari kita lihat keduanya sekaligus:

apiVersion: 1
datasources:
  - name: Prometheus
    type: prometheus
    access: proxy
    url: http://127.0.0.1:9090
    isDefault: true
    editable: false

Pendekatan provisioning file (apiVersion: 1) adalah cara yang direkomendasikan Grafana untuk deployment otomatis: folder JSON dashboard tinggal ditaruh di dalam path yang terdaftar, dan Grafana akan memuatnya. Sedangkan modul grafana_dashboard memanfaatkan REST API Grafana — berguna untuk mengimpor dashboard dari library Grafana.com atau memperbarui dashboard yang sudah ada secara programatik. Pilih sesuai kebutuhan: provisioning file untuk dashboard milik sendiri (repo), modul API untuk dashboard komunitas.

Logging: ELK Stack (Elasticsearch, Logstash, Kibana)

Stack monitoring berbasis metric belum cukup — ketika aplikasi error, kalian butuh log untuk root cause analysis. Stack paling klasik adalah ELK. Deploy-nya dengan Ansible mengikuti pola yang sama: Elasticsearch sebagai penyimpan, Logstash sebagai pipeline, Kibana sebagai visualisasi, dan Filebeat sebagai agent di setiap node.

Berikut contoh konfigurasi Filebeat yang di-template agar mengirim log aplikasi ke Logstash:

filebeat.yml.j2
filebeat.inputs:
  - type: log
    enabled: true
    paths:
      - /var/log/nginx/*.log
      - /var/log/app/*.log
    fields:
      service: "{{ service_name }}"
    fields_under_root: true
 
output.logstash:
  hosts: ["{{ logstash_host }}:5044"]
 
logging.level: info
playbook-filebeat.yml
- name: Deploy Filebeat ke node aplikasi
  hosts: app_servers
  become: true
  vars:
    service_name: web-app
    logstash_host: logstash.internal
  tasks:
    - name: Render konfigurasi Filebeat
      ansible.builtin.template:
        src: filebeat.yml.j2
        dest: /etc/filebeat/filebeat.yml
        owner: root
        group: root
        mode: "0600"
      notify: restart filebeat
 
    - name: Mulai dan aktifkan Filebeat
      ansible.builtin.systemd_service:
        name: filebeat
        state: started
        enabled: true

Note

ELK Stack sangat powerful, tetapi boros resource — Elasticsearch butuh banyak RAM, dan tiga komponen (ES + Logstash + Kibana) harus dikelola bersama. Untuk tim kecil atau volume log menengah, banyak yang beralih ke Loki karena satu binary ringan dan terintegrasi langsung dengan Grafana yang sudah ada.

Loki & Promtail: Alternatif Ringan

Loki dirancang mengikuti filosofi Prometheus: labels sebagai indeks utama, simpan log apa adanya. Alih-alih membuat indeks penuh seperti Elasticsearch, Loki mengindeks label log dan membiarkan isi log di-compress — jauh lebih hemat resource. Pasangannya, Promtail, adalah agent yang mengirim log dari node ke Loki. Kedua konfigurasi ini bisa kita kelola sebagai file templated:

auth_enabled: false
 
server:
  http_listen_port: 3100
 
common:
  path_prefix: /var/lib/loki
  storage:
    filesystem:
      chunks_directory: /var/lib/loki/chunks
      rules_directory: /var/lib/loki/rules
  replication_factor: 1
 
schema_config:
  configs:
    - from: 2024-01-01
      store: tsdb
      object_store: filesystem
      schema: v13

Perhatikan label job: nginx, job: varlogs — ini yang membuat pencarian log di Loki cepat: query seperti {job="nginx"} |= "ERROR" hanya memindai label tersebut, bukan seluruh isi log. Kemudahan ini lagi-lagi mengajarkan prinsip yang sama: konfigurasi (label, path, target) adalah kode, bukan barang yang di-edit manual per server.

Kesalahan Umum (Common Pitfalls)

1. Target Prometheus di-hardcode

Menulis IP server satu per satu di prometheus.yml berarti menambahkan server baru = edit file manual. Ini membuat drift cepat terjadi. Gunakan templating dengan groups['nodes'] seperti di atas, atau lebih lanjut lagi: file_sd_configs/http_sd_configs yang digenerate Ansible.

2. Alert tidak pernah ter-evaluasi karena evaluation_interval

evaluation_interval menentukan seberapa sering Prometheus mengevaluasi rule files. Kalau nilainya terlalu besar (misal 1m padahal for: 10m dan kondisi cepat), alert datang telat. Standarnya 15s — cukup untuk sebagian besar kasus.

3. Alert rules YAML tidak valid

Satu kesalahan indentasi di alert.rules.yml dan Prometheus menolak memuat semua rules — alert diam-diam mati. Biasakan menambahkan task validasi, misalnya promtool check rules /etc/prometheus/rules/, sebelum restart/reload.

4. API key Grafana plaintext

API key Grafana yang dibutuhkan modul grafana_dashboard seharusnya tinggal di Ansible Vault. Jangan sampai ter-commit ke Git — kalian tahu konsekuensinya dari episode 14.

5. Node Exporter terekspos ke publik

Port 9100 seharusnya hanya bisa diakses dari server Prometheus. Ekspos ke internet = informasi detail server (kernel version, daftar disk, suhu) bocor ke siapa saja. Batasi dengan firewall/U FW (kita bahas hardening di episode 27).

6. Promtail tidak punya izin baca log

Promtail sering gagal diam-diam membaca /var/log karena grupnya tidak punya akses. Pastikan user Promtail masuk grup adm (Debian/Ubuntu) atau konfigurasikan izin yang benar.

7. Lupa merencanakan kapasitas disk Prometheus

Retention default Prometheus menyimpan 15 hari data. Dengan jumlah target dan metric cardinality tinggi, disk bisa penuh dalam hitungan hari. Konfigurasikan retention.time dan monitor disk volume-nya — ironisnya, stack monitoring paling sering kena alert disk full miliknya sendiri.

Penutup

Pada episode ini kita telah membangun fondasi observability as code dengan Ansible: deployment Prometheus dan Alertmanager, Node Exporter yang otomatis menyebar ke seluruh node, templating prometheus.yml dengan dynamic targets dari inventory, alert rules dan Alertmanager routing sebagai kode, Grafana dengan datasource dan dashboard yang di-provision, serta logging stack ELK dan alternatif ringannya Loki + Promtail.

Prinsip yang menyatukan semuanya: monitoring adalah infrastruktur. Ia harus dikelola dengan cara yang sama — versioned, di-review, idempotent, dan diterapkan otomatis. Dengan begitu, menambah server baru bukan berarti menambah pekerjaan manual, melainkan cukup menambahkan satu baris di inventory.

Di episode 26 selanjutnya, kita akan membawa seluruh skill ini ke level berikutnya: Cloud Infrastructure Provisioning — mengelola EC2, VPC, S3, dan IAM di AWS; Compute Engine dan GCS di Google Cloud; hingga Virtual Machines dan Virtual Networks di Azure — lengkap dengan pola multi-cloud. Pastikan tetap semangat!

Belajar Ansible - Monitoring & Observability Stack Automation | Belajar Ansible