أسس قابلية الرصد

لوحات المراقبة الفعّالة

18 دقيقة الدرس 7 من 28

لوحات المراقبة الفعّالة

لوحة المراقبة هي حوار بين نظامك والمهندس المناوب. عندما يمتلئ هذا الحوار بالضوضاء — عدادات ترتفع دائماً، مقاييس تبدو طبيعية حتى أثناء الأعطال، رسوم بيانية لم ينظر إليها أحد منذ ستة أشهر — تتوقف عن كونها مفيدة وتبدأ في خلق ثقة زائفة. المهندسون في Google وNetflix وStripe لا يقيسون جودة لوحات المراقبة بعدد اللوحات. يقيسونها بمدى السرعة التي تمكّن بها اللوحة من الانتقال من "هناك خطأ ما" إلى "أعرف ماذا وأين". هذا هو المقياس الوحيد الذي يهم.

هذا الدرس يتناول قرارات التصميم التي تُميز لوحات المراقبة التي تُسرّع الاستجابة للحوادث عن تلك التي تجعل المهندسين يتجاوزونها إلى واجهة الاستعلام المباشرة.

التدرج من النظرة العامة إلى التفاصيل

يجب أن يمتلك كل نظام إنتاج مستويين على الأقل من لوحات المراقبة: نظرة عامة (صحة الخدمة للوهلة الأولى) وتفصيلية (غوص عميق لكل مكون). التسلسل الهرمي المُصمَّم جيداً يعني أنك لن تحتاج أبداً إلى النظر في لوحة تفصيلية عندما يكون كل شيء على ما يرام، وستعرف دائماً أي لوحة تفصيلية تفتح عند حدوث خطأ.

Dashboard hierarchy: Overview to Detail Tier 0 — Service Overview SLO burn rates · Error % · p99 latency · Saturation drill down Tier 1 — API Service RPS · Latency · DB pool · Cache hit Tier 1 — Worker Service Queue depth · Job duration · Error rate Tier 1 — Data Layer Query latency · Connections · Replication lag Tier 2 — Pod / Node CPU throttling · OOM events · Restarts Tier 2 — Queue Detail Per-queue depth · DLQ size · Throughput Tier 2 — DB Internals Slow queries · Lock waits · Index usage قاعدة التنقل: ينطلق التنبيه ← افتح Tier 0 لتأكيد الأضرار ← افتح Tier 1 للخدمة المتأثرة ← Tier 2 لتحليل السبب الجذري. لا يجب أن تحتاج أي لوحة في Tier 0 إلى وسيلة إيضاح لتفسيرها — إن احتجت، فهي تنتمي إلى Tier 1 أو Tier 2.
التسلسل الهرمي لثلاثة مستويات من لوحات المراقبة: Tier 0 يعرض صحة مؤشرات SLO للنظام كاملاً؛ Tier 1 يعرض تفاصيل كل خدمة؛ Tier 2 يتعمق في البنية التحتية والمقاييس الداخلية لتحليل السبب الجذري.
لوحة Tier 0 يجب أن تجيب على سؤال واحد في أقل من خمس ثوانٍ: هل تجربة المستخدم مقبولة الآن؟ إن احتاج المهندس المناوب إلى التفكير، فاللوحة معقدة للغاية. يجب أن تمتلك كل لوحة في Tier 0 عتبة مرئية — منطقة حمراء/صفراء/خضراء — حتى يكون العبء المعرفي لتحديد "جيد أم سيئ؟" صفراً.

تجنب الرسوم البيانية الوهمية

الرسم البياني الوهمي هو أي لوحة تجعل النظام يبدو نشطاً دون مساعدتك على اتخاذ قرار. أكثر المخالفين شيوعاً في لوحات الإنتاج:

  • إجمالي الطلبات على الإطلاق: عداد يرتفع فقط. يُثبت أن الخدمة تعمل. لا يتغير شكله أبداً أثناء الحادث.
  • استخدام CPU دون سياق: نسبة 60% CPU إما جيدة (يوجد هامش) أو مرعبة (أنت عند الحد الذي سيبدأ المُجدوِل بعده في التقليص). دون عرض معدل الطلبات جانباً، فهي بلا معنى.
  • زمن الاستجابة p50 فقط: الوسيط يخفي الـ tail latency. أبطأ 1% من مستخدميك — الأكثر احتمالاً للانسحاب — غير مرئيين. دائماً اعرض p95 وp99 أو p99.9 إلى جانب p50.
  • تعليقات "نُشر منذ 14 ساعة": الفوضى دون نافذة SLO واضحة تجعل علامات النشر عديمة الفائدة. لا تُضف تعليقات النشر إلا عندما تكون داخل نافذة استهلاك ميزانية الخطأ الحالية.
  • رسوم بيانية لا يملكها أحد: إن لم يستطع أي فرد في الفريق شرح سبب وجود اللوحة وما الإجراء الذي تستدعيه، احذفها. يجب أن تكون لوحات المراقبة مملوكة ومُراجعة ربع سنوياً.

ما تحتاجه كل لوحة خدمة

منهجا USE (الاستخدام والتشبع والأخطاء) وRED (المعدل والأخطاء والمدة) من Google يمنحانك الحد الأدنى من اللوحات اللازمة لأي خدمة. في الممارسة، ادمجهما:

  • Rate (المعدل): طلبات/ثانية، وظائف/ثانية — الإنتاجية. استخدم معدلاً لا عداداً.
  • Errors (الأخطاء): نسبة الأخطاء (لا عدد). sum(rate(http_requests_total{status=~"5.."}[5m])) / sum(rate(http_requests_total[5m])) — خط مستقيم عند 0% أثناء التشغيل العادي يصبح واضحاً فوراً عند الارتفاع.
  • Duration (المدة): p50 وp95 وp99 للكمون في لوحة واحدة بألوان مختلفة.
  • Saturation (التشبع): كم تقترب من الحد؟ معدل تقليص CPU، حجم عمل الذاكرة مقابل الحد، استخدام مجموعة الاتصالات، عمق قائمة الانتظار مقابل الطاقة الاستيعابية.
  • معدل استهلاك ميزانية SLO: هل معدل الخطأ الحالي يستهلك ميزانية الخطأ بشكل أسرع من تجددها؟ هذا هو أكثر رقم واحد قابل للتنفيذ على أي لوحة خدمة.

بناء لوحة Grafana بالكود

اللوحات المُعرَّفة في الواجهة والمحفوظة بالنقر على "حفظ" فخٌّ: إنها موجودة فقط في قاعدة بيانات Grafana، ولا يمكن مراجعتها كوداً، وتضيع عند استعادة قاعدة البيانات. يجب أن تُدار كل لوحة إنتاج ككود — مخزنة في Git، مطبّقة عبر CI، وخاضعة للتحكم في الإصدار.

النهجان الرئيسيان هما نموذج JSON الخاص بـ Grafana المُخزَّن مباشرةً، أو مكتبة Grafonnet Jsonnet التي تُولّد JSON. للفرق التي تستخدم Terraform بالفعل، يُدير موفر grafana في Terraform اللوحات كموارد HCL. إليك نهج Terraform لمجموعة لوحات نظرة عامة بسيطة للخدمة:

# dashboards.tf resource "grafana_dashboard" "api_service_overview" { folder = grafana_folder.observability.id config_json = jsonencode({ title = "API Service — Overview" uid = "api-svc-overview" refresh = "30s" time = { from = "now-1h", to = "now" } templating = { list = [ { name = "env" type = "custom" options = [ { value = "prod", text = "Production" }, { value = "staging", text = "Staging" } ] current = { value = "prod" } } ] } panels = [ { title = "Request Rate (RPS)" type = "timeseries" gridPos = { x = 0, y = 0, w = 8, h = 8 } targets = [{ expr = "sum(rate(http_requests_total{env=\"$env\"}[5m])) by (route)" legendFormat = "{{route}}" }] fieldConfig = { defaults = { unit = "reqps" color = { mode = "palette-classic" } } } }, { title = "Error Rate (%)" type = "timeseries" gridPos = { x = 8, y = 0, w = 8, h = 8 } targets = [{ expr = "100 * sum(rate(http_requests_total{env=\"$env\",status=~\"5..\"}[5m])) / sum(rate(http_requests_total{env=\"$env\"}[5m]))" legendFormat = "error %" }] fieldConfig = { defaults = { unit = "percent" thresholds = { mode = "absolute" steps = [ { color = "green", value = null }, { color = "yellow", value = 0.1 }, { color = "red", value = 1 } ] } } } }, { title = "Latency — p50 / p95 / p99" type = "timeseries" gridPos = { x = 16, y = 0, w = 8, h = 8 } targets = [ { expr = "histogram_quantile(0.50, sum(rate(http_request_duration_seconds_bucket{env=\"$env\"}[5m])) by (le))" legendFormat = "p50" }, { expr = "histogram_quantile(0.95, sum(rate(http_request_duration_seconds_bucket{env=\"$env\"}[5m])) by (le))" legendFormat = "p95" }, { expr = "histogram_quantile(0.99, sum(rate(http_request_duration_seconds_bucket{env=\"$env\"}[5m])) by (le))" legendFormat = "p99" } ] fieldConfig = { defaults = { unit = "s" } } } ] }) }
استخدم دائماً متغير قالب للبيئة. لوحة بها متغير $env كقائمة منسدلة يمكن استخدامها للاستجابة للحوادث في كل من staging والإنتاج دون الحاجة إلى نسختين منفصلتين. أضف متغيراً ثانياً لـ $cluster إن كنت تشغّل عدة كلاسترات Kubernetes. تُقيّم Grafana هذه المتغيرات عند وقت الاستعلام — لا تكرار في نموذج JSON.

ربط اللوحات ببعضها

