อาการล้าจากการแจ้งเตือนมีจริง: นักพัฒนาที่เก่งแก้มันอย่างไร

เมื่อการแจ้งเตือนทุกอันดูเร่งด่วนเท่ากัน ก็เลยไม่มีอะไรรู้สึกเร่งด่วน นี่คือวิธีจัดระบบมอนิเตอร์ใหม่ด้วยการแจ้งเตือนแบบแบ่งระดับ และเครื่องมือที่เข้ากับ Echobell ได้ดี

อัปเดตเมื่อ

สารบัญ

มันมีชื่อเรียกของมัน: อาการล้าจากการแจ้งเตือน (alert fatigue) คือสิ่งที่เกิดขึ้นเมื่อเครื่องมือมอนิเตอร์ของคุณส่งการแจ้งเตือนมามากจนสมองเริ่มกรองมันทิ้งเองโดยอัตโนมัติ แม้แต่อันที่สำคัญจริง ๆ

มันมักเริ่มจากเรื่องเล็ก ๆ คุณตั้งการแจ้งเตือนทางอีเมลสำหรับข้อผิดพลาด 5xx ทุกครั้ง จากนั้นก็ตั้งการแจ้งเตือนใน Slack สำหรับทุกบิลด์ที่ล้มเหลว แล้วก็มี Datadog เด้ง อีเมลจาก Sentry ข้อความจาก UptimeRobot ภายในเดือนเดียว มือถือคุณสั่นวันละ 50 ครั้งและไม่มีอะไรรู้สึกเร่งด่วนเลย ส่วนที่แย่จริง ๆ คืออะไร? พอมีอะไรพังขึ้นมาจริง ๆ คุณก็ฝึกตัวเองให้เพิกเฉยไปเรียบร้อยแล้ว

อาการล้าจากการแจ้งเตือนฆ่าเวลาตอบสนอง และเวลาตอบสนองที่ช้าก็ฆ่าผลิตภัณฑ์

ทำไมระบบมอนิเตอร์ส่วนใหญ่จึงสร้างสัญญาณรบกวนมากกว่าสัญญาณจริง

ปัญหาพื้นฐานคือเครื่องมือแจ้งเตือนส่วนใหญ่ปฏิบัติกับทุกอีเวนต์เหมือนกันหมด เทสต์ที่ล้มเหลวแบบไม่คงเส้นคงวา 10% ของเวลาทั้งหมด ได้รูปแบบการแจ้งเตือนเดียวกับ "API การชำระเงินคืนค่า 500 ให้ผู้ใช้ทุกคน" อันหนึ่งควรโทรปลุกคุณตอนตีสอง อีกอันน่าจะแค่โผล่ในสรุปประจำสัปดาห์ก็พอ

เมื่อทุกอย่างได้รับการปฏิบัติเหมือนกัน คนก็เริ่มเพิกเฉยกับทุกอย่าง

ทางแก้ไม่ใช่การลดจำนวนการแจ้งเตือน แต่คือการส่งที่ฉลาดขึ้น อีเวนต์เดียวกันที่ระดับความรุนแรงต่างกัน หรือที่ความถี่ต่างกัน ควรให้ความเร่งด่วนบนมือถือของคุณต่างกันด้วย

แนวทางสามระดับ

Echobell ให้โหมดการส่งการแจ้งเตือนสามแบบ และการใช้ทั้งสามแบบให้ดีคือหัวใจทั้งหมด:

  • ปกติ (Active): การแจ้งเตือนแบบพุชมาตรฐาน เหมาะกับอีเวนต์เชิงข้อมูลที่ไม่ต้องลงมือทำทันที
  • สำคัญตามเวลา: ทะลุโหมดโฟกัสของ iOS ได้ เหมาะกับเรื่องที่ต้องจัดการภายในหนึ่งถึงสองชั่วโมงข้างหน้า
  • โทรเข้า: ทำให้มือถือของคุณดังเหมือนสายเรียกเข้า สงวนไว้ให้เรื่องประเภท "แก้เดี๋ยวนี้ ไม่งั้นเกิดผลเสียจริง"

เป้าหมายคือรักษาระดับโทรเข้าไว้ให้อีเวนต์ที่การตอบสนองช้าแล้วมีผลกระทบจริง เช่น รายได้ที่หายไป ความล้มเหลวลูกโซ่ หรือข้อมูลผู้ใช้ที่เสี่ยง ส่วนที่เหลือให้ลดลงมาเป็นสำคัญตามเวลาหรือต่ำกว่านั้น

Sentry: เลิกถูกเรียกทุกครั้งที่มี exception ของ Python

Sentry คือตัวอย่างคลาสสิกของอาการล้าจากการแจ้งเตือน ค่าเริ่มต้นของมันคือส่งอีเมลหาคุณทุกครั้งที่มี issue ประเภทใหม่ ในโค้ดเบสที่มีความเคลื่อนไหวช่วงสัปดาห์ปกติ นั่นคือสายน้ำที่ไหลไม่หยุด

