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

تكلفة المراقبة وإدارة البيانات

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

تكلفة المراقبة وإدارة البيانات

على نطاق Google، تكلّف البنية التحتية للمراقبة أكثر من الميزانية الهندسية الكاملة لكثير من الشركات. حتى في الشركات المتوسطة التي تستخدم منصات مراقبة مُدارة — Datadog وNew Relic وHoneycomb — تتلقى الفرق بانتظام فواتير تتراوح بين 500 ألف دولار و2 مليون دولار سنوياً، ولا تكتشف الأرقام إلا بعد أن تنفجر. إن فهم سبب تكلفة بيانات القياس عالياً والأدوات التي يمكنك استخدامها للسيطرة على التكاليف دون التضحية بجودة الإشارة هو انضباط هندسي بالدرجة الأولى، لا مجرد شأن مالي.

يتناول هذا الدرس الآليات الثلاث التي تهيمن على تكاليف المراقبة: البعد الأساسي (عدد سلاسل المقاييس الزمنية الفريدة)، والأخذ بالعينات (الاحتفاظ بنسبة مفيدة إحصائياً من التتبعات والسجلات)، ومستويات الاستبقاء (تخزين البيانات بمفاضلات مختلفة بين التكلفة والدقة بمرور الوقت). أتقن هذه المحاور الثلاثة وستتمكن من تخفيض الفواتير بنسبة 60–80% مع إبقاء كل التنبيهات ولوحات المعلومات وتحقيقات الحوادث تعمل بشكل طبيعي.

انفجارات البعد الأساسي

يُعرَّف كل مقياس في Prometheus باسمه مع مجموعة من أزواج مفتاح-قيمة للتسميات. إجمالي تركيبات التسميات المتميزة هو البعد الأساسي للمقياس. يخزّن Prometheus كل تركيبة فريدة كسلسلة زمنية منفصلة، تتطلب كل منها أجزاء رأس في الذاكرة وكتل على القرص. البعد الأساسي هو المحرك الأكبر للتكلفة في أنظمة المقاييس.

قرار بسيط للتسمية يتضاعف بسرعة. مقياس بتسميات region (5) وhttp_method (6) وstatus_code (12) وroute (200 مسار موحّد) ينتج 5 × 6 × 12 × 200 = 72,000 سلسلة. أضف تسمية بـ 50 قيمة وستصل إلى 3.6 مليون سلسلة. أضف user_id بـ 10 ملايين قيمة وسيستنفد Prometheus ذاكرة الوصول العشوائي وينهار — محتجباً نظام التنبيه في اللحظة التي تحتاجه فيها أكثر ما يكون.

قنبلة البعد الأساسي: أكثر كوارث الإنتاج شيوعاً هي إضافة مطوّر لقيمة عالية البعد الأساسي كتسمية مقياس — user_id أو order_id أو request_id أو email أو عنوان IP أو أي UUID. كل كيان جديد يُنشئ سلسلة زمنية جديدة. بعد ارتفاع حركة المرور أو حملة تسجيل، يُوقف Prometheus نفسه بسبب نفاد الذاكرة خلال دقائق. الحل ثقافي وقائم على الأدوات: يجب أن تأتي قيم التسميات من مجموعة محدودة ومعروفة الحجم. القيم عالية البعد تنتمي إلى سمات امتدادات التتبع وحقول السجلات المنظمة — ليس في تسميات المقاييس أبداً.

يتطلب اكتشاف مشاكل البعد الأساسي قبل وصولها إلى الإنتاج مراقبة نشطة لخط أنابيب المقاييس نفسه. يكشف Prometheus عن prometheus_tsdb_head_series (عدد السلاسل النشطة الحالية) وprometheus_tsdb_head_series_created_total (معدل الإنشاء). نبّه عند نمو هذه الأرقام بشكل أسرع مما تبرره عدد خدماتك.

# قاعدة تسجيل Prometheus: تتبع البعد الأساسي لكل مهمة # prometheus/rules/cardinality.yml groups: - name: cardinality.rules interval: 5m rules: # عدد السلاسل لكل مهمة — نبّه إذا تجاوزت أي مهمة 50 ألف سلسلة - record: job:prometheus_tsdb_head_series:max expr: max by (job) (prometheus_tsdb_head_series) - alert: CardinalityExplosion expr: job:prometheus_tsdb_head_series:max > 50000 for: 10m labels: severity: warning annotations: summary: "Job {{ $labels.job }} لديه {{ $value }} سلسلة نشطة" runbook: "ابحث عن قيم تسميات غير محدودة — غالباً user_id أو request_id أو مسارات URL خام" # اعثر على أكبر مساهمي البعد الأساسي بـ PromQL: # topk(20, count by (__name__, job) ({__name__=~".+"}))

