Alertmanager: จัดการ Alerts จาก Prometheus ด้วย Grouping Routing

Alertmanager เป็นส่วนประกอบสำคัญของ Prometheus ecosystem ที่ทำหน้าที่รับสัญญาณจาก Prometheus server แล้วจัดการตั้งแต่การ deduplicate, grouping, silencing, inhibition ไปจนถึงการ route ส่งต่อยังช่องทางปลายทางเช่น email, Slack, PagerDuty หรือ webhook การใช้งานอย่างถูกวิธีจะช่วยลด noise และทำให้ทีม oncall ได้รับข้อมูลที่ actionable จริง

บทความนี้จะลงรายละเอียดวิธีการ deploy ตัว handler, เขียน routing tree ที่ซับซ้อน, ใช้ inhibit_rules ลดการแจ้งเตือนซ้ำซ้อน และตั้ง time_intervals สำหรับ business hours เพื่อให้ระบบแจ้งเตือนทำงานได้ฉลาดขึ้น

สถาปัตยกรรมและการทำงาน

ตัวจัดการทำงานเป็น service แยกจาก Prometheus server โดยรับ event ผ่าน HTTP API จาก Prometheus ที่ evaluate rule แล้วพบเงื่อนไข firing หลังจากนั้นจะผ่าน pipeline ภายในที่ทำ deduplication, grouping, routing, และสุดท้ายส่งออกไปยัง receiver ที่กำหนด

  • Deduplication — ถ้ามี Prometheus หลาย instance ส่งเหตุการณ์ตัวเดียวกัน ระบบจะรวมให้เหลือตัวเดียว
  • Grouping — จัดกลุ่ม event ที่เกี่ยวข้องกันเข้าด้วยกัน ลดจำนวน notification
  • Routing — ส่งแต่ละกลุ่มไปยัง receiver ที่เหมาะสมตาม label
  • Silencing — ระงับการแจ้งเตือนชั่วคราวตามเงื่อนไข label matcher
  • Inhibition — ระงับ event เมื่อมี event อื่นที่ rank สูงกว่า firing อยู่

การ Deploy

วิธีที่ง่ายที่สุดคือใช้ Docker Compose รันคู่กับ Prometheus

version: '3'
services:
  prometheus:
    image: prom/prometheus:v2.48.0
    ports:
      - "9090:9090"
    volumes:
      - ./prometheus.yml:/etc/prometheus/prometheus.yml
      - ./rules.yml:/etc/prometheus/rules.yml

  alertmanager:
    image: prom/alertmanager:v0.26.0
    ports:
      - "9093:9093"
    volumes:
      - ./alertmanager.yml:/etc/alertmanager/alertmanager.yml
    command:
      - '--config.file=/etc/alertmanager/alertmanager.yml'
      - '--storage.path=/alertmanager'

ใน prometheus.yml ต้องประกาศ endpoint เพื่อให้ Prometheus รู้ว่าจะส่งสัญญาณไปที่ไหน

alerting:
  alertmanagers:
    - static_configs:
        - targets: ['alertmanager:9093']

rule_files:
  - '/etc/prometheus/rules.yml'

โครงสร้าง Configuration

ไฟล์ alertmanager.yml ประกอบด้วย 4 section หลัก global สำหรับการตั้งค่าส่วนกลางเช่น smtp_smarthost, route สำหรับ routing tree, receivers สำหรับปลายทาง และ inhibit_rules สำหรับกฎการระงับ

global:
  resolve_timeout: 5m
  smtp_smarthost: 'smtp.gmail.com:587'
  smtp_from: '[email protected]'
  smtp_auth_username: '[email protected]'
  smtp_auth_password: 'app-password'

route:
  receiver: 'default-team'
  group_by: ['alertname', 'cluster']
  group_wait: 30s
  group_interval: 5m
  repeat_interval: 4h

receivers:
  - name: 'default-team'
    email_configs:
      - to: '[email protected]'

Grouping: ลด Notification ซ้ำ

