Alerting

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_wait dan group_interval mencegah banjir notifikasi saat banyak alert muncul bersamaan — Alertmanager menunggu dan menggabungkannya.
  • inhibit_rules menekan 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.

← Sebelumnya: Monitoring   Berikutnya: Dashboard →

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