معمارية تسجيل الأحداث على نطاق واسع
معمارية تسجيل الأحداث على نطاق واسع
كل نظام يُصدر سجلات. خدمة مصغرة واحدة على جهاز محلي تُنتج تدفقاً قابلاً للإدارة. أما عنقود Kubernetes يُشغّل 400 خدمة عبر ثلاث مناطق، فيُنتج مليارات السطور يومياً بمعدلات قد تقفز إلى مئات الميجابايت في الثانية. المعمارية التي تستخدمها لجمع ونقل وتخزين والاستعلام عن تلك السجلات ليست تفصيلاً — إنها اهتمام بنية تحتية من الدرجة الأولى يُحدد مباشرة ما إذا كان فريق المناوبة يستطيع التحقيق في حادث إنتاجي في دقائق أو ساعات.
يبني هذا الدرس النموذج الذهني لتسجيل الأحداث المركزي على نطاق واسع: ما الذي تفعله المراحل الأربع في خط الأنابيب فعلاً، وأين يمكن لكل مرحلة أن تفشل تحت الحمل، وكيف تفكر المنظمات الهندسية من الدرجة الأولى في المقايضات عند كل حد.
لماذا يوجد تسجيل الأحداث المركزي
بدون نظام تسجيل مركزي، يعني تصحيح أخطاء نظام موزع الدخول بـ SSH إلى عقد فردية والبحث بـ grep في ملفات محلية، وأمل أن يكون السجل ذي الصلة قد وصل إلى الجهاز الذي تنظر إليه. على نطاق واسع هذا مستحيل تشغيلياً: الـ pods مؤقتة (تُقتل بعد نفاد الذاكرة، ويُعيد الجدولي إنشاءها)، يمكن استبدال العقد بواسطة القياس التلقائي، والطلب الذي تحقق فيه ربما لمس عشرين خدمة عبر خمسة namespaces.
يحل تسجيل الأحداث المركزي هذا بتوجيه كل مخرجات السجل إلى backend واحد قابل للاستعلام. تطرح استعلاماً واحداً، تحصل على الصورة الكاملة. التحدي الهندسي هو القيام بذلك بشكل موثوق، دون أن يُصبح خط أنابيب التسجيل نفسه نقطة فشل، ودون انفلات التكلفة عن السيطرة.
المرحلة 1: الجمع
الجمع هو فعل التقاط مخرجات السجل الخام من أي مكان نشأت: stdout/stderr من حاوية، ملف على القرص تكتبه عملية خلفية، مقبس syslog، أو SDK تطبيق يكتب أحداثاً منظَّمة مباشرة. يعمل جامع الأحداث قريباً من المصدر — كـ DaemonSet في Kubernetes، كـ sidecar للحاوية، أو كمكتبة مُدمجة في التطبيق.
الجامعات ذات الجودة الإنتاجية (Fluent Bit، Promtail، OpenTelemetry Collector، Vector) تفعل أكثر من مجرد إعادة توجيه البايتات. تُنفّذ التحليل (تحويل النص غير المنظَّم إلى حقول منظَّمة)، والإثراء (إضافة بيانات Kubernetes الوصفية: اسم الـ pod، الـ namespace، العقدة، وسم صورة الحاوية)، والتصفية (حذف ضجيج فحوصات الصحة قبل وصولها إلى الشبكة)، والتخزين المؤقت (الاحتفاظ بالبيانات في الذاكرة أو على القرص حين يكون المصب مؤقتاً غير متاح).
طبقة الجمع هي المكان الذي يرتكب فيه كثير من المنظمات أول خطأ مكلف: اختيار وكيل ثقيل (Logstash كـ DaemonSet) يستهلك 500 ميجابايت من الذاكرة لكل عقدة، أو نشر بدون تخزين مؤقت بحيث يُسبب انقطاع النقل ضياع السجلات. Fluent Bit هو الخيار الأفضل الممارسة الحالي لـ Kubernetes: مكتوب بـ C، يعمل في أقل من 50 ميجابايت RSS، ولديه إثراء بيانات Kubernetes الوصفية مُدمج.
Mem_Buf_Limit وstorage.type filesystem في Fluent Bit. بدون مخزن مؤقت على الملف، يُضيع انقطاع المصب السجلات. معه، يُخزّن الوكيل على القرص ويُعيد التشغيل حين يتعافى النقل. Google وDatadog وAWS جميعها تُشغّل خطوط أنابيب جمع ذات تخزين مؤقت لهذا السبب بالذات.المرحلة 2: النقل
طبقة النقل تُحرّك بيانات السجل من الجامعات إلى backend التخزين. على نطاق صغير هذا دفع HTTP/gRPC مباشر من كل وكيل. على نطاق إنتاجي — آلاف العقد، جيجابايتات في الثانية — وسيط رسائل مُخصص يفصل الجامع عن المخزن.
وسيط النقل النموذجي لتسجيل الأحداث الكبير النطاق هو Apache Kafka. تُنشر الجامعات إلى موضوعات Kafka مُقسَّمة حسب الخدمة أو الـ namespace. يقرأ المستهلكون المصب (الفهارس، طبقة استيعاب التخزين) من تلك الموضوعات بوتيرتهم الخاصة. يوفر Kafka المتانة (أجزاء السجل على القرص، احتفاظ قابل للضبط)، وعزل الضغط الخلفي (مُفهرس بطيء لا يتسبب في تراكم الجامع ونفاد ذاكرته)، والإعادة (إذا كان للمُفهرس خطأ وفقد يوماً من البيانات، يمكنك الإعادة من Kafka).
بالنسبة للمنظمات الأصغر أو المتواجدة بالفعل على AWS، تُؤدي Kinesis Data Streams نفس الدور. بالنسبة لـ Grafana Loki، يمكن أن يكون النقل دفعاً مباشراً عبر Promtail أو Grafana Alloy (نطاق صغير) أو Kafka (نطاق كبير). يمكن لخطوط أنابيب OpenTelemetry Collector التوزيع على backends متعددة في وقت واحد — نمط قوي للمنظمات التي تهاجر بين أنظمة التخزين.
المرحلة 3: التخزين
طبقة التخزين تُفهرس وتحتفظ ببيانات السجل حتى يمكن الاستعلام عنها بكفاءة. الخياران الرئيسيان مفتوحا المصدر يعكسان فلسفتَي تصميم مختلفتين جوهرياً:
- Elasticsearch (أو OpenSearch) — فهرس عكسي للنص الكامل لكل حقل. سريع للغاية لبحث الكلمات المفتاحية والتجميعات عبر الحقول الاعتباطية. تكلفة كتابة عالية: كل حقل يُحلَّل ويُفهرس عند الاستيعاب، مستهلكاً CPU وتخزيناً كبيرَين (عادةً 2–5 أضعاف حجم السجل الخام). الخيار الصحيح حين يحتاج المهندسون إلى استعلامات ad-hoc على مستوى الحقل عبر بيانات شبه منظَّمة.
- Grafana Loki — تخزين مُفهرَس بالتسميات ومضغوط في مقاطع. فقط التسميات (البيانات الوصفية: app، namespace، level، region) تُفهرَس؛ محتوى السجل يُخزَّن في مقاطع مضغوطة ويُمسح عند الاستعلام عبر مرشح LogQL. أرخص 10–20 مرة من Elasticsearch لكل جيجابايت. الخيار الصحيح حين تكون سجلاتك منظَّمة وأنماط استعلامك تبدأ بالتسمية. مُصمَّم للعمل مع Prometheus وGrafana Tempo بشكل طبيعي.
خيار ثالث يكتسب زخماً في سياقات التحليلات عالية الحجم هو ClickHouse: قاعدة بيانات OLAP عمودية يمكنها استيعاب ملايين الأحداث في الثانية وتنفيذ استعلامات التجميع في ميلي ثوان بنسب ضغط عالية جداً. تستخدمها Cloudflare وByteDance لتحليلات السجلات على نطاق البيتابايت.
المرحلة 4: الاستعلام
طبقة الاستعلام هي كيفية تفاعل البشر والأنظمة الآلية مع السجلات المخزَّنة. تشمل واجهة المستخدم (Kibana لـ Elasticsearch، Grafana Explore لـ Loki)، ولغة الاستعلام (KQL/Lucene أو LogQL)، والتكامل مع التنبيه (قواعد Grafana Alerting المدعومة باستعلامات السجل).
على نطاق الشركات الكبرى، تنقسم أنماط الاستعلام إلى فئتين بمتطلبات مختلفة جداً:
- التحقيق التفاعلي في الحوادث — مهندس يُشغّل استعلامات ad-hoc، يريد نتائج في أقل من 3 ثوان. يتطلب فهرساً ساخناً، وواجهة أمامية قوية للاستعلام تُخزّن النتائج مؤقتاً وتُوازي الاستعلامات، ونطاقاً زمنياً افتراضياً جيداً (15 دقيقة أخيرة لا كل الوقت).
- التنبيه الآلي — محرك قواعد يُقيّم تعبير LogQL أو KQL كل 60 ثانية ويُطلق تنبيهاً عند تحقق شرط. يجب أن تكون هذه استعلامات حتمية منخفضة القيم الكاردينالية رخيصة التقييم بشكل متكرر. وجّهها عبر مسار استعلام مُخصَّص لتجنب التنافس مع الاستعلامات التفاعلية خلال حادث.
أوضاع فشل خط الأنابيب
فهم مواضع فشل خط الأنابيب تحت الحمل لا يقل أهمية عن فهم تشغيله الطبيعي. أكثر أوضاع الفشل الإنتاجية شيوعاً:
- نفاد ذاكرة الجامع: طوفان سجلات (عاصفة استثناءات، تسجيل تصحيح مطوّل متروك في الإنتاج) يُطغي المخزن المؤقت في ذاكرة الجامع. بدون
Mem_Buf_Limitوالتخزين المؤقت على الملف، يُقتل pod الجامع بسبب OOM وتُفقد السجلات. الإصلاح: مخزن مؤقت على الملف + حد مضبوط أدنى من طلب الذاكرة للجامع. - تأخر مستهلك Kafka: طبقة استيعاب التخزين تتأخر عن منتج Kafka (الجامعات). يتراكم التأخر. إذا كان احتفاظ Kafka قصيراً جداً (مثلاً 24 ساعة)، تُحذف الأجزاء غير المُستهلَكة قبل أن يعالجها المُفهرس. الإصلاح: ضبط احتفاظ Kafka على 48–72 ساعة على الأقل لموضوعات التسجيل؛ تنبيه على تأخر مجموعة المستهلكين.
- النقاط الساخنة في شرائح Elasticsearch: كل سجلات نافذة زمنية تستقر في نفس شريحة Elasticsearch لأن نمط الفهرس قائم على الزمن وعدد الشرائح منخفض جداً. تلك الشريحة تصبح عنق زجاجة للكتابة. الإصلاح: تحديد حجم الشرائح بـ 30–50 جيجابايت كحد أقصى؛ استخدام سياسات rollover في ILM لإنشاء شرائح جديدة بالوتيرة المناسبة.
- عاصفة استعلامات خلال حادث: حين يقع انقطاع كبير، يُشغّل عشرة مهندسين في آن واحد استعلامات واسعة. يمكن أن يُثقل هذا طبقة الاستعلام ويجعل نظام التسجيل غير متاح تماماً حين يُحتاج إليه. الإصلاح: واجهة أمامية للاستعلام مع تخزين مؤقت للنتائج وحدود معدل لكل مستخدم؛ دائماً قيّد الاستعلامات بتسمية قبل فتح النطاق الزمني.
اختيار بنيتك
البنية الصحيحة تعتمد على نطاقك وأنماط استعلامك، لكن الافتراضيات الصناعية لعام 2025 تبدو كالآتي:
- Kubernetes-native، حساس للتكلفة: Grafana Alloy (جامع) → دفع مباشر أو Kafka → Grafana Loki (تخزين) → Grafana (استعلام). أدنى تكلفة تشغيلية؛ تكامل طبيعي مع Prometheus وTempo للملاحظة المترابطة.
- أحمال عمل مختلطة، بحث ad-hoc غني: Fluent Bit (جامع) → Kafka → Elasticsearch/OpenSearch (تخزين) → Kibana (استعلام). أغلى لكن أكثر مرونة لمحتوى السجل غير المنظَّم.
- تحليلات عالية الحجم، فريق هندسة بيانات: OTel Collector → Kafka → ClickHouse (تخزين) → Grafana أو أدوات SQL مخصَّصة. أفضل ضغط وأداء تجميع على نطاق البيتابايت.
تبني الدروس المتبقية في هذا الدرس التعليمي كل مكوّن من هذه البنية بعمق — بدءاً بمعايير التسجيل المنظَّم التي تجعل كل هذا قابلاً للاستعلام أصلاً.