นี่คือการตั้งค่าที่ฉลาดกว่า:

  1. ใน Sentry ไปที่ Alerts → Create Alert → Issue Alert
  2. เพิ่มเงื่อนไข: The issue is seen more than 10 times in 1 hour
  3. เพิ่มการกระทำ: Send a notification via webhook → วาง URL ของช่อง Echobell ของคุณ
  4. ตั้งประเภทการแจ้งเตือนของช่องเป็น time-sensitive

สำหรับเส้นทางที่วิกฤตจริง ๆ เช่น exception ที่ไม่ถูกจัดการในโฟลว์การชำระเงิน การยืนยันตัวตนล้มเหลว หรือข้อมูลเสียหาย ให้สร้างการแจ้งเตือนแยกอีกอันที่มีเกณฑ์ต่ำกว่า และใช้ช่อง Echobell ระดับ calling ช่องนั้นจะดังเฉพาะเมื่อมีอะไรพังในเส้นทางเจาะจงนั้นเท่านั้น

ผลลัพธ์: issue ทั่วไปสะสมเงียบ ๆ อยู่ในแดชบอร์ด Sentry ส่วนปัญหาที่ทำให้โปรดักชันพังจะโทรเข้ามือถือคุณ

Prometheus และ AlertManager: จัดเส้นทางตามความรุนแรง

ถ้าคุณใช้ Prometheus อยู่ คุณก็มี AlertManager คอยจัดเส้นทางให้อยู่แล้ว คุณส่งการแจ้งเตือนตรงไปยัง Echobell ได้โดยเพิ่มมันเป็น webhook receiver

ในไฟล์ alertmanager.yml ของคุณ:

receivers:
  - name: echobell-critical
    webhook_configs:
      - url: https://hook.echobell.one/t/<channel-token>
        send_resolved: true

  - name: slack-warnings
    slack_configs:
      - api_url: YOUR_SLACK_WEBHOOK

route:
  group_by: ['alertname', 'job']
  group_wait: 30s
  group_interval: 5m
  repeat_interval: 4h
  receiver: slack-warnings
  routes:
    - match:
        severity: critical
      receiver: echobell-critical

ด้วยการตั้งค่านี้ การแจ้งเตือนที่มี severity: critical จะไปยัง Echobell และโทรเข้ามือถือคุณ ส่วนคำเตือนจะไป Slack ที่รอถึงเช้าได้ คุณไม่ต้องแก้กฎของ Prometheus แม้แต่ข้อเดียว แค่เพิ่มชั้นการจัดเส้นทางใน AlertManager เท่านั้น

ตั้งช่อง Echobell เองให้เป็น โทรเข้า เพราะถ้า AlertManager ติดป้ายว่า critical มันก็คู่ควรกับเสียงเรียกเข้าจริง ๆ

AWS CloudWatch: SNS → Lambda → Echobell

CloudWatch ไม่มีเอาต์พุตแบบ webhook ในตัว แต่คุณไปถึงจุดนั้นได้ในไม่กี่นาทีด้วย SNS และฟังก์ชัน Lambda เล็ก ๆ

  1. สร้าง SNS topic แล้วผูกเข้ากับ CloudWatch alarm ของคุณ
  2. สร้างฟังก์ชัน Lambda ที่ subscribe topic นั้น:
import json
import urllib.request

def lambda_handler(event, context):
    message = json.loads(event['Records'][0]['Sns']['Message'])
    alarm_state = message.get('NewStateValue', 'UNKNOWN')

    payload = {
        "title": f"AWS: {message['AlarmName']}",
        "body": message.get('NewStateReason', 'No details'),
        "notificationType": "calling" if alarm_state == "ALARM" else "active"
    }

    req = urllib.request.Request(
        'https://hook.echobell.one/t/<channel-token>',
        data=json.dumps(payload).encode(),
        headers={'Content-Type': 'application/json'},
        method='POST'
    )
    urllib.request.urlopen(req)

รูปแบบนี้ใช้ได้กับทุกบริการของ AWS ที่รองรับ SNS ทั้งอีเวนต์ของ RDS ความล้มเหลวของบริการ ECS การแจ้งเตือนเมื่อค่าใช้จ่ายถึงเกณฑ์ และการเปลี่ยนสถานะของ EC2 instance เพิ่ม Lambda หนึ่งตัว ต่อเข้ากับ SNS แล้วทุก CloudWatch alarm ก็กลายเป็นสายโทรเมื่อมันทำงานจริง

ทำให้การจัดเส้นทางการแจ้งเตือนเป็นการตัดสินใจของทีม

พลังที่แท้จริงของการแจ้งเตือนแบบแบ่งระดับคือการทำให้การตัดสินใจเรื่องเส้นทางชัดเจน ไม่ใช่แค่สิ่งที่คนคนหนึ่งเคยตั้งไว้ครั้งเดียวแล้วไม่มีใครหาเจอ

โครงสร้างที่ใช้ได้จริงสำหรับทีมวิศวกรรมเล็ก ๆ:

ช่องประเภทใครติดตาม
production-api-criticalโทรเข้าวิศวกรที่เข้าเวร on-call
production-api-warningsสำคัญตามเวลาทีมพัฒนาทั้งทีม
staging-allปกติทีมพัฒนา (ไม่บังคับ)
background-jobsปกติใครก็ได้ที่สนใจ

เมื่อรอบเวร on-call เปลี่ยน คนที่ออกเวรก็เลิกติดตามช่องวิกฤต แล้วคนที่เข้าเวรก็กดติดตาม เท่านี้ก็ส่งต่อเวรครบแล้ว ไม่ต้องมีไฟล์คอนฟิก ไม่ต้องมีหน้าจอผู้ดูแลระบบ

ช่องของ Echobell แชร์ผ่านลิงก์ได้ การกดติดตามจึงใช้เวลาราว 10 วินาทีต่อสมาชิกใหม่หนึ่งคน

บททดสอบสัญญาณต่อสัญญาณรบกวน

ก่อนเพิ่มการแจ้งเตือนใหม่ทุกครั้ง ให้ถามคำถามเดียว: ถ้ามันดังขึ้นตอนตีสามของวันศุกร์ ฉันจะทำอะไรกับมันจริง ๆ

  • "ตื่นขึ้นมาแก้เดี๋ยวนี้" → โทรเข้า
  • "จัดการเป็นเรื่องแรกตอนเช้า" → สำคัญตามเวลา หรือปกติ
  • "คงไม่ทำอะไร นอนต่อ" → ให้ทบทวนว่าการแจ้งเตือนนี้ควรมีอยู่หรือไม่

ระบบมอนิเตอร์ส่วนใหญ่มีของอยู่ในกลุ่มแรกมากเกินไป และในกลุ่มที่สองน้อยเกินไป คำตอบที่ถูกต้องสำหรับอีเวนต์ส่วนใหญ่คือ "ค่อยจัดการพรุ่งนี้" และการแจ้งเตือนแบบสำคัญตามเวลาที่ขึ้นบนหน้าจอล็อกโดยไม่ส่งเสียงเรียกเข้า ก็คือเครื่องมือที่เหมาะกับกรณีนั้นพอดี

เปลี่ยนทีละอย่าง

ถ้าระบบปัจจุบันของคุณกำลังสร้างอาการล้าจากการแจ้งเตือน ทางแก้ที่เร็วที่สุดไม่ใช่การรื้อใหม่ทั้งหมด ให้เลือกแหล่งที่ส่งเสียงดังที่สุด ซึ่งน่าจะเป็นอีเมลจาก Sentry หรือห้อง Slack ที่ทุกคนปิดเสียงไปแล้ว จากนั้นจัดหมวดอีเวนต์แต่ละประเภทว่าเป็นโทรเข้า สำคัญตามเวลา หรือปกติ

หนึ่งแหล่ง หนึ่งสัปดาห์ แล้วดูว่าสัญญาณรบกวนลดลงโดยไม่เสียสัญญาณจริงไปไหม

จากนั้นค่อยทำแหล่งถัดไป

ระบบแจ้งเตือนที่จูนมาดีเป็นหนึ่งในสิ่งที่ค่อย ๆ ทำให้ชีวิตการทำงานประจำวันของคุณดีขึ้นอย่างเงียบ ๆ โดยไม่ต้องมีดราม่า คุณจะเลิกกลัวมือถือของตัวเอง และเริ่มเชื่อได้ว่าถ้ามันดังขึ้นมา มันสำคัญจริง


เนื้อหาที่เกี่ยวข้อง

บทความที่เกี่ยวข้อง

แจ้งเตือนโทรเข้าจาก CloudWatch ไม่ต้องใช้ Lambda หรือ Twilio

SNS โทรเข้าโทรศัพท์ไม่ได้ ส่งต่อ CloudWatch alarm ผ่าน EventBridge ไปยังเว็บฮุกแบบโทรเข้า พร้อมวิธีตั้งค่า ตัวกรอง และข้อควรระวัง

อ่านต่อ

แจ้งเตือนแบบโทรเข้าจาก Sentry สำหรับข้อผิดพลาดร้ายแรง

Sentry ไม่มีแอ็กชันโทรออก ส่ง issue alert ผ่านเว็บฮุกเพื่อให้โทรศัพท์ดังเฉพาะข้อผิดพลาดที่ทำให้โปรดักชันพัง พร้อมวิธีตั้งค่า โครงสร้างข้อมูล และการกรอง

อ่านต่อ

Prometheus Alertmanager โทรแจ้งเตือน: ปลุกเฉพาะเรื่องวิกฤต

Alertmanager ไม่มี receiver แบบเสียง มาดูวิธีส่ง alert ของ Prometheus ไปเป็นสายโทรเข้าเฉพาะระดับวิกฤต ทั้งการตั้งค่า webhook เงื่อนไข และกับดักของ Watchdog

อ่านต่อ