هشدار در Prometheus با Alertmanager: rules، routing و تلگرام

برای راه‌اندازی هشدار در Prometheus سه قطعه لازم است: فایل قاعده (Alerting Rule) که Prometheus آن را در بازه‌های منظم ارزیابی می‌کند، اتصال Prometheus به Alertmanager، و فایل alertmanager.yml که هشدارها را گروه‌بندی می‌کند و بر اساس برچسب severity به ایمیل یا تلگرام می‌فرستد. پیش از اینکه به هشدارها تکیه کنید، پیکربندی را با promtool و amtool بررسی کنید و یک هشدار آزمایشی بفرستید.

این راهنما فرض می‌کند Prometheus و node_exporter روی سرور نصب شده‌اند. اگر هنوز به این مرحله نرسیده‌اید، ابتدا آموزش نصب و کانفیگ سرور Prometheus را ببینید. در آن مطلب و در راهنمای کانفیگ Prometheus و Grafana فقط یک نمونه کوتاه از Alertmanager آمده است؛ تمرکز این مطلب روی خود هشداردهی است: نوشتن قاعده، مسیریابی، گیرنده‌ها، تست و خطاهای رایج. مثال‌ها بر اساس مستندات رسمی prometheus.io نوشته شده‌اند.

هشدار در Prometheus چگونه کار می‌کند؟

Prometheus خودش پیامی برای کسی نمی‌فرستد. در هر بازه ارزیابی، عبارت PromQL هر قاعده را اجرا می‌کند و اگر نتیجه‌ای برگردد، برای هر سری زمانی یک هشدار می‌سازد و آن را به Alertmanager تحویل می‌دهد. Alertmanager تصمیم می‌گیرد هشدارها با چه گروه‌بندی، به کدام گیرنده و هر چند وقت یک بار ارسال شوند و کدام‌یک موقتاً ساکت بمانند.

جزءوظیفهفایل یا پورت
node_exporterانتشار متریک‌های سیستم عامل مانند CPU، حافظه و دیسکپورت 9100
Prometheusجمع‌آوری متریک و ارزیابی Alerting Rulesprometheus.yml و فایل‌های rule_files
Alertmanagerگروه‌بندی، مسیریابی، Silence و ارسال اعلانalertmanager.yml، پورت 9093
گیرنده (Receiver)مقصد نهایی پیامایمیل، تلگرام یا Webhook

هر هشدار سه وضعیت دارد. در وضعیت inactive شرط برقرار نیست. در وضعیت pending شرط برقرار شده اما مدتی که با for تعیین کرده‌اید هنوز تمام نشده است. در وضعیت firing شرط به اندازه for ادامه داشته و هشدار برای Alertmanager ارسال می‌شود. وضعیت همه قاعده‌ها را در صفحه Alerts رابط وب Prometheus می‌بینید.

نوشتن Alerting Rules در Prometheus

قاعده‌ها را در فایلی جداگانه مانند ‎/etc/prometheus/rules/node-alerts.yml بنویسید تا فایل اصلی شلوغ نشود. نمونه زیر چهار هشدار پایه برای سرورهای لینوکس دارد: از دسترس خارج شدن سرور، کمبود فضای دیسک، کمبود حافظه و مصرف بالای CPU.

groups:
  - name: node-basic
    rules:
      - alert: InstanceDown
        expr: up == 0
        for: 2m
        labels:
          severity: critical
        annotations:
          summary: "Instance {{ $labels.instance }} is down"
          description: "Prometheus could not scrape {{ $labels.job }} for 2 minutes."

      - alert: DiskSpaceLow
        expr: (node_filesystem_avail_bytes{fstype=~"ext4|xfs"} / node_filesystem_size_bytes{fstype=~"ext4|xfs"}) * 100 < 10
        for: 10m
        labels:
          severity: warning
        annotations:
          summary: "Low disk space on {{ $labels.instance }} ({{ $labels.mountpoint }})"
          description: 'Only {{ printf "%.1f" $value }}% free.'

      - alert: MemoryPressure
        expr: (node_memory_MemAvailable_bytes / node_memory_MemTotal_bytes) * 100 < 10
        for: 10m
        labels:
          severity: warning
        annotations:
          summary: "Available memory below 10% on {{ $labels.instance }}"

      - alert: HighCpuUsage
        expr: 100 - (avg by (instance) (rate(node_cpu_seconds_total{mode="idle"}[5m])) * 100) > 90
        for: 15m
        labels:
          severity: warning
        annotations:
          summary: "CPU above 90% for 15 minutes on {{ $labels.instance }}"

