Prometheus وGrafana

توسيع نطاق Prometheus

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

توسيع نطاق Prometheus

يستطيع خادم Prometheus الواحد استطلاع عشرات الآلاف من الأهداف ومعالجة ملايين السلاسل الزمنية النشطة — حتى حدود معينة. في المؤسسات الكبيرة، يصطدم Prometheus الأحادي بثلاثة جدران في آنٍ واحد: الذاكرة (تُخزَّن جميع السلاسل النشطة في الـ RAM)، والقرص (قاعدة بيانات TSDB المحلية غير مصممة للاستبقاء متعدد السنوات)، ونطاق التأثير (عطل خادم واحد يُعمي كل التنبيهات). تتناول هذه الدرس الأنماط الإنتاجية المستخدمة لتجاوز تلك الحدود: الـ Federation، والكتابة عن بُعد (Remote Write)، وأنظمة التخزين طويل الأمد مثل Thanos وGrafana Mimir.

مشكلة التوسع بالأرقام

تستهلك كل سلسلة زمنية نشطة نحو 2–3 كيلوبايت من الذاكرة في Prometheus 2.x. عند 10 ملايين سلسلة نشطة — وهو أمر شائع في أسطول Kubernetes كبير — يعني ذلك 20–30 غيغابايت من الـ heap قبل احتساب WAL وتعقيد الاستعلامات وعبء التسميات. تتكبّد قاعدة بيانات TSDB المحلية عبر الضغط إدارةً معقولة للقرص طوال 15 يومًا من الاستبقاء، إلا أن متطلبات الامتثال كثيرًا ما تشترط 13 شهرًا. هذان القيدان وحدهما يفرضان بنية موزعة.

الـ Federation

تتيح Federation في Prometheus لخادم عالمي استطلاع مقاييس مجمَّعة مسبقًا من عدة خوادم Prometheus محلية عبر نقطة نهاية HTTP الخاصة بها /federate. يستطلع كل خادم محلي شريحته الخاصة من الأهداف، بينما يسحب الخادم العالمي نتائج قواعد التسجيل ومقاييس التنبيهات الحرجة فحسب — لا السلاسل الخام.

# global-prometheus.yml — إعداد استطلاع الـ federation scrape_configs: - job_name: 'federate' scrape_interval: 60s honor_labels: true # الإبقاء على تسميات job/instance الأصلية metrics_path: /federate params: match[]: - '{__name__=~"job:.*"}' # مجاميع قواعد التسجيل فقط - 'up' - 'ALERTS{alertstate="firing"}' static_configs: - targets: - 'prometheus-dc1:9090' - 'prometheus-dc2:9090' - 'prometheus-dc3:9090'
الـ Federation لا تحل مشكلة الاستبقاء. تُقلّل من التعقيد العالمي للسلاسل عبر سحب المجاميع، لكن كل خادم محلي يظل يخزّن بياناته الخام محليًا. للتخزين طويل الأمد تحتاج إلى Remote Write.

تُعدّ الـ Federation الخيار الصحيح حين تريد لوحة مراقبة موحدة لتنبيهات متعددة مراكز البيانات دون تكرار البيانات الخام. اضبط honor_labels: true كي يحتفظ Prometheus العالمي بتسميات job وinstance من الخادم المصدر بدلًا من استبدالها باسم وظيفة الـ federation.

الكتابة عن بُعد (Remote Write)

تُرسل Remote Write كل عينة من Prometheus المحلي إلى خلفية تخزين خارجية عبر اتصال HTTP/2 دائم باستخدام حمولة protobuf مضغوطة بـ snappy. تجعلها قائمة WAL متينة: تُخزَّن العينات على القرص إن كان الخادم البعيد غير متاح وتُعاد إرسالها عند عودة الاتصال (بحد أقصى يساوي مدة استبقاء WAL المحددة بـ --storage.tsdb.retention.time).