الحل لمشاكل البعد الأساسي الموجودة هو إعادة التسمية في طبقة السحب قبل دخول البيانات إلى TSDB، باستخدام metric_relabel_configs في Prometheus. يمكنك حذف مقاييس بأكملها أو تسميات محددة أو استبدال قيم التسميات بمجموعات موحّدة — كل ذلك دون لمس كود التطبيق.

# prometheus/prometheus.yml — إعادة التسمية للحد من البعد الأساسي عند السحب scrape_configs: - job_name: "checkout-service" static_configs: - targets: ["checkout:9090"] metric_relabel_configs: # حذف المقاييس عالية البعد التي لا تستعلم عنها - source_labels: [__name__] regex: "go_gc_duration_seconds|go_memstats_.*" action: drop # توحيد تسمية route — استبدال الأجزاء الرقمية بـ {id} - source_labels: [route] regex: ".*/([0-9]+)(/.*)?" replacement: "/{id}$2" target_label: route action: replace # حذف تسمية user_id كلياً — يجب ألا تكون تسمية مقياس أبداً - regex: "user_id" action: labeldrop # تجميع رموز الحالة في فئات: 200-299 -> 2xx - source_labels: [status_code] regex: "([2345])[0-9]{2}" replacement: "${1}xx" target_label: status_class - regex: "status_code" action: labeldrop
Cardinality explosion: bounded vs unbounded labels Bounded Labels (آمن) method: GET | POST | PUT | DELETE | PATCH 5 قيم — محدودة ومعروفة status_class: 2xx | 4xx | 5xx 3 قيم — مجمّعة في فئات route: /orders/{id} | /users/{id} ~50 قالب موحّد — محدود الإجمالي: 5 × 3 × 50 = 750 سلسلة Prometheus يتعامل معه بسهولة Unbounded Labels (خطر) user_id: 1, 2, 3, ... 10,000,000 10 ملايين قيمة — تنمو إلى الأبد request_id: UUID لكل طلب قيم فريدة لا حصر لها raw_url: /orders/12345/items/67 ملايين من المسارات الفريدة الإجمالي: 750 × 10M = 7.5 مليار سلسلة Prometheus ينهار في دقائق
التسميات المحدودة مقابل غير المحدودة — الفرق بين 750 سلسلة و7.5 مليار سلسلة من مقياس واحد.

الأخذ بالعينات: الاحتفاظ بما يهم

التتبعات الموزعة والسجلات مطوّلة بطبيعتها. خدمة تستقبل 1000 طلب في الثانية تنتج 86.4 مليون امتداد تتبع يومياً. تخزين كلها يكلّف مبالغ ضخمة مع عوائد متناقصة — الـ 999 استدعاء ناجحاً لـ GET /healthz لا تخبرك بشيء لم تخبرك به الأولى. الأخذ بالعينات هو الاحتفاظ بنسبة مفيدة إحصائياً مع التخلص من الأغلبية الزائدة.

ثمة ثلاث استراتيجيات للأخذ بالعينات، كل منها مناسبة عند نقطة مختلفة في الخط:

  • الأخذ بالعينات عند الرأس (Head-based) — يُتخذ القرار عند بداية الطلب قبل إنشاء أي امتدادات. بسيط، صفري التكلفة، لكنه أعمى: لا تعرف بعد إذا كان هذا التتبع سيكون مثيراً للاهتمام. مناسب للحركة عالية الحجم منخفضة القيمة (فحوص الصحة، سحب المقاييس).
  • الأخذ بالعينات عند الذيل (Tail-based) — يُؤجَّل القرار حتى اكتمال التتبع بأكمله. يُعازل المجمع الامتدادات ويُقيّم القواعد: احتفظ إذا كان أي امتداد يحمل خطأ، احتفظ إذا تجاوزت الزمن الكلي ثانيتين، احتفظ بـ 1% من الباقي. هذه هي الاستراتيجية الصحيحة لخدمات الإنتاج لأنها تضمن الاحتفاظ بـ 100% من الأخطاء والتتبعات البطيئة. التكلفة هي مخزن مؤقت (عادة 30–60 ثانية من الامتدادات في الذاكرة).
  • الأخذ بالعينات التكيفي (Adaptive) — يتعدّل معدل العينات تلقائياً بناءً على معدلات الأخطاء وتوزيعات زمن الاستجابة وحجم الحركة. هذا هو الحل الأمثل للخدمات الكبيرة لكنه يتطلب إعداداً أكثر.