نقش for و keep_firing_for

گزینه for جلوی هشدارهای لحظه‌ای را می‌گیرد؛ یک جهش سی‌ثانیه‌ای CPU نباید کسی را نیمه‌شب بیدار کند. اگر for را ننویسید، هشدار با اولین ارزیابی موفق firing می‌شود. برای InstanceDown مقدار کوتاه و برای دیسک و CPU مقدار طولانی‌تر انتخاب کنید، چون پر شدن دیسک تدریجی است و چند دقیقه تأخیر در اعلان آن مشکلی ایجاد نمی‌کند.

keep_firing_for کار برعکس را انجام می‌دهد: هشدار را پس از برطرف شدن شرط، مدتی در وضعیت firing نگه می‌دارد. این گزینه برای متریک‌هایی که دور مرز آستانه نوسان می‌کنند مفید است، چون جلوی ارسال پشت سر هم پیام «رفع شد» و «دوباره فعال شد» را می‌گیرد.

برچسب severity و annotations

برچسب‌های بخش labels به هشدار اضافه می‌شوند و Alertmanager مسیر ارسال را با همین برچسب‌ها انتخاب می‌کند. annotations فقط متن توضیحی هستند و در مسیریابی اثری ندارند. داخل annotations با {{ $labels.instance }} به برچسب‌های سری زمانی و با {{ $value }} به مقدار عددی عبارت دسترسی دارید.

در عمل دو سطح severity کافی است: critical برای مشکلی که همین حالا اقدام می‌خواهد و warning برای چیزی که می‌تواند تا ساعت کاری صبر کند. برای هر هشدار یک annotation به نام runbook با نشانی دستورالعمل رفع مشکل هم اضافه کنید تا کسی که پیام را می‌خواند بداند قدم اول چیست.

بررسی قاعده‌ها و اتصال Prometheus به Alertmanager

پیش از بارگذاری، نحو فایل را با promtool بررسی کنید. سپس مسیر فایل را در بخش rule_files و نشانی Alertmanager را در بخش alerting فایل prometheus.yml قرار دهید:

promtool check rules /etc/prometheus/rules/node-alerts.yml
promtool check config /etc/prometheus/prometheus.yml

# prometheus.yml
rule_files:
  - /etc/prometheus/rules/*.yml

alerting:
  alertmanagers:
    - static_configs:
        - targets:
            - localhost:9093

# Reload without a full restart
sudo kill -HUP $(pidof prometheus)
curl -X POST http://localhost:9090/-/reload    # only if started with --web.enable-lifecycle

نقطه پایانی ‎/-/reload به‌صورت پیش‌فرض غیرفعال است و فقط وقتی کار می‌کند که Prometheus با فلگ ‎–web.enable-lifecycle اجرا شده باشد؛ در غیر این صورت از سیگنال SIGHUP استفاده کنید. اگر چند Alertmanager به‌صورت خوشه دارید، مستندات Alertmanager توصیه می‌کند نشانی همه آن‌ها را در targets بنویسید و ترافیک بینشان را Load Balance نکنید.

پیکربندی Alertmanager: گروه‌بندی، routing و گیرنده‌ها

فایل alertmanager.yml سه بخش اصلی دارد: route که درخت مسیریابی است، receivers که مقصدها را تعریف می‌کند و inhibit_rules که هشدارهای وابسته را ساکت می‌کند. نمونه زیر هشدارهای critical را هم به تلگرام و هم به ایمیل می‌فرستد و بقیه هشدارها فقط به ایمیل می‌روند:

route:
  receiver: ops-email
  group_by: ['alertname', 'instance']
  group_wait: 30s
  group_interval: 5m
  repeat_interval: 4h
  routes:
    - matchers:
        - severity="critical"
      receiver: ops-telegram
      continue: true
    - matchers:
        - severity="critical"
      receiver: ops-email

receivers:
  - name: ops-email
    email_configs:
      - to: 'ops@example.com'
        from: 'alertmanager@example.com'
        smarthost: 'mail.example.com:587'
        auth_username: 'alertmanager@example.com'
        auth_password_file: '/etc/alertmanager/smtp_password'
        send_resolved: true

  - name: ops-telegram
    telegram_configs:
      - bot_token_file: '/etc/alertmanager/telegram_token'
        chat_id: -1001234567890
        parse_mode: 'HTML'

inhibit_rules:
  - source_matchers:
      - severity="critical"
    target_matchers:
      - severity="warning"
    equal: ['instance']

ترتیب routes مهم است. Alertmanager زیرمسیرها را از بالا به پایین بررسی می‌کند و با اولین تطابق متوقف می‌شود، مگر اینکه continue برابر true باشد. در نمونه بالا continue باعث می‌شود هشدار critical پس از تلگرام به مسیر دوم هم برسد و ایمیل آن هم ارسال شود. هشداری که با هیچ زیرمسیری تطابق نداشته باشد، به receiver ریشه یعنی ops-email می‌رود.

group_by، group_wait و repeat_interval

گزینهپیش‌فرضکاربرد
group_byدر نمونه: alertname و instanceهشدارهایی که این برچسب‌ها را مشترک دارند در یک پیام ارسال می‌شوند
group_wait30sمکث پیش از اولین پیام یک گروه تازه، تا هشدارهای مرتبط هم به همان پیام برسند
group_interval5mفاصله ارسال پیام بعدی برای گروهی که هشدار جدید به آن اضافه شده است
repeat_interval4hفاصله تکرار پیام برای هشداری که هنوز فعال است
continuefalseادامه بررسی زیرمسیرهای بعدی پس از اولین تطابق

فرض کنید ارتباط یک رک با بیست سرور قطع شود. اگر group_by فقط alertname باشد، به جای بیست پیام یک پیام با فهرست همه سرورها دریافت می‌کنید. اگر برعکس هر برچسب جزئی را در group_by بگذارید، گروه‌بندی عملاً بی‌اثر می‌شود.

ارسال هشدار Prometheus به ایمیل

در smarthost حتماً پورت را بنویسید. گزینه require_tls به‌صورت پیش‌فرض true است، پس سرور SMTP روی آن پورت باید STARTTLS را پشتیبانی کند. send_resolved برای ایمیل به‌صورت پیش‌فرض false است و اگر پیام «رفع شد» می‌خواهید، باید آن را true کنید. رمز SMTP را به جای متن فایل پیکربندی در فایلی جداگانه با دسترسی محدود نگه دارید:

sudo install -m 600 -o alertmanager -g alertmanager /dev/null /etc/alertmanager/smtp_password
sudo nano /etc/alertmanager/smtp_password    # paste the SMTP password, no trailing spaces

نام کاربر و گروه alertmanager به روش نصب شما بستگی دارد؛ همان کاربری را بنویسید که سرویس Alertmanager با آن اجرا می‌شود.

ارسال هشدار Prometheus به تلگرام

گیرنده داخلی telegram_configs از نسخه 0.24.0 به Alertmanager اضافه شده و به برنامه واسط نیازی ندارد. مراحل آماده‌سازی:

  1. در تلگرام با BotFather یک ربات بسازید و توکن آن را در فایل ‎/etc/alertmanager/telegram_token با دسترسی 600 ذخیره کنید.
  2. ربات را به گروه تیم فنی اضافه کنید و یک پیام در گروه بفرستید.
  3. با فراخوانی متد getUpdates در API تلگرام، مقدار chat.id را پیدا کنید. شناسه گروه‌ها عدد منفی است.
  4. chat_id را به‌صورت عدد و بدون کوتیشن در alertmanager.yml بنویسید؛ نوع این فیلد در مستندات int است.

برای گیرنده تلگرام، send_resolved به‌صورت پیش‌فرض true و parse_mode پیش‌فرض HTML است و متن پیام از قالب پیش‌فرض telegram.default.message ساخته می‌شود. Alertmanager برای ارسال باید به api.telegram.org دسترسی خروجی داشته باشد. اگر شبکه سرور مانیتورینگ این مقصد را باز نمی‌کند، تلگرام را تنها گیرنده هشدارهای critical نکنید و ایمیل یا یک Webhook به سامانه داخلی سازمان را کنار آن تعریف کنید.

ساکت کردن هشدارهای وابسته با inhibit_rules

قاعده inhibit در نمونه بالا می‌گوید اگر برای یک instance هشدار critical فعال است، هشدارهای warning همان instance ارسال نشوند. وقتی سرور کلاً از دسترس خارج شده، پیام‌های مربوط به CPU و دیسک همان سرور فقط شلوغی ایجاد می‌کنند. برچسب‌هایی که در equal می‌نویسید باید در هر دو هشدار وجود داشته باشند.

تست هشدار پیش از روز حادثه

هشداری که هیچ‌وقت تست نشده ممکن است روز حادثه به‌خاطر یک غلط تایپی در نام receiver ارسال نشود. این سه بررسی چند دقیقه وقت می‌گیرد و amtool همراه بسته Alertmanager نصب می‌شود:

# 1. Validate the configuration (no running Alertmanager needed)
amtool check-config /etc/alertmanager/alertmanager.yml

# 2. Which receiver gets an alert with these labels?
amtool config routes test --config.file=/etc/alertmanager/alertmanager.yml severity=critical

# 3. Send a test alert to the running Alertmanager
amtool alert add TestAlert severity=critical instance=test-01 \
  --annotation=summary='Test alert, please ignore' \
  --alertmanager.url=http://localhost:9093

خروجی دستور دوم نام receiver را نشان می‌دهد. اگر انتظار دارید هشدار critical به تلگرام برود و فقط ops-email را می‌بینید، ترتیب routes یا matchers را بررسی کنید. پس از دستور سوم، پیام باید بعد از group_wait برسد. اگر نرسید، لاگ سرویس را با journalctl -u alertmanager -f دنبال کنید؛ خطای اتصال به SMTP یا API تلگرام همان‌جا ثبت می‌شود.

Silence برای زمان نگهداری

پیش از ری‌استارت برنامه‌ریزی‌شده یا به‌روزرسانی کرنل، به جای حذف یا کامنت کردن قاعده، یک Silence موقت بسازید. Silence پس از پایان مدت خودش منقضی می‌شود و خطر فراموش کردن برگرداندن قاعده را ندارد:

amtool silence add instance="web01:9100" --duration=1h --comment="kernel update" \
  --alertmanager.url=http://localhost:9093
amtool silence query --alertmanager.url=http://localhost:9093

خطاهای رایج هشدار در Prometheus و Alertmanager

مشکلعلت محتملراه‌حل
هشدار در صفحه Alerts در وضعیت pending می‌ماندمتریک پیش از پایان for به حالت عادی برمی‌گرددexpr را در بخش Graph اجرا کنید، رفتار متریک را ببینید و for یا آستانه را اصلاح کنید
هشدار firing است اما پیامی نمی‌رسدبخش alerting در prometheus.yml تنظیم نشده یا route به receiver دیگری می‌رسدtargets را بررسی و amtool config routes test را اجرا کنید
خطای TLS هنگام ارسال ایمیلrequire_tls فعال است و سرور SMTP روی آن پورت STARTTLS نداردپورت و تنظیمات TLS سرور ایمیل را اصلاح کنید
خطای chat not found در لاگ تلگرامربات عضو گروه نیست یا chat_id اشتباه استربات را به گروه اضافه و شناسه را دوباره با getUpdates بخوانید
ده‌ها پیام تکراریgroup_by بیش از حد جزئی یا repeat_interval کوتاهبرچسب‌های group_by را کمتر و repeat_interval را بیشتر کنید
پیام «رفع شد» برای ایمیل نمی‌آیدsend_resolved در email_configs به‌صورت پیش‌فرض false استsend_resolved: true را اضافه کنید
تغییر قاعده اعمال نمی‌شودPrometheus پس از ویرایش فایل بارگذاری مجدد نشده استpromtool را اجرا و SIGHUP ارسال کنید

چه چیزهایی ارزش هشدار دارند؟

هر هشدار باید یک اقدام مشخص پشتش داشته باشد. اگر گیرنده پیام نمی‌داند باید چه کند یا عادت کرده آن را نادیده بگیرد، قاعده را حذف کنید یا متریک را فقط در داشبورد نشان دهید. چند اصل ساده برای شروع:

  • هشدار روی چیزی که کاربر حس می‌کند (در دسترس نبودن سرویس، خطای HTTP، تأخیر پاسخ) اولویت دارد بر متریک‌های داخلی مثل مصرف CPU.
  • برای دیسک، به جای درصد ثابت می‌توانید زمان پر شدن را پیش‌بینی کنید؛ عبارت predict_linear(node_filesystem_avail_bytes{fstype=~"ext4|xfs"}[6h], 24*3600) < 0 یعنی با روند شش ساعت اخیر، دیسک تا ۲۴ ساعت دیگر پر می‌شود.
  • برای critical حداقل دو کانال مستقل تعریف کنید تا قطع شدن یک کانال باعث از دست رفتن هشدار نشود.
  • هر چند ماه فهرست پیام‌های ارسال‌شده را مرور کنید و قاعده‌های پرسروصدا را اصلاح یا حذف کنید.

برای نمایش همین متریک‌ها در داشبورد، آموزش کانفیگ سرور Grafana را ببینید. اگر هنوز بین ابزارها مردد هستید، مقایسه Zabbix، Prometheus و Nagios تفاوت‌ها را نشان می‌دهد؛ در Zabbix هشداردهی داخل خود سرور Zabbix تعریف می‌شود و به Alertmanager نیازی نیست. طراحی، مستندسازی و بازبینی دوره‌ای هشدارها بخشی از خدمات مانیتورینگ زیرساخت و Observability کانفیگ سرور است.

سوالات متداول

آیا بدون Alertmanager می‌توان از Prometheus هشدار گرفت؟

Prometheus قاعده‌ها را ارزیابی می‌کند و وضعیتشان را در صفحه Alerts نشان می‌دهد، اما ارسال ایمیل یا پیام تلگرام، گروه‌بندی و Silence کار Alertmanager است. Grafana هم سامانه هشدار مستقلی دارد که می‌تواند روی داده Prometheus کار کند.

Alertmanager باید روی همان سرور Prometheus نصب شود؟

الزامی نیست و برای محیط کوچک نصب روی همان سرور کافی است. اما اگر همان سرور از دسترس خارج شود، هیچ هشداری ارسال نمی‌شود. برای این حالت یک بررسی ساده از بیرون، روی سرور یا شبکه‌ای دیگر، در نظر بگیرید.

چرا یک هشدار پشت سر هم فعال و رفع می‌شود؟

متریک دور مرز آستانه نوسان می‌کند. for را طولانی‌تر کنید، از میانگین در بازه زمانی (مثلاً rate روی ۵ دقیقه) استفاده کنید یا با keep_firing_for هشدار را پس از رفع شرط مدتی فعال نگه دارید.

فایل‌های rule را کجا نگه داریم؟

فایل‌های rule و alertmanager.yml را در یک مخزن Git نگه دارید و پیش از اعمال، promtool و amtool check-config را اجرا کنید. به این ترتیب تاریخچه تغییرات هشدارها مشخص است و خطای نحوی به سرور نمی‌رسد.

پاسخی بگذارید

نشانی ایمیل شما منتشر نخواهد شد. بخش‌های موردنیاز علامت‌گذاری شده‌اند *