ถ้ามี 100 pod ใน Kubernetes cluster ล่มพร้อมกัน เราไม่ต้องการ notification 100 ฉบับ Grouping จะรวมเข้าเป็นฉบับเดียวตาม label ที่กำหนดใน group_by

  • group_wait — รอเวลานี้ก่อนส่งกลุ่มแรก เพื่อรวมเหตุการณ์ที่เข้ามาติด ๆ กันได้ (ค่าแนะนำ 30s)
  • group_interval — ถ้ามี event ใหม่เข้ามาในกลุ่มเดิม รอนานเท่าไรก่อนส่ง notification รอบถัดไป (ค่าแนะนำ 5m)
  • repeat_interval — ถ้ากลุ่มเดิมยัง firing อยู่ ส่งซ้ำทุก ๆ กี่เวลา (ค่าแนะนำ 4h)

Routing Tree แบบ Hierarchical

Routing tree เป็นโครงสร้างต้นไม้ที่ตรวจ label ของแต่ละ event แล้วเลือก receiver ตามลำดับ branch ลูกจะ inherit ค่าจากพ่อ ทำให้เขียน config ยืดหยุ่น

route:
  receiver: 'default-team'
  group_by: ['alertname', 'cluster', 'service']
  group_wait: 30s
  group_interval: 5m
  repeat_interval: 4h
  routes:
    - match:
        severity: critical
      receiver: 'pagerduty-critical'
      continue: true
    - match_re:
        service: (database|db-.*)
      receiver: 'database-team'
      routes:
        - match:
            severity: warning
          receiver: 'database-team-slack'
    - match:
        team: frontend
      receiver: 'frontend-slack'
      group_wait: 10s

ใน route ด้านบน continue: true ทำให้ event ที่ match แล้วยังคงไปตรวจ route ถัดไปได้ เหมาะสำหรับกรณี critical ต้องส่งทั้ง PagerDuty และ Slack พร้อมกัน

Inhibit Rules: ลดการแจ้งเตือนที่ซ้ำซ้อน

เมื่อเซิร์ฟเวอร์ดับทั้งเครื่อง service ทุกตัวก็ดับตามไปด้วย การแจ้งเตือนทุกตัวคงไม่มีประโยชน์ เราสามารถใช้ inhibit_rules เพื่อระงับการแจ้งเตือนระดับ warning ถ้าเหตุการณ์ระดับ critical ใน cluster เดียวกัน firing อยู่

inhibit_rules:
  - source_match:
      severity: 'critical'
    target_match:
      severity: 'warning'
    equal: ['cluster', 'service']

  - source_match:
      alertname: 'NodeDown'
    target_match_re:
      alertname: '(HighCPU|HighMemory|DiskFull)'
    equal: ['instance']

rule แรก — ถ้ามี event severity=critical บน cluster/service เดียวกัน ระงับ notification ที่ severity=warning ทั้งหมด rule ที่สอง — เมื่อ node ดับ ระงับการแจ้งเตือนเกี่ยวกับ CPU/Memory/Disk ของ instance นั้น

Time Intervals: แจ้งเตือนเฉพาะเวลา

บางระบบที่ไม่ใช่ production ไม่จำเป็นต้องปลุกคนกลางดึก เราสามารถกำหนด time_intervals แล้วใช้ mute_time_intervals หรือ active_time_intervals ใน route

time_intervals:
  - name: business-hours
    time_intervals:
      - times:
          - start_time: '09:00'
            end_time: '18:00'
        weekdays: ['monday:friday']
        location: 'Asia/Bangkok'

  - name: weekends
    time_intervals:
      - weekdays: ['saturday', 'sunday']

route:
  receiver: 'default-team'
  routes:
    - match:
        severity: warning
        env: staging
      receiver: 'staging-slack'
      active_time_intervals:
        - business-hours
    - match:
        severity: critical
      receiver: 'pagerduty'
      mute_time_intervals:
        - weekends

ตัวอย่าง Receiver ที่ใช้บ่อย

Receiver กำหนดปลายทางที่จะส่ง notification ไป สามารถตั้งหลายช่องทางใน receiver เดียวได้