# prometheus.yml — remote write إلى Thanos Receive أو Mimir remote_write: - url: "http://thanos-receive:19291/api/v1/receive" remote_timeout: 30s queue_config: capacity: 10000 # عينات مؤقتة في الذاكرة لكل shard max_shards: 200 # الحد الأقصى للتوازي min_shards: 1 max_samples_per_send: 2000 batch_send_deadline: 5s min_backoff: 30ms max_backoff: 5s write_relabel_configs: - source_labels: [__name__] regex: 'go_gc_.*' # حذف مقاييس وقت التشغيل الكثيرة action: drop
اضبط الـ shards بلوحة remote write. يوفر Prometheus لوحة Grafana جاهزة (المعرف 15032) تعرض عمق الطابور واستخدام الـ shards وعمر العينات. إذا كانت prometheus_remote_storage_shards دائمًا عند max_shards، ارفع الحد الأقصى أو قلّل حجم البيانات باستخدام write_relabel_configs.

Thanos — تحليل عميق للبنية

Thanos هو الحل مفتوح المصدر الأكثر انتشارًا للتخزين طويل الأمد المتاح عالميًا لبيانات Prometheus. يتكون من مكونات صغيرة مستقلة التوزيع بدلًا من ملف ثنائي واحد:

  • Sidecar — يعمل جنبًا إلى جنب مع كل pod Prometheus؛ يرفع كتل TSDB المكتملة (كل ساعتين) إلى التخزين الكائني (S3، GCS، Azure Blob) ويكشف Store API عبر gRPC لتمكين Thanos Query من الوصول إلى البيانات المحلية الحديثة.
  • Query — محرك استعلام متوافق مع Prometheus يوزع الاستعلامات على جميع نقاط Store API المسجلة (Sidecars، Store Gateways، Rulers) ويزيل التكرار من النسخ المتداخلة.
  • Store Gateway — يخدم الاستعلامات على الكتل الموجودة في التخزين الكائني؛ يدعم تخزين الفهرس مؤقتًا (Memcached/Redis) لتجنب إعادة قراءة البيانات الوصفية في كل طلب.
  • Compactor — يعمل كنسخة واحدة؛ يدمج الكتل ويُنزّل دقتها في التخزين الكائني (دقتا 5 دقائق وساعة واحدة) لإبقاء الاستعلامات على نطاق زمني واسع سريعةً.
  • Ruler — يُقيّم قواعد التسجيل والتنبيه عالميًا عبر جميع الشرائح، مما يُلغي الحاجة إلى federation لكل شريحة.
  • Receive — يقبل remote_write من Prometheus (بنية Thanos بدون Sidecars) ويكتب مباشرةً إلى التخزين الكائني؛ يُستخدم في الطوبولوجيا القائمة على الدفع.
Thanos scaled Prometheus architecture DC 1 Prometheus Sidecar gRPC Store API DC 2 Prometheus Sidecar DC 3 Prometheus Sidecar Object Storage S3 / GCS / ABS TSDB blocks رفع الكتل Store Gateway يخدم الكتل القديمة Compactor دمج + خفض الدقة Thanos Query توزيع + إزالة تكرار متوافق مع PromQL Grafana datasource: Query
بنية Thanos الموسّعة: ترفع الـ Sidecars كتل TSDB إلى التخزين الكائني؛ يوزع Thanos Query الاستعلامات على Sidecars (البيانات الحديثة) وStore Gateway (البيانات التاريخية)؛ يضغط Compactor الكتل لتسريع الاستعلامات بعيدة المدى.

Grafana Mimir — حين لا يكفي Thanos

يعتمد Thanos Sidecar على استمرار عمل Prometheus لخدمة البيانات الحديثة. أما Grafana Mimir (مفتوح المصدر، ترخيص Apache 2.0) فهو قاعدة بيانات TSDB مفككة بالكامل وقابلة للتوسع الأفقي مبنية على Cortex، تقبل remote_write وتشغّل كل مكون — Ingester وQuerier وCompactor وStore Gateway وRuler — كخدمات مصغّرة مستقلة التوسع خلف مسار كتابة واستعلام واحد. وهي الخيار الأنسب حين تحتاج إلى استيعاب ملايين السلاسل في الثانية أو إلى عزل متعدد المستأجرين (بيانات كل مستأجر في مساحة أسماء منفصلة).

