Alerting #
Monitoring tanpa alerting seperti dashboard yang tidak ada yang melihat — masalah bisa terjadi tanpa ada yang tahu. Alerting adalah lapisan di atas monitoring yang secara proaktif memberitahu tim saat kondisi abnormal terdeteksi. Tapi alerting yang buruk bisa sama berbahayanya dengan tidak ada alerting sama sekali: terlalu banyak notifikasi yang tidak actionable membuat tim mati rasa dan mengabaikan alert yang benar-benar penting. Artikel ini membahas cara mengotomasi setup Alertmanager dengan Ansible dan pola alerting yang mengurangi noise.
Arsitektur Alerting #
Prometheus → evaluasi alert rules → FIRING alert
│
▼
Alertmanager → routing, grouping, deduplication, inhibition
│
├──► Slack (untuk warning)
├──► PagerDuty (untuk critical)
└──► Email (untuk digest harian)
Prometheus bertugas mendeteksi kondisi yang memenuhi alert rules. Alertmanager bertugas memutuskan siapa yang harus diberitahu, kapan, dan bagaimana — termasuk menggabungkan banyak alert yang terkait menjadi satu notifikasi.
Instalasi dan Konfigurasi Alertmanager #
# roles/alertmanager/tasks/main.yml
---
- name: Buat user alertmanager
user:
name: alertmanager
system: true
shell: /usr/sbin/nologin
home: /var/lib/alertmanager
create_home: true
- name: Download Alertmanager
get_url:
url: >
https://github.com/prometheus/alertmanager/releases/download/
v{{ alertmanager_version }}/alertmanager-{{ alertmanager_version }}.linux-amd64.tar.gz
dest: /tmp/alertmanager.tar.gz
checksum: "sha256:{{ alertmanager_checksum }}"
- name: Extract dan install binary
unarchive:
src: /tmp/alertmanager.tar.gz
dest: /tmp/
remote_src: true
- name: Salin binary ke /usr/local/bin
copy:
src: "/tmp/alertmanager-{{ alertmanager_version }}.linux-amd64/alertmanager"
dest: /usr/local/bin/alertmanager
owner: root
group: root
mode: '0755'
remote_src: true
- name: Deploy konfigurasi Alertmanager
template:
src: alertmanager.yml.j2
dest: /etc/alertmanager/alertmanager.yml
owner: alertmanager
group: alertmanager
mode: '0640'
validate: "alertmanager --config.file=%s --storage.path=/tmp/amtest check-config"
notify: Restart Alertmanager
- name: Deploy systemd unit file
template:
src: alertmanager.service.j2
dest: /etc/systemd/system/alertmanager.service
notify:
- Reload systemd
- Restart Alertmanager
- name: Jalankan Alertmanager
systemd:
name: alertmanager
state: started
enabled: true
Konfigurasi Routing dan Notifikasi #
Template konfigurasi Alertmanager dengan routing ke beberapa channel:
{# roles/alertmanager/templates/alertmanager.yml.j2 #}
global:
resolve_timeout: 5m
slack_api_url: "{{ vault_slack_webhook_url }}"
pagerduty_url: "https://events.pagerduty.com/v2/enqueue"
templates:
- /etc/alertmanager/templates/*.tmpl
route:
# Default receiver jika tidak ada route yang cocok
receiver: slack-warnings
group_by: ['alertname', 'environment', 'job']
group_wait: 30s # Tunggu 30 detik sebelum kirim notifikasi pertama
group_interval: 5m # Kirim update setiap 5 menit jika ada alert baru dalam grup
repeat_interval: 4h # Ulangi notifikasi setiap 4 jam jika belum resolved
routes:
# Alert critical → PagerDuty (untuk on-call duty)
- matchers:
- severity = critical
receiver: pagerduty-critical
repeat_interval: 1h # Ulangi lebih sering untuk critical
# Alert untuk environment production → channel terpisah
- matchers:
- environment = production
- severity =~ "warning|critical"
receiver: slack-production
continue: true # Lanjutkan ke route berikutnya juga
# Alert database → team database
- matchers:
- job =~ "postgresql|mysql|redis"
receiver: slack-database-team
receivers:
- name: slack-warnings
slack_configs:
- channel: "#alerts-warning"
send_resolved: true
title: >
{{ '{{' }} template "slack.title" . {{ '}}' }}
text: >
{{ '{{' }} template "slack.text" . {{ '}}' }}
- name: slack-production
slack_configs:
- channel: "#alerts-production"
send_resolved: true
icon_emoji: ":fire:"
- name: pagerduty-critical
pagerduty_configs:
- routing_key: "{{ vault_pagerduty_routing_key }}"
description: >
{{ '{{' }} template "pagerduty.description" . {{ '}}' }}
- name: slack-database-team
slack_configs:
- channel: "#team-database"
send_resolved: true
inhibit_rules:
# Jika ada alert critical dari host yang sama, suppress alert warning-nya
- source_matchers:
- severity = critical
target_matchers:
- severity = warning
equal: ['instance', 'job']
Template Notifikasi yang Informatif #
Alert yang hanya bilang “something went wrong” tidak berguna. Template yang baik menyertakan semua informasi yang dibutuhkan untuk memulai investigasi:
{# /etc/alertmanager/templates/slack.tmpl #}
{{ '{{' }} define "slack.title" {{ '}}' }}
[{{ '{{' }} .Status | toUpper {{ '}}' }}{{ '{{' }} if eq .Status "firing" {{ '}}' }}:{{ '{{' }} .Alerts.Firing | len {{ '}}' }}{{ '{{' }} end {{ '}}' }}]
{{ '{{' }} .CommonLabels.alertname {{ '}}' }} — {{ '{{' }} .CommonLabels.environment {{ '}}' }}
{{ '{{' }} end {{ '}}' }}
{{ '{{' }} define "slack.text" {{ '}}' }}
{{ '{{' }} range .Alerts {{ '}}' }}
*Host:* `{{ '{{' }} .Labels.instance {{ '}}' }}`
*Keterangan:* {{ '{{' }} .Annotations.description {{ '}}' }}
*Dimulai:* {{ '{{' }} .StartsAt.Format "2006-01-02 15:04:05 WIB" {{ '}}' }}
*Runbook:* {{ '{{' }} .Annotations.runbook_url {{ '}}' }}
{{ '{{' }} end {{ '}}' }}
{{ '{{' }} end {{ '}}' }}
Mengelola Silences dengan Ansible #
Saat ada maintenance terjadwal, buat silence untuk mencegah false alert:
# playbooks/create-silence.yml
---
- name: Buat silence di Alertmanager selama maintenance
hosts: localhost
vars:
alertmanager_url: "http://alertmanager.internal:9093"
silence_duration_hours: 4
silence_comment: "Maintenance terjadwal — {{ ansible_date_time.date }}"
tasks:
- name: Hitung waktu berakhir silence
set_fact:
silence_end: >-
{{ (ansible_date_time.epoch | int + silence_duration_hours * 3600) | strftime('%Y-%m-%dT%H:%M:%S.000Z') }}
- name: Buat silence di Alertmanager
uri:
url: "{{ alertmanager_url }}/api/v2/silences"
method: POST
body_format: json
body:
matchers:
- name: environment
value: "{{ env }}"
isRegex: false
startsAt: "{{ ansible_date_time.iso8601 }}"
endsAt: "{{ silence_end }}"
comment: "{{ silence_comment }}"
createdBy: "ansible-automation"
status_code: 200
register: silence_result
- name: Tampilkan ID silence yang dibuat
debug:
msg: "Silence dibuat dengan ID: {{ silence_result.json.silenceID }}"
Pola Mengurangi Alert Fatigue #
Alert fatigue terjadi saat tim menerima terlalu banyak notifikasi yang tidak actionable. Beberapa prinsip untuk menguranginya:
# Alert rules yang baik memiliki:
# 1. Threshold yang bermakna (bukan arbitrer)
# 2. Durasi 'for' yang cukup (menghindari flapping)
# 3. Severity yang tepat
# 4. Annotation yang actionable
# ANTI-PATTERN: alert yang terlalu sensitif
- alert: HighCPU
expr: cpu_usage > 80
# Tidak ada 'for' — alert setiap kali ada spike sesaat
# BENAR: alert dengan durasi dan konteks
- alert: SustainedHighCPU
expr: >
avg by(instance) (
rate(node_cpu_seconds_total{mode!="idle"}[5m])
) * 100 > 90
for: 15m # Harus bertahan 15 menit sebelum alert
labels:
severity: warning
annotations:
summary: "CPU usage tinggi berkelanjutan di {{ '{{' }} $labels.instance {{ '}}' }}"
description: "CPU {{ '{{' }} $value | printf \"%.1f\" {{ '}}' }}% selama 15 menit terakhir"
runbook_url: "https://wiki.company.com/runbooks/high-cpu"
Ringkasan #
- Alertmanager menangani routing, grouping, deduplication, dan silencing — pisahkan tanggung jawab ini dari Prometheus yang hanya bertugas deteksi.
group_waitdangroup_intervalmencegah banjir notifikasi saat banyak alert muncul bersamaan — Alertmanager menunggu dan menggabungkannya.inhibit_rulesmenekan alert turunan saat alert induk sudah firing — misalnya, suppress warning saat critical dari host yang sama sudah aktif.- Template notifikasi yang baik menyertakan: host, deskripsi masalah, waktu mulai, dan link ke runbook — semua yang dibutuhkan untuk memulai investigasi.
- Gunakan
for:di alert rules — alert tanpa durasi minimum akan fire setiap spike sesaat dan menjadi noise.- Silences via Ansible memungkinkan maintenance terjadwal tanpa banjir notifikasi false alarm — buat silence sebelum mulai maintenance, hapus setelah selesai.