receivers:
  - name: 'default-team'
    email_configs:
      - to: '[email protected]'
        send_resolved: true

  - name: 'pagerduty-critical'
    pagerduty_configs:
      - routing_key: 'YOUR_PAGERDUTY_INTEGRATION_KEY'
        severity: 'critical'

  - name: 'slack-warnings'
    slack_configs:
      - api_url: 'https://hooks.slack.com/services/XXX/YYY/ZZZ'
        channel: '#alerts'
        title: '{{ .GroupLabels.alertname }}'
        text: |
          {{ range .Alerts }}
          *Summary:* {{ .Annotations.summary }}
          *Severity:* {{ .Labels.severity }}
          {{ end }}

  - name: 'webhook-custom'
    webhook_configs:
      - url: 'http://custom-service/alerts'
        send_resolved: true

Silencing: ระงับการแจ้งเตือนชั่วคราว

เมื่อมีแผน maintenance เราสามารถสร้าง silence ผ่าน UI ที่ http://alertmanager:9093 หรือใช้ API โดยระบุ label matcher และระยะเวลา เช่น ระงับการแจ้งเตือนทั้งหมดของ cluster prod-east เป็นเวลา 2 ชั่วโมง

curl -X POST http://localhost:9093/api/v2/silences \
  -H "Content-Type: application/json" \
  -d '{
    "matchers": [
      {"name": "cluster", "value": "prod-east", "isRegex": false}
    ],
    "startsAt": "2026-04-18T10:00:00Z",
    "endsAt": "2026-04-18T12:00:00Z",
    "createdBy": "ops-team",
    "comment": "Scheduled maintenance"
  }'

High Availability

ในระบบ production ควรรัน Alertmanager อย่างน้อย 2-3 instance แล้ว cluster กันผ่าน gossip protocol เพื่อให้ deduplication ทำงานข้าม instance ได้ Prometheus แต่ละตัวต้องประกาศ target ทุก instance ใน config

# บน instance 1
alertmanager --config.file=alertmanager.yml \
  --cluster.listen-address=0.0.0.0:9094 \
  --cluster.peer=alertmanager-2:9094 \
  --cluster.peer=alertmanager-3:9094

# บน instance 2
alertmanager --config.file=alertmanager.yml \
  --cluster.listen-address=0.0.0.0:9094 \
  --cluster.peer=alertmanager-1:9094 \
  --cluster.peer=alertmanager-3:9094

Best Practices

  • ใช้ amtool ตรวจ config — รัน amtool check-config alertmanager.yml ก่อน deploy ทุกครั้ง
  • ทดสอบ routing tree — ใช้ amtool config routes test severity=critical service=api เพื่อดูว่า event จะไปถึง receiver ไหน
  • group_by ให้เหมาะสม — อย่า group แคบเกินไป (เช่น instance เดียว) ทำให้ notification ท่วม อย่า group กว้างไปจนสูญรายละเอียด
  • ใช้ severity label — กำหนด severity ใน Prometheus rule ให้เป็นมาตรฐาน (critical, warning, info) แล้วใช้ routing ตาม label นี้
  • เปิด send_resolved — ให้รู้ว่าเหตุการณ์ถูกแก้ไขแล้ว ช่วยปิด ticket อัตโนมัติ
  • Monitor ตัว Alertmanager เอง — ใช้ metric alertmanager_notifications_failed_total เพื่อจับกรณีส่งไม่สำเร็จ

สรุป

ระบบจัดการการแจ้งเตือนของ Prometheus มีความสามารถครบครันตั้งแต่ grouping, inhibition, silencing ไปจนถึง routing tree ที่ซับซ้อน การเขียน config อย่างถูกต้องช่วยให้ทีม oncall ได้รับเฉพาะ notification ที่สำคัญและมีบริบทเพียงพอสำหรับ troubleshoot

จุดสำคัญคือเริ่มจาก routing tree ที่เรียบง่าย แล้วค่อย ๆ เพิ่ม inhibit rules กับ time intervals ตามกรณีที่พบจริง การใช้ amtool ทดสอบก่อน deploy จะช่วยป้องกัน notification ที่ route ผิด receiver หรือถูกระงับโดยไม่ตั้งใจ