اگر هر شب چند هشدار میگیرید که نیازی به اقدام ندارند، خیلی زود هشدارها را نادیده میگیرید؛ و آن شبی که هشدار واقعی بیاید، همان بیتوجهی مشکلساز میشود. این پدیده «خستگی از هشدار» (alert fatigue) است و راه حل آن طراحی هشدار، نه اضافهکردن ابزار است. این راهنما میگوید چه چیزی را مانیتور کنید (چهار سیگنال طلایی)، چه زمانی هشدار بدهید (علامتها نه علتها)، و چگونه با SLO و error budget هشدارهایی بنویسید که واقعاً ارزش بیدارکردن کسی را دارند.
چهار سیگنال طلایی
کتاب SRE گوگل چهار معیار را برای هر سرویس کاربرمحور پیشنهاد میکند. اگر فقط چهار چیز را مانیتور کنید، همینها باشد:
| سیگنال | پرسش | مثال متریک |
|---|---|---|
| Latency (تأخیر) | پاسخدادن چقدر طول میکشد؟ (موفق و ناموفق جدا) | histogram_quantile(0.95, …) |
| Traffic (ترافیک) | چقدر درخواست میرسد؟ | rate(http_requests_total[5m]) |
| Errors (خطا) | چه درصدی شکست میخورد؟ | rate(…{status=~"5.."}) / rate(…) |
| Saturation (اشباع) | سیستم چقدر به سقف ظرفیت نزدیک است؟ | دیسک، حافظه، طول صف، pool اتصال |
RED و USE: دو قالب سادهتر
- RED (برای سرویسها): Rate، Errors، Duration؛ همان سه سیگنال اول.
- USE (برای منابع مثل CPU، دیسک، شبکه): Utilization، Saturation، Errors.
با این دو قالب میتوانید برای هر مؤلفهی جدید، بدون فکر زیاد، فهرست متریکهای ضروری را بسازید. نحوهی نوشتن کوئریهای آنها در راهنمای PromQL آمده است.
هشدار روی علامت، نه علت
یک اصل ساده، اغلب فرق هشدار خوب و بد را میسازد: برای چیزی که کاربر حس میکند هشدار (page) بدهید، نه برای چیزی که ممکن است باعث آن شود.
| علامت (هشدار بدهید) | علت احتمالی (داشبورد/تیکت) |
|---|---|
| درصد خطای ۵xx بالا | CPU بالا، حافظه کم، ریاستارت پادها |
| تأخیر p95 از هدف بیشتر شد | کندی دیتابیس، پر بودن صف |
سرویس در دسترس نیست (up == 0) | کرش، OOM، قطع شبکه |
| دیسک تا ۴ ساعت دیگر پر میشود | رشد لاگ یا داده |
مصرف CPU یا حافظهی بالا، اگر کاربر را متأثر نکند، مشکل نیست. هشدار روی علت یعنی صدها هشدار که فقط بعضیشان به مشکل واقعی میرسند. استثنا، اشباعهایی است که قطعی را حتمی کردهاند (دیسک در حال پر شدن، گواهی در حال انقضا) و علامت هنوز ظاهر نشده است.
SLI، SLO و error budget
- SLI (شاخص): یک اندازهگیری، مثل «نسبت درخواستهای موفق به کل» یا «نسبت درخواستهای سریعتر از ۳۰۰ms».
- SLO (هدف): مقدار هدف آن شاخص در یک بازه، مثل «۹۹٫۹٪ درخواستها در ۳۰ روز موفق باشند».
- Error budget (بودجهی خطا): تفاوت ۱۰۰٪ و SLO؛ مقدار خطایی که مجاز است.
بودجهی خطا برای یک بازهی ۳۰روزه:
| SLO | خطای مجاز | معادل قطعی در ۳۰ روز |
|---|---|---|
| 99% | 1% | ≈ 7.2 ساعت |
| 99.9% | 0.1% | ≈ 43.2 دقیقه |
| 99.95% | 0.05% | ≈ 21.6 دقیقه |
| 99.99% | 0.01% | ≈ 4.3 دقیقه |
SLO را بر اساس نیاز واقعی کاربر انتخاب کنید، نه بالاترین عدد ممکن: هر «نُه» اضافه هزینهی مهندسی را چند برابر میکند. وقتی بودجه باقی است، تیم میتواند ریسک کند و ویژگی تازه منتشر کند؛ وقتی تمام شد، تمرکز به پایداری برمیگردد.
هشدار بر پایهی نرخ مصرف بودجه (burn rate)
هشدار «درصد خطا از ۰٫۱٪ بیشتر شد» دو مشکل دارد: برای یک اوج کوتاه بیاهمیت هم هشدار میدهد و برای یک خطای کوچک ولی طولانی هیچوقت. روش بهتر، burn rate است: بودجه با چه سرعتی نسبت به حالت عادی مصرف میشود. burn rate برابر ۱ یعنی بودجه دقیقاً در پایان بازه تمام میشود، و ۱۴٫۴ یعنی در یک ساعت ۲٪ از بودجهی ماهانه مصرف میشود.
الگوی توصیهشده در کتاب SRE Workbook گوگل، ترکیب چند پنجره است: یک پنجرهی بلند برای اطمینان از جدیبودن، و یک پنجرهی کوتاه برای اینکه هشدار بعد از برطرفشدن مشکل خاموش شود:
| واکنش | burn rate | پنجرهی بلند | پنجرهی کوتاه |
|---|---|---|---|
| Page (فوری) | 14.4 | 1h | 5m |
| Page | 6 | 6h | 30m |
| Ticket (غیرفوری) | 1 | 3d | 6h |
نمونهی قاعدهی Prometheus برای SLO برابر ۹۹٫۹٪ (بودجه ۰٫۰۰۱):
groups:
- name: slo-api
rules:
- alert: ErrorBudgetBurnFast
expr: |
(
sum(rate(http_requests_total{job="api",status=~"5.."}[1h]))
/ sum(rate(http_requests_total{job="api"}[1h])) > (14.4 * 0.001)
)
and
(
sum(rate(http_requests_total{job="api",status=~"5.."}[5m]))
/ sum(rate(http_requests_total{job="api"}[5m])) > (14.4 * 0.001)
)
labels:
severity: page
annotations:
summary: "API is burning error budget 14x faster than allowed"
runbook_url: "https://wiki.example.com/runbooks/api-errors"
YAMLYAML Validatorنحو فایل قواعد هشدار را پیش از بارگذاری در Prometheus بررسی کنید.برای سرویسهای پرترافیک، محاسبهی نسبتها با recording ruleها (مثلاً job:http_error_ratio:rate5m) هم کوئریها را سبک میکند و هم خوانایی قواعد را بالا میبرد.
ویژگیهای یک هشدار خوب
- قابلاقدام: کسی که هشدار را میگیرد باید بداند چه کاری انجام دهد. اگر پاسخ «هیچ» است، آن هشدار نباید وجود داشته باشد.
- فوری: فقط چیزهایی که باید همین الان رسیدگی شوند بیدارکننده باشند. بقیه تیکت یا اعلان کاری در ساعت اداری.
- قابلفهم: عنوان بگوید چه خراب است و روی کدام سرویس؛ و لینک runbook (قدمهای بررسی و رفع) و داشبورد مرتبط داشته باشد.
- بدون نوسان: از
forاستفاده کنید تا فقط مشکل پایدار هشدار بدهد. - غیرتکراری: یک مشکل یک هشدار؛ نه دهها هشدار همزمان برای یک علت.
- آزمایششده: هشدار را حداقل یکبار عمداً فعال کنید تا مطمئن شوید به مقصد میرسد.
annotations:
summary: "{{ $labels.instance }}: disk will be full in 4 hours"
description: "Free space is {{ $value | humanizePercentage }} and shrinking."
runbook_url: "https://wiki.example.com/runbooks/disk-full"
dashboard: "https://grafana.example.com/d/nodes?var-instance={{ $labels.instance }}"
مسیریابی در Alertmanager: گروهبندی، سرکوب و سکوت
Alertmanager فقط ارسالکنندهی هشدار نیست؛ سه ابزار ضدنویز دارد:
- گروهبندی (group_by): ده هشدار مرتبط را در یک پیام میفرستد.
- سرکوب (inhibit_rules): وقتی هشدار علتریشهای فعال است، هشدارهای ناشی از آن را خاموش میکند (مثلاً اگر یک نود پایین است، هشدار تکتک پادهای روی آن لازم نیست).
- سکوت (silence): برای نگهداری برنامهریزیشده، هشدارها را موقتاً بیصدا میکند.
route:
receiver: ticket
group_by: [alertname, service]
routes:
- matchers: [severity="page"]
receiver: oncall
repeat_interval: 1h
- matchers: [severity="warning"]
receiver: ticket
repeat_interval: 12h
inhibit_rules:
- source_matchers: [severity="page"]
target_matchers: [severity="warning"]
equal: [service] # اگر page برای همان سرویس فعال است، warning را ساکت کن
receivers:
- name: oncall
webhook_configs: [{ url: "http://notifier.internal:8080/page" }]
- name: ticket
webhook_configs: [{ url: "http://notifier.internal:8080/ticket" }]
برای مسیریابی با regex (مثلاً match_re روی نام سرویس)، الگو را قبل از استفاده امتحان کنید؛ Alertmanager هم مثل Prometheus از RE2 و تطابق کامل استفاده میکند.
هشدارها را دورهای بازبینی کنید
هشدارها مثل کد، با زمان فرسوده میشوند. ماهی یکبار، هشدارهای ارسالشده را مرور کنید و برای هرکدام بپرسید:
- چند بار فعال شد و چند بار منجر به اقدام شد؟ هشدار با نسبت اقدام پایین را حذف یا کمحساس کنید.
- آیا مشکلهای واقعی را بدون هشدار از دست دادیم؟ پساز هر حادثه بپرسید «کدام هشدار باید میگفت؟».
- runbook هنوز درست است؟
در ثبت زمان حوادث و تطبیق آن با زمان هشدار، تبدیل timestamp بین UTC و زمان محلی خطای رایجی است:
TSUnix Timestamp Converterیک زمان را بین UTC و زمان محلی تبدیل کنید تا خط زمانی حادثه با هشدارها همخوان شود.چکلیست شروع
- برای هر سرویس مهم، چهار سیگنال طلایی را در داشبورد داشته باشید.
- یک SLO ساده (مثلاً دسترسپذیری ۹۹٫۹٪) تعریف کنید و SLI آن را با PromQL بنویسید.
- دو هشدار burn-rate (سریع و کند) و چند هشدار اشباع (دیسک، گواهی) بنویسید؛ نه بیشتر.
- برای هر هشدار runbook و لینک داشبورد بگذارید.
- سرکوب و گروهبندی را در Alertmanager فعال کنید.
- ماهانه بازبینی کنید و هشدارهای بیاقدام را حذف کنید.
جمعبندی و مطالعهی بعدی
هشدار خوب کم، دقیق و قابلاقدام است: بر علامتهای کاربر (خطا و تأخیر) تکیه میکند، با SLO و burn rate میزان فوریت را مشخص میکند و با گروهبندی و سرکوب نویز را کم میکند. اگر هنوز استک مانیتورینگ ندارید، راهاندازی Prometheus، Grafana و Alertmanager را اجرا کنید و کوئریهای لازم را از راهنمای PromQL بردارید.