Workshop: ตั้งค่า On-Call Alerting ด้วย Prometheus + Alertmanager + PagerDuty

การมีระบบ Monitoring ที่ดีไม่เพียงพอหากไม่มี On-Call Alerting ที่สามารถแจ้งเตือนทีมได้ทันเวลาเมื่อเกิดปัญหาในเวลากลางคืนหรือวันหยุด การตั้ง Alertmanager ร่วมกับ PagerDuty ช่วยให้ทีม SRE/DevOps สามารถ rotate เวร รับ alert ผ่านโทรศัพท์ และจัดการ incident ได้อย่างเป็นระบบ ลด MTTR (Mean Time To Recovery) และป้องกัน alert fatigue

Workshop นี้จะสอนการเชื่อมต่อ Prometheus Alertmanager กับ PagerDuty ตั้งแต่การสร้าง Service, กำหนด Routing rules, จัดการ Alert Grouping, Inhibition, และ Silencing เพื่อให้ทีมได้ระบบแจ้งเตือนที่เชื่อถือได้สำหรับ Production

ทำไมต้อง On-Call Alerting

ระบบแจ้งเตือนผ่าน Email หรือ Slack เพียงอย่างเดียวมีข้อจำกัดเมื่อเกิด incident นอกเวลาทำการ — ทีมอาจไม่เห็นข้อความหรือตอบสนองช้า On-Call Alerting ผ่าน PagerDuty แก้ปัญหานี้ด้วยการโทรเรียกหรือส่ง SMS ไปยังคนที่อยู่เวรตาม schedule ที่กำหนดไว้ พร้อม escalation policy ที่ส่งต่อให้คนถัดไปหากไม่มีการ acknowledge ภายในเวลาที่กำหนด

Prerequisites

  • Prometheus server ที่ติดตั้งแล้ว (จาก Workshop ก่อนหน้า)
  • Alertmanager version 0.26+
  • บัญชี PagerDuty (มี Free tier สำหรับทดสอบ)
  • ความเข้าใจ PromQL และ Alert Rules พื้นฐาน

Step 1: สร้าง PagerDuty Service และ Integration Key

เข้าสู่ PagerDuty แล้วสร้าง Service ใหม่ โดยเลือก Integration Type เป็น “Prometheus” หรือ “Events API v2” ระบบจะสร้าง Integration Key (routing key) ให้เก็บค่านี้ไว้ใช้กับ Alertmanager

  • ไปที่ Services > Service Directory > + New Service
  • ตั้งชื่อ Service เช่น “Production API”
  • กำหนด Escalation Policy (ใครรับก่อน, ใครรับต่อ)
  • เลือก Integration: “Events API v2”
  • คัดลอก Integration Key ที่ได้

Step 2: ตั้งค่า Alertmanager Config

แก้ไขไฟล์ alertmanager.yml เพื่อเชื่อมต่อกับ PagerDuty โดยใช้ pagerduty_configs ใน receivers:

global:
  resolve_timeout: 5m

route:
  group_by: ['alertname', 'cluster', 'service']
  group_wait: 30s
  group_interval: 5m
  repeat_interval: 4h
  receiver: 'default-pagerduty'
  routes:
    - match:
        severity: critical
      receiver: 'critical-pagerduty'
      continue: true
    - match:
        severity: warning
      receiver: 'slack-warnings'

receivers:
  - name: 'default-pagerduty'
    pagerduty_configs:
      - routing_key: 'YOUR_INTEGRATION_KEY_HERE'
        severity: 'error'
        description: '{{ .CommonAnnotations.summary }}'

  - name: 'critical-pagerduty'
    pagerduty_configs:
      - routing_key: 'YOUR_CRITICAL_KEY_HERE'
        severity: 'critical'
        client: 'Alertmanager'
        client_url: 'https://alerts.example.com'
        details:
          firing: '{{ .Alerts.Firing | len }}'
          resolved: '{{ .Alerts.Resolved | len }}'

  - name: 'slack-warnings'
    slack_configs:
      - api_url: 'https://hooks.slack.com/services/XXX/YYY/ZZZ'
        channel: '#alerts-warning'
        title: '{{ .CommonAnnotations.summary }}'

Step 3: กำหนด Alert Rules ใน Prometheus

เขียน alert rules ที่มี label severity เพื่อให้ Alertmanager routing ได้ถูกต้อง ตัวอย่าง alerts.yml:

groups:
  - name: production-alerts
    interval: 30s
    rules:
      - alert: HighErrorRate
        expr: |
          sum(rate(http_requests_total{status=~"5.."}[5m])) by (service)
          /
          sum(rate(http_requests_total[5m])) by (service)
          > 0.05
        for: 5m
        labels:
          severity: critical
          team: backend
        annotations:
          summary: "Error rate สูงเกิน 5% บน {{ $labels.service }}"
          description: "Service {{ $labels.service }} มี error rate {{ $value | humanizePercentage }} ในช่วง 5 นาทีที่ผ่านมา"
          runbook_url: "https://wiki.example.com/runbooks/high-error-rate"

      - alert: PodCrashLooping
        expr: rate(kube_pod_container_status_restarts_total[15m]) > 0
        for: 10m
        labels:
          severity: critical
          team: platform
        annotations:
          summary: "Pod {{ $labels.namespace }}/{{ $labels.pod }} กำลัง crash loop"

      - alert: DiskSpaceLow
        expr: |
          (node_filesystem_avail_bytes{mountpoint="/"} / node_filesystem_size_bytes{mountpoint="/"}) * 100 < 15
        for: 15m
        labels:
          severity: warning
          team: infra
        annotations:
          summary: "Disk space เหลือน้อยกว่า 15% บน {{ $labels.instance }}"

Step 4: ทดสอบ End-to-End

ทดสอบว่า alert ส่งไปยัง PagerDuty จริงโดย trigger alert ด้วยมือผ่าน amtool หรือ curl ตรงไปที่ Alertmanager API:

curl -XPOST http://alertmanager:9093/api/v2/alerts -H "Content-Type: application/json" -d '[
  {
    "labels": {
      "alertname": "TestCriticalAlert",
      "severity": "critical",
      "service": "test-service"
    },
    "annotations": {
      "summary": "ทดสอบการแจ้งเตือน PagerDuty"
    },
    "generatorURL": "http://prometheus:9090"
  }
]'

ภายใน 30 วินาที ควรได้รับการโทรหรือ notification จาก PagerDuty ไปยังคนที่อยู่เวรตาม Escalation Policy

Step 5: Alert Grouping และ Inhibition

เมื่อ incident ใหญ่เกิดขึ้น (เช่น เครือข่ายล่ม) อาจมี alert นับสิบเข้ามาพร้อมกัน Alertmanager มี feature สำคัญ 2 อย่างที่ช่วยลด noise:

  • Grouping: รวม alert ที่มี label เดียวกันเข้าเป็น notification เดียว ใช้ group_by, group_wait, group_interval
  • Inhibition: ระงับ alert ระดับรองเมื่อมี alert ระดับสูงเกิดขึ้นแล้ว เช่น ถ้า cluster ทั้งหมดล่ม ไม่ต้อง alert รายบริการ
inhibit_rules:
  - source_matchers:
      - severity = "critical"
      - alertname = "ClusterDown"
    target_matchers:
      - severity = "warning"
    equal: ['cluster']

Step 6: Silencing ระหว่าง Maintenance

ก่อนทำ maintenance หรือ deployment ที่รู้ล่วงหน้าว่าจะมี alert เกิดขึ้น ควรสร้าง Silence ไว้ก่อนเพื่อไม่ให้รบกวนคนอยู่เวร ใช้ amtool หรือ Alertmanager UI:

amtool silence add alertname="HighErrorRate" service="payment-api" \
  --author="[email protected]" \
  --comment="Scheduled maintenance window" \
  --duration=2h

Best Practices สำหรับ On-Call

  • Runbook Link: ทุก alert ต้องมี runbook_url ระบุวิธีแก้ไขเบื้องต้น
  • Alert ต้อง actionable: หาก alert ไม่สามารถลงมือแก้ไขได้ทันที ควรลดระดับเป็น warning หรือ ticket แทน
  • Severity แยกชัดเจน: critical = โทรเรียกตีสอง, warning = รอเช้าค่อยดู
  • Rotate ไม่เกิน 1 สัปดาห์ต่อคน: ป้องกัน burnout
  • Post-Incident Review: หลัง incident ต้อง review ว่า alert รายนี้เป็น false positive, missing coverage, หรือ actionable

Alert Fatigue: ศัตรูตัวร้ายของ On-Call

หากทีมได้รับ alert มากเกินไปจะเริ่มมองข้าม alert ที่สำคัญจริง ๆ วิธีลด alert fatigue ที่ได้ผล: ตั้งค่า for: ให้นานพอ (5-15 นาที) ไม่ให้ flaky metrics trigger alert, ใช้ SLO-based alerting แทน threshold-based, และทำ weekly review ของ alert ที่ส่งไปยัง PagerDuty เพื่อลบหรือปรับเกณฑ์ของ alert ที่ไม่มีประโยชน์

สรุป

การต่อ Alertmanager เข้ากับ PagerDuty เป็นขั้นตอนสำคัญที่ทำให้ระบบ Monitoring กลายเป็น Alerting Pipeline ที่ใช้งานจริงใน Production ได้ การออกแบบ routing, grouping, inhibition, และ escalation ที่ดีจะช่วยให้ทีมตอบสนองต่อ incident ได้เร็ว ลด MTTR และป้องกันไม่ให้เกิด alert fatigue

สิ่งสำคัญที่ต้องจำคือ alert ทุกตัวควรเป็น actionable มี runbook รองรับ และทีมต้องมี culture ของการ review alert อย่างสม่ำเสมอ ไม่ใช่ตั้งไว้แล้วลืม