# mimir.yaml — إعداد الوضع الأحادي البسيط (ملف ثنائي واحد، مناسب لأقل من 50 مليون سلسلة) target: all multitenancy_enabled: false blocks_storage: backend: s3 s3: bucket_name: mimir-blocks endpoint: s3.us-east-1.amazonaws.com tsdb: dir: /data/tsdb compactor: data_dir: /data/compactor store_gateway: sharding_enabled: false ingester: ring: replication_factor: 3 kvstore: store: memberlist limits: max_global_series_per_user: 0 # 0 = بلا حدود ingestion_rate: 100000 # عينة/ثانية لكل مستأجر

الاختيار بين Thanos وMimir

  • Thanos Sidecar — الأنسب حين لديك Prometheus بالفعل وتحتاج استبقاءً في التخزين الكائني بأدنى تغيير تشغيلي. ممتاز حتى نحو 50 مليون سلسلة نشطة عبر جميع الشرائح.
  • Thanos Receive — حين تريد دفع البيانات (بدون Sidecar) وتقبل تعقيد النسخ المتماثلة.
  • Mimir — للبيئات الجديدة على نطاق واسع؛ إنتاجية كتابة أفضل، ودعم أصيل لتعدد المستأجرين، وتوسع أفقي أكثر مرونة. تُشغّل Google وMeta مشتقات من Cortex/Mimir بمئات المليارات من السلاسل النشطة.
يجب أن يعمل Compactor كنسخة واحدة فقط. سواء مع Thanos أو Mimir، فإن تشغيل نسختين من Compactor ضد دلو التخزين الكائني ذاته في آنٍ واحد سيُفسد البيانات الوصفية للكتل. استخدم قفلًا موزعًا (يستخدم Thanos التخزين الكائني نفسه للقفل عبر ملف thanos.shipper.meta.json) وتأكد من أن نشرك لا يولّد وظائف ضغط متداخلة.

التعقيد التصنيفي: جذر مشاكل التوسع

قبل إضافة المزيد من البنية التحتية لمعالجة مشكلة Prometheus، دائمًا افحص التعقيد التصنيفي أولًا. تسمية واحدة عالية التعقيد (مثل request_id أو user_id) يمكنها إنشاء ملايين السلاسل لن يُرخّصها أي قدر من federation أو sharding. استخدم واجهة برمجة TSDB للعثور على المقاييس المسببة للمشكلة:

# أعلى 10 أسماء مقاييس من حيث عدد السلاسل curl -s http://localhost:9090/api/v1/status/tsdb \ | python3 -m json.tool \ | grep -A2 '"seriesCountByMetricName"' # أو في PromQL — التعقيد لكل job sort_desc(count by (job) ({__name__!=""}))
احذف عند المصدر لا عند المخزن. استخدم metric_relabel_configs في وظائف الاستطلاع داخل Prometheus لحذف السلاسل عالية التعقيد قبل استيعابها. الحذف في طبقة remote_write يُهدر الذاكرة ومساحة WAL على الخادم المحلي. الحذف في خلفية التخزين يُهدر الشبكة. احذف في أبكر مرحلة ممكنة.

قائمة التحقق الإنتاجية لتوسيع Prometheus

  1. اضبط --storage.tsdb.retention.time=15d على Prometheus المحلي واعتمد على التخزين الكائني لما يزيد على ذلك.
  2. فعّل ضغط WAL: --storage.tsdb.wal-compression (يوفر نحو 30% من مساحة WAL).
  3. حجّم ذاكرة Prometheus بنحو 3 بايت × عدد السلاسل النشطة × 2 (هامش للذروات).
  4. توزّع فترات الاستطلاع عبر الشرائح لتجنب ارتفاعات الحمل المتزامن على المُصدِّرات.
  5. راقب المُراقِبين: اكشف مقاييس Thanos Sidecar وStore Gateway لنسخة Prometheus خفيفة مستقلة مخصصة لمنظومة الرصد لديك.
  6. اختبر اتصال التخزين الكائني بانتظام — خطأ صامت في صلاحيات S3 قد يملأ القرص من WAL قبل أن يُنبَّه أحد.