احتفظ دائماً بـ 100% من الأخطاء. القاعدة الأساسية للأخذ بالعينات: لا تُبعد تتبع خطأ أبداً. خطأ واحد في مليون تجاهلته هو حادثة لا يمكنك التحقيق فيها. هيّئ مأخوذ عينات الذيل ليعتبر أي امتداد بـ error=true أو بحالة HTTP 5xx غير قابل للتفاوض — معدل استبقاء 100% لذلك التتبع بصرف النظر عن نسبة العينات الإجمالية.

في OTel Collector، يُنفَّذ الأخذ بالعينات عند الذيل بمعالج tailsampling. يحتفظ هذا الإعداد بجميع الأخطاء وجميع التتبعات البطيئة و5% من الحركة العادية:

# otel-collector/config.yaml — معالج الأخذ بالعينات عند الذيل processors: tail_sampling: # مدة الانتظار لجميع امتدادات التتبع قبل اتخاذ القرار decision_wait: 30s # أقصى عدد من التتبعات في الذاكرة — اضبطه على RPS × متوسط مدة التتبع num_traces: 100000 expected_new_traces_per_sec: 1000 policies: # السياسة 1: احتفظ دائماً بالتتبعات التي تحمل خطأ - name: keep-errors type: status_code status_code: { status_codes: [ERROR] } # السياسة 2: احتفظ بالتتبعات الأبطأ من ثانيتين - name: keep-slow-traces type: latency latency: { threshold_ms: 2000 } # السياسة 3: احتفظ بالتتبعات المُعلَّمة للتتبع (من feature flags) - name: keep-debug-tagged type: string_attribute string_attribute: key: "sampling.priority" values: ["always_sample"] # السياسة 4: احتمالي — احتفظ بـ 5% من الباقي - name: probabilistic-sample type: probabilistic probabilistic: { sampling_percentage: 5 } service: pipelines: traces: receivers: [otlp] processors: [tail_sampling, batch] exporters: [otlp/tempo]

مستويات الاستبقاء: منحنى التكلفة والدقة

لا تتمتع جميع بيانات القياس بالقيمة ذاتها مع مرور الوقت. ارتفاع معدل الأخطاء قابل للتنفيذ خلال 72 ساعة الأولى من الحادثة. تلك البيانات ذاتها مفيدة لتحليل الاتجاهات لمدة 30 يوماً. بعد 90 يوماً، نادراً ما تحتاج البيانات الخام — تحتاج ملخصات مُجمَّعة: متوسط الاستجابة اليومي عند الشريحة 99، ومعدل الأخطاء الأسبوعي لكل خدمة. بعد عام، تبقى فقط البيانات التي يُلزم بها الامتثال.

تُنفذ فرق المراقبة في كبرى شركات التقنية استبقاءً متعدد المستويات يُقايض الدقة بالتكلفة في كل مرحلة:

  • المستوى الساخن (0–72 ساعة) — دقة كاملة، بعد كامل، استعلامات سريعة. التكلفة: الأعلى. الغرض: لوحات المعلومات الفورية وتحقيقات المناوبة.
  • المستوى الدافئ (72 ساعة – 30 يوماً) — مقاييس بدقة كاملة مضغوطة في تخزين الكائنات (S3/GCS). سرعة الاستعلام أبطأ 2–5 أضعاف لكن التكلفة تنخفض 10 أضعاف. الغرض: مراجعات ما بعد الحوادث وحسابات معدل استهلاك SLO.
  • المستوى البارد (30 يوماً – سنة) — بيانات مُخفَّضة الدقة فقط. يُشغّل Thanos Compactor تخفيض دقة بمعدل 5m و1h، مما يُقلل التخزين 100–1000 ضعف مقارنة بالبيانات الخام. الغرض: تخطيط السعة ومقاييس المراجعات الفصلية.
  • الأرشيف / التخزين الجليدي (سنة+) — تبقى فقط المجاميع المُحسوبة مسبقاً. تُحذف الامتدادات والسجلات الخام؛ تستمر جداول الملخصات في مستودع البيانات لفترات الحفظ القانوني.
# thanos/compactor-config.yaml — التخفيض التلقائي للدقة والاستبقاء # تشغيل Thanos Compactor كنشر مستقل جنباً إلى جنب مع Thanos Store type: S3 config: bucket: my-thanos-bucket region: us-east-1 # إعدادات استبقاء Compactor (تُمرَّر كأعلام): # --retention.resolution-raw=30d احتفظ بعينات 15 ثانية لمدة 30 يوماً # --retention.resolution-5m=90d احتفظ بتخفيض 5 دقائق لمدة 90 يوماً # --retention.resolution-1h=1y احتفظ بتخفيض ساعة لمدة سنة # # مثال على علامات Compactor الرئيسية: # - compact # - --data-dir=/var/thanos/compact # - --objstore.config-file=/etc/thanos/objstore.yml # - --retention.resolution-raw=30d # - --retention.resolution-5m=90d # - --retention.resolution-1h=365d # - --wait # # يختار Thanos Querier تلقائياً مستوى الدقة المناسب # بناءً على نطاق زمن الاستعلام — لا تغييرات في التطبيق مطلوبة. # الاستعلامات التي تتجاوز 30 يوماً تقرأ تلقائياً من تخفيض 5 دقائق.
قواعد التسجيل كمُضاعِف للتوفير. قواعد التسجيل في Prometheus تحسب مسبقاً التجميعات المكلفة وتخزن النتيجة كمقياس جديد منخفض البعد. بدلاً من تشغيل sum(rate(http_requests_total[5m])) by (service) عبر ملايين السلاسل عند كل تحميل للوحة معلومات، شغّله مرة واحدة كل 30 ثانية عبر قاعدة تسجيل واستعلم عن السلسلة المُحسوبة الرخيصة. هذا يُقلل تكلفة الاستعلام 100 ضعف على مجموعات Prometheus المشغولة وهو النمط الصحيح لكل مقياس SLO ولوحة معلومات يُشغَّل أكثر من مرة في الدقيقة.

تحديد التكاليف والحوكمة

في المنظمات متعددة الفرق، يمنع تحديد تكاليف القياس الإنفاق غير المحدود. يجب أن تمتلك الفرق مراكز تكلفة خاصة بها. في Datadog، يظهر تكلفة كل فريق عبر ميزة Usage Attribution. في الحلول الذاتية الاستضافة، يمكن قياس التكلفة بعدد السلاسل لكل فريق: صنّف المقاييس بـ team="payments" واستعلم عن count by (team) ({__name__=~".+"}) لإنتاج فاتورة عدد السلاسل لكل فريق. ضع ميزانيات سلاسل لكل فريق ونبّه في CI عندما يتسبب PR في تجاوز الخدمة ميزانيتها.

الأدوار الثلاثة: كل قرار لتكلفة المراقبة يتضمن ثلاثة أدوار — البعد الأساسي (عدد السلاسل/التدفقات)، الحجم (عدد نقاط البيانات/الأحداث)، والاستبقاء (المدة الزمنية). تقليل أي منها يُخفض التكاليف بشكل متناسب. تقليلها جميعاً يُضاعف التوفير. ادمج مراجعة شهرية للأدوار الثلاثة في إيقاع فريقك التشغيلي.

إدارة بيانات المراقبة هي في نهاية المطاف مسألة اقتصاديات هندسية. الهدف ليس صفراً من القياس — بل الحد الأدنى من القياس اللازم لتلبية متطلبات التحقيق في SLO والتنبيه. خطة مراقبة تكلّف 2 مليون دولار سنوياً لكنها تكتشف كل حادثة في أقل من 5 دقائق قد تستحق كل دولار. خطة تكلّف 500 ألف دولار لكنها تترك فريقك في عمى أثناء الحوادث لا قيمة لها. اضبط الأدوار — البعد الأساسي ومعدلات العينات ونوافذ الاستبقاء — على مخاطر نظامك وأهداف وقت التعافي الفعلية، لا على أهداف تكلفة تعسفية.