التسلسل الهرمي للوحات لا يعمل إلا إذا كانت الروابط بين المستويات صريحة. في Grafana، تتيح لك روابط البيانات وروابط اللوحات النقر على ارتفاع في مقياس والقفز مباشرةً إلى لوحة التفاصيل مع نفس النافذة الزمنية ونفس فلتر البيئة مُعبّأ مسبقاً. قم بإعدادها في JSON اللوحة:

# داخل تعريف لوحة (Grafana JSON) "links": [ { "title": "Open pod detail for this service", "url": "/d/pod-detail?var-env=${__field.labels.env}&var-service=${__field.labels.service}&from=${__from}&to=${__to}", "targetBlank": false } ] # في Terraform (موفر grafana >= 1.x يدعم dataLinks في fieldConfig) fieldConfig = { defaults = { links = [ { title = "Open DB Detail Dashboard" url = "/d/db-detail?var-env=$${__field.labels.env}&from=$${__from}&to=$${__to}" } ] } }

متغيرات النطاق الزمني ${__from} و${__to} بالغة الأهمية — فهي تحافظ على النافذة الزمنية للحادث بينما تتعمق في التفاصيل. بدونها، تفتح لوحة التفاصيل على "آخر ساعة" وتفقد السياق حول متى حدث الشذوذ بالضبط.

أنماط الفشل الشائعة في لوحات الإنتاج

  • معدل تحديث اللوحة مرتفع جداً: تحديث كل 5 ثوانٍ لـ 20 لوحة باستعلامات Prometheus مكثفة يمكنه بذاته التسبب في عاصفة استعلامات، مما يُضعف قاعدة بيانات المقاييس أثناء الحادث الذي تحاول تشخيصه. افترض تحديثاً كل 30 ثانية؛ استخدم 10 ثوانٍ فقط على Tier 0 أثناء الحوادث النشطة.
  • غياب تعريف "الطبيعي": دون نطاق مرجعي يُظهر نفس الفترة من الأسبوع الماضي، لا يستطيع المهندسون تحديد ما إذا كان الارتفاع الحالي غير عادي أم مجرد حركة مرور يوم الثلاثاء. أضف استعلام offset 1w على كل لوحة RPS وكمون كخط مرجعي خافت في الخلفية.
  • البيانات المفقودة مقابل الصفر: لوحة تُظهر صفراً ثابتاً يمكن أن تعني "كل شيء هادئ" أو "توقف إرسال المقاييس." استخدم absent() أو لوحة "آخر وقت استخراج" منفصلة للتمييز بين الصمت والفشل.
  • تعفن اللوحات: تُعاد تسمية الخدمات، وتتغير المقاييس، وتعود اللوحات القديمة صامتةً بـ "No data." قس لوحاتك: نفّذ دورياً استعلاماً على Grafana API لاكتشاف اللوحات ذات نقاط البيانات الصفرية وأرسل تنبيهاً بشأنها.
لا تنشئ تنبيهاً مباشرةً من لوحة في الواجهة. التنبيهات المُعرَّفة عبر محرر لوحة Grafana مُخزَّنة في Grafana لا في إعدادات التنبيه ككود. عند استعادة قاعدة بيانات Grafana أو الترحيل إلى مثيل جديد، تختفي تلك التنبيهات. عرّف جميع التنبيهات في ملفات YAML لقواعد تنبيه Prometheus أو Alertmanager، خزّنها في Git، ودع لوحة التوضيح تكون للتصور فقط — مع رابط للقاعدة في التعليق.

قائمة مراجعة اللوحات

قبل دمج لوحة في الإنتاج، يجب أن تجتاز كل لوحة هذه المراجعة:

  1. قابلة للتنفيذ: إذا أظهرت هذه اللوحة قيمة سيئة، ماذا أفعل؟ إن لم تستطع الإجابة، احذف اللوحة.
  2. تجميع صحيح: هل تستخدم rate() للعدادات؟ histogram_quantile() للكمون؟ هل تجمع بدلاً من المتوسط عبر البودات؟
  3. عتبة مُعيَّنة: هل تمتلك اللوحة عتبات خضراء/صفراء/حمراء، أم تتطلب من المهندس تذكّر ما يبدو عليه "الطبيعي"؟
  4. موسومة: هل سيفهم مهندس جديد في أول مناوبة طارئة له ما تعرضه هذه اللوحة دون السؤال؟ يجب أن تكون وحدات محور Y ومنسق وسيلة الإيضاح والعنوان صريحين.
  5. مرتبطة: هل يأخذك النقر على ارتفاع إلى لوحة التفاصيل ذات الصلة أو استعلام السجلات؟

اللوحات التي تجتاز هذه القائمة تصبح معرفة مؤسسية. إنها تُدوّن الفهم الجماعي للفريق لكيفية سلوك النظام، وتجعل كل حادث مستقبلي أسرع في الحل. هذا هو المعيار الذي يجب السعي إليه.