المكوّنات ذات الصلة بـ ClickHouse
- يُعدّ OpenTelemetry Collector وكيلًا يستقبل بيانات القياس عن بُعد ويعالجها ويصدّرها. ويستخدم الحل المعتمد على ClickHouse هذا المكوّن لكلٍّ من جمع السجلات ومعالجة الأحداث قبل تجميعها على دفعات وإدراجها.
- SDKs الخاصة باللغات التي تنفّذ المواصفات وواجهات برمجة التطبيقات وتصدير بيانات القياس عن بُعد. وتضمن هذه SDKs عمليًا تسجيل الـ traces على نحو صحيح داخل شيفرة التطبيق، مع إنشاء الـ spans المكوِّنة لها وضمان تمرير الـ context بين الخدمات عبر البيانات الوصفية، ما يكوّن traces موزعة ويضمن إمكانية ربط الـ spans ببعضها. وتتكامل هذه SDKs مع منظومة توفّر تنفيذًا تلقائيًا للمكتبات وأطر العمل الشائعة، ما يعني أن المستخدم لا يحتاج إلى تغيير شيفرته ويحصل على instrumentation جاهزة مباشرة.
التوزيعات
- تقليل حجم المجمِّع، مما يسرّع أوقات نشره
- تحسين أمان المجمِّع عبر تقليل سطح الهجوم المتاح
إدخال البيانات باستخدام OTel
أدوار نشر OpenTelemetry Collector
- الوكيل - تجمع مثيلات الوكيل البيانات عند الطرفية، مثلًا على الخوادم أو على عُقد Kubernetes، أو تستقبل الأحداث مباشرةً من التطبيقات المزوّدة بـ OpenTelemetry SDK. وفي الحالة الأخيرة، يعمل مثيل الوكيل مع التطبيق أو على المضيف نفسه الذي يعمل عليه التطبيق (مثل حاوية جانبية أو مجموعة شياطين). ويمكن للوكلاء إما إرسال بياناتهم مباشرةً إلى ClickHouse أو إلى مثيل بوابة. وفي الحالة الأولى، يُشار إلى ذلك باسم نمط نشر الوكيل.
- البوابة - توفّر مثيلات البوابة خدمة مستقلة (على سبيل المثال، عملية نشر في Kubernetes)، وعادةً ما تكون لكل عنقود، أو لكل مركز بيانات، أو لكل منطقة. وتتلقى هذه المثيلات الأحداث من التطبيقات (أو من مجمّعات أخرى تعمل كوكلاء) عبر نقطة نهاية OTLP واحدة. وعادةً ما تُنشر مجموعة من مثيلات البوابة، مع استخدام موازن حمل جاهز لتوزيع الحمل بينها. وإذا كانت جميع الوكلاء والتطبيقات ترسل إشاراتها إلى نقطة النهاية الواحدة هذه، فغالبًا ما يُشار إلى ذلك باسم نمط نشر البوابة.
جمع السجلات
- الكشط عبر مستقبِل filelog - يتتبع هذا المستقبِل الملفات على القرص ويحوّلها إلى رسائل سجل، ثم يرسلها إلى ClickHouse. ويتعامل هذا المستقبِل مع مهام معقدة مثل اكتشاف الرسائل متعددة الأسطر، والتعامل مع تدوير السجلات، وإنشاء نقاط تحقق لضمان المتانة عند إعادة التشغيل، واستخراج البنية. كما يستطيع هذا المستقبِل أيضًا تتبّع سجلات حاويات Docker وKubernetes، ويمكن نشره كمخطط Helm، مع استخراج بنيتها وإثرائها بتفاصيل الـ pod.
نصيحة:
otelbin.ioيُعد otelbin.io مفيدًا للتحقق من الإعدادات وتصورها.المهيكلة مقابل غير المهيكلة
مثال
json_parser لأن سجلاتنا منظَّمة. عدّل المسار إلى ملف access-structured.log.
فكّر في استخدام ClickHouse لإجراء التحليليستخرج المثال أدناه
timestamp من السجل. ويتطلب ذلك استخدام العامل json_parser، الذي يحوّل سطر السجل بالكامل إلى JSON string، ثم يضع النتيجة في LogAttributes. قد يكون هذا مكلفًا من الناحية الحسابية، ويمكن تنفيذه بكفاءة أعلى في ClickHouse - استخراج البنية باستخدام SQL. ويمكن العثور على مثال غير منظَّم مكافئ يستخدم regex_parser لتحقيق ذلك هنا.filelog receiver)؛ فعلى سبيل المثال، بدلًا من otelcol_0.102.1_darwin_arm64.tar.gz سينزّل المستخدمون otelcol-contrib_0.102.1_darwin_arm64.tar.gz. ويمكن العثور على الإصدارات هنا.
بعد التثبيت، يمكن تشغيل OTel Collector باستخدام الأوامر التالية:
Body، لكن JSON يُستخرج تلقائيًا إلى حقل Attributes بفضل json_parser. كما استُخدم operator نفسه لاستخراج الطابع الزمني إلى العمود Timestamp المناسب. للاطلاع على توصيات بشأن معالجة السجلات باستخدام OTel، راجع Processing.
المشغّلاتالمشغّلات هي أبسط وحدة في معالجة السجلات. ويؤدي كل مشغّل مهمة واحدة فقط، مثل قراءة الأسطر من ملف أو تحليل JSON من حقل. ثم تُربط المشغّلات معًا ضمن pipeline لتحقيق النتيجة المطلوبة.
TraceID أو SpanID. وإذا كانا موجودين، على سبيل المثال في الحالات التي يطبّق فيها المستخدمون distributed tracing، فيمكن استخراجهما من JSON باستخدام الأساليب نفسها الموضحة أعلاه.
بالنسبة إلى المستخدمين الذين يحتاجون إلى جمع log files محلية أو خاصة بـ Kubernetes، نوصيهم بالتعرّف على خيارات الإعداد المتاحة لـ filelog receiver، وكيفية التعامل مع offsets، وكيفية تحليل السجلات متعددة الأسطر.
جمع سجلات Kubernetes
ResourceAttributes. يستخدم ClickHouse حاليًا النوع Map(String, String) لهذا العمود. راجع Using Maps وExtracting from maps لمزيد من التفاصيل حول كيفية التعامل مع هذا النوع وتحسينه.
جمع التتبعات
مثال
telemetrygen لتوليد بيانات التتبّع. اتبع التعليمات هنا للتثبيت.
يستقبل الإعداد التالي أحداث التتبّع عبر مستقبِل OTLP قبل إرسالها إلى stdout.
config-traces.xml
telemetrygen:
المعالجة - التصفية والتحويل والإثراء
-
المعالِجات - تأخذ المعالِجات البيانات التي تجمعها المستقبِلات وتعدّلها أو تحوّلها قبل إرسالها إلى المصدِّرات. وتُطبَّق المعالِجات بالترتيب المحدد في قسم
processorsمن تهيئة collector. وهي اختيارية، لكن عادةً ما يُوصى بالحد الأدنى منها. وعند استخدام OTel collector مع ClickHouse، نوصي بقصر المعالِجات على ما يلي:- يُستخدم memory_limiter لمنع حالات نفاد الذاكرة في الـ collector. راجع تقدير الموارد للاطلاع على التوصيات.
- أي معالج ينفّذ الإثراء استنادًا إلى السياق. على سبيل المثال، يتيح Kubernetes Attributes Processor التعيين التلقائي لسمات resource الخاصة بـ spans وmetrics وlogs باستخدام بيانات k8s الوصفية، مثل إثراء الأحداث بمعرّف الـ pod المصدر.
- أخذ العينات من tail أو head إذا لزم الأمر بالنسبة إلى traces.
- التصفية الأساسية - إسقاط الأحداث غير المطلوبة إذا تعذر تنفيذ ذلك عبر operator (انظر أدناه).
- التجميع على دفعات - وهو أمر أساسي عند العمل مع ClickHouse لضمان إرسال البيانات على دفعات. راجع “التصدير إلى ClickHouse”.
- المشغّلات - توفّر المشغّلات أبسط وحدة معالجة متاحة على مستوى receiver. كما تدعم parsing أساسيًا، بما يتيح تعيين حقول مثل Severity وTimestamp. ويُدعَم هنا أيضًا parsing لكل من JSON وregex، إلى جانب تصفية الأحداث والتحويلات الأساسية. ونوصي بإجراء تصفية الأحداث هنا.
مثال
regex_parser) وتصفية الأحداث، إلى جانب معالج لتجميع الأحداث في دفعات والحد من استخدام الذاكرة.
config-unstructured-logs-with-processor.yaml
التصدير إلى ClickHouse
استخدم OpenTelemetry Collector Contribيُعد ClickHouse exporter جزءًا من OpenTelemetry Collector Contrib، وليس من التوزيعة الأساسية. يمكنك إما استخدام توزيعة contrib أو بناء collector مخصص.
- pipelines - يوضّح الإعداد أعلاه استخدام pipelines، وهي تتكوّن من مجموعة من receivers وprocessors وexporters، مع pipeline للسجلات وأخرى للتتبعات.
- endpoint - تتم تهيئة الاتصال مع ClickHouse عبر المعلَمة
endpoint. تؤدي connection string tcp://localhost:9000?dial_timeout=10s&compress=lz4&async_insert=1إلى إجراء الاتصال عبر TCP. إذا كنت تفضّل HTTP لأسباب تتعلق بتحويل حركة المرور، فعدّل connection string هذه كما هو موضح هنا. وتجد هنا تفاصيل الاتصال الكاملة، بما في ذلك إمكانية تحديد username وpassword داخل connection string هذه.
- ttl - تحدد القيمة هنا مدة الاحتفاظ بالبيانات. مزيد من التفاصيل في “إدارة البيانات”. يجب تحديدها كوحدة زمنية بالساعات، مثل 72h. نعطّل TTL في المثال أدناه لأن بياناتنا تعود إلى عام 2019، وسيحذفها ClickHouse فوراً إذا تم إدخالها.
- traces_table_name وlogs_table_name - يحددان اسمَي جدولي السجلات والتتبعات.
- create_schema - يحدد ما إذا كانت الجداول ستُنشأ باستخدام schemas الافتراضية عند بدء التشغيل. تكون القيمة الافتراضية true لأغراض Getting Started. ينبغي ضبطه على false وتعريف schema خاص بك.
- database - قاعدة البيانات الهدف.
- retry_on_failure - إعدادات تحدد ما إذا كان ينبغي إعادة محاولة batches الفاشلة.
- batch - يضمن batch processor إرسال الأحداث على شكل batches. نوصي بقيمة لا تقل عن 10,000 مع timeout قدره 5s (ويمكن استخدام قيم تصل إلى 100,000 إذا سمحت الذاكرة). وأيٌّ من هذين الحدّين يتم بلوغه أولاً سيؤدي إلى بدء batch ليتم flush إلى exporter. إن خفض هذه القيم يعني pipeline أقل latency مع إتاحة البيانات للاستعلام عنها بشكل أسرع، لكن على حساب المزيد من connections والمزيد من batches المرسلة إلى ClickHouse. لا نوصي بذلك إذا كنت لا تستخدم asynchronous inserts، إذ قد يسبب ذلك مشكلات too many parts في ClickHouse. وعلى العكس، إذا كنت تستخدم asynchronous inserts، فإن إتاحة هذه البيانات للاستعلام ستعتمد أيضاً على إعدادات asynchronous insert، رغم أن البيانات ستظل تُرسل من connector بسرعة أكبر. راجع Batching لمزيد من التفاصيل.
- sending_queue - يتحكم في حجم sending queue. يحتوي كل عنصر في queue على batch. وإذا تم تجاوز سعة هذه queue، مثلاً بسبب تعذر الوصول إلى ClickHouse مع استمرار وصول الأحداث، فسيتم إسقاط batches.
telemetrygen:
المخطط الجاهز للاستخدام
create_schema. بالإضافة إلى ذلك، يمكن تغيير اسمي جدولَي السجلات والتتبعات من القيمتَين الافتراضيتَين otel_logs وotel_traces عبر الإعدادات المذكورة أعلاه.
في المخططات أدناه، نفترض أن TTL مفعّل بقيمة 72h.
otelcol-contrib v0.102.1):
- افتراضيًا، يُقسَّم الجدول حسب التاريخ باستخدام
PARTITION BY toDate(Timestamp). وهذا يجعل حذف البيانات التي انتهت صلاحيتها أكثر كفاءة. - يُضبط TTL عبر
TTL toDateTime(Timestamp) + toIntervalDay(3)، وهو يتوافق مع القيمة المحددة في تهيئة collector. وتعنيttl_only_drop_parts=1أنه لا تُحذف إلا الأجزاء الكاملة عندما تكون جميع الصفوف التي تحتويها قد انتهت صلاحيتها. وهذا أكثر كفاءة من حذف الصفوف داخل الأجزاء، إذ يترتب على ذلك تنفيذ عملية حذف مكلفة. نوصي دائمًا بضبط هذا الإعداد. راجع إدارة البيانات باستخدام TTL لمزيد من التفاصيل. - يستخدم الجدول محرك
MergeTreeengine التقليدي. وهذا الخيار موصى به للسجلات وآثار التتبع، ولا ينبغي أن تحتاج إلى تغييره. - يُرتَّب الجدول بواسطة
ORDER BY (ServiceName, SeverityText, toUnixTimestamp(Timestamp), TraceId). وهذا يعني أن الاستعلامات ستكون مُحسّنة لعامل تصفية علىServiceNameوSeverityTextوTimestampوTraceId، وأن الأعمدة الأسبق في القائمة ستكون التصفية عليها أسرع من الأعمدة اللاحقة. فعلى سبيل المثال، ستكون التصفية حسبServiceNameأسرع بكثير من التصفية حسبTraceId. ينبغي تعديل هذا الترتيب وفقًا لأنماط الوصول المتوقعة — راجع اختيار مفتاح أساسي. - يطبّق المخطط أعلاه
ZSTD(1)على الأعمدة. وهذا يوفّر أفضل Compression للسجلات. يمكنك زيادة مستوى ضغط ZSTD (فوق القيمة الافتراضية 1) للحصول على ضغط أفضل، رغم أن ذلك نادرًا ما يكون مفيدًا. ستؤدي زيادة هذه القيمة إلى زيادة عبء CPU عند insert time (أثناء الضغط)، مع أن فك الضغط (وبالتالي الاستعلامات) ينبغي أن يظل بمستوى مماثل. راجع هنا لمزيد من التفاصيل. كما يُطبَّق delta encoding إضافي علىTimestampبهدف تقليل حجمه على القرص. - لاحظ أن
ResourceAttributesوLogAttributesوScopeAttributesهي maps. ومن المهم فهم الفروق بينها. راجع “استخدام الخرائط” لمعرفة كيفية الوصول إلى هذه الخرائط وتحسين الوصول إلى المفاتيح داخلها. - معظم الأنواع الأخرى هنا، مثل
ServiceNameمن النوع LowCardinality، مُحسَّنة. لاحظ أنBody، وهو JSON في سجلات الأمثلة لدينا، يُخزَّن كسلسلة String. - تُطبَّق bloom filters على مفاتيح الخرائط وقيمها، وكذلك على العمود
Body. وتهدف هذه إلى تحسين زمن الاستعلامات التي تصل إلى هذه الأعمدة، لكنها لا تكون مطلوبة عادةً. راجع الفهارس الثانوية/فهارس تخطي البيانات.
تحسين عمليات الإدراج
التجميع على دفعات
- (1) إذا كانت هناك مشكلة في العقدة التي تستقبل البيانات، فستنتهي مهلة استعلام insert (أو سيظهر خطأ أكثر تحديدًا) ولن يتلقى المرسِل تأكيدًا.
- (2) إذا كانت العقدة قد كتبت البيانات، لكن تعذّر إعادة التأكيد إلى مُرسِل الاستعلام بسبب انقطاع في الشبكة، فسيتلقى المرسِل إما انتهاء مهلة أو خطأ في الشبكة.
timeout الخاص بمعالج الدفعات، مما يضمن بقاء زمن الانتقال من طرف إلى طرف في مسار المعالجة منخفضًا، وأن تكون الدفعات ذات حجم متسق.
استخدم asynchronous inserts
timeout الخاصة بـ معالج الدفعات. وقد يؤدي ذلك إلى مشكلات، وهنا تبرز الحاجة إلى asynchronous inserts. تظهر هذه الحالة عادةً عندما تُضبط collectors التي تعمل بدور agent للإرسال مباشرةً إلى ClickHouse. ويمكن لـ gateways، من خلال عملها كمجمِّعات مركزية، أن تخفف هذه المشكلة — راجع التوسع باستخدام gateways.
إذا لم يكن من الممكن ضمان دفعات كبيرة، فيمكنك إسناد التجميع إلى ClickHouse باستخدام asynchronous inserts. مع asynchronous inserts، تُدرج البيانات أولًا في buffer، ثم تُكتب لاحقًا إلى مساحة تخزين قاعدة البيانات، أي بشكل غير متزامن.
عند تمكين asynchronous inserts، عندما يستقبل ClickHouse ① insert query، تُكتب بيانات الاستعلام ② فورًا إلى buffer داخل الذاكرة. وعندما ③ تحدث عملية flush التالية للـ buffer، تُرتَّب بيانات الـ buffer ترتيبًا ثم تُكتب كجزء إلى مساحة تخزين قاعدة البيانات. لاحظ أن البيانات لا تكون قابلة للبحث عبر queries قبل flush إلى مساحة تخزين قاعدة البيانات؛ كما أن عملية flush للـ buffer قابلة للضبط.
لتمكين asynchronous inserts للمجمّع، أضف async_insert=1 إلى connection string. نوصي باستخدام wait_for_async_insert=1 (وهو الإعداد الافتراضي) للحصول على ضمانات التسليم — راجع هنا لمزيد من التفاصيل.
تُدرج بيانات async insert بمجرد تنفيذ flush لـ buffer في ClickHouse. ويحدث ذلك إما بعد تجاوز async_insert_max_data_size أو بعد مرور async_insert_busy_timeout_ms مللي ثانية منذ أول INSERT query. وإذا ضُبط async_insert_stale_timeout_ms على قيمة غير صفرية، فستُدرج البيانات بعد مرور async_insert_stale_timeout_ms milliseconds منذ آخر query. يمكنك ضبط هذه settings للتحكم في زمن الانتقال من طرف إلى طرف في pipeline لديك. وترد هنا settings إضافية يمكن استخدامها لضبط flush للـ buffer. وبوجه عام، تكون القيم الافتراضية مناسبة.
فكّر في asynchronous inserts التكيفيةفي الحالات التي يُستخدم فيها عدد قليل من agents، مع معدل نقل منخفض ومتطلبات صارمة لزمن الانتقال من طرف إلى طرف، فقد تكون asynchronous inserts التكيفية مفيدة. لكن هذه الميزة لا تكون مناسبة عادةً لحالات استخدام Observability ذات معدل النقل المرتفع، كما هو الحال مع ClickHouse.
async_insert_deduplicate.
يمكن العثور على التفاصيل الكاملة حول ضبط هذه feature هنا، مع شرح معمّق هنا.
معماريات النشر
agents فقط
traces من التطبيقات المحلية (مثلًا كحاوية sidecar) وتجمع logs من الخوادم وعُقد Kubernetes. في هذا الوضع، ترسل agents بياناتها مباشرةً إلى ClickHouse.
تُعد هذه المعمارية مناسبة لعمليات النشر الصغيرة والمتوسطة. وتتمثل ميزتها الرئيسية في أنها لا تتطلب عتادًا إضافيًا، وتُبقي البصمة الإجمالية لموارد حل observability من ClickHouse عند الحد الأدنى، مع اقتران بسيط بين التطبيقات وcollectors.
ينبغي التفكير في الانتقال إلى معمارية قائمة على البوابة بمجرد أن يتجاوز عدد agents عدة مئات. فهذه المعمارية تنطوي على عدة عيوب تجعل توسيع نطاقها صعبًا:
- توسيع نطاق الاتصالات - سينشئ كل agent اتصالًا مع ClickHouse. ومع أن ClickHouse قادر على الحفاظ على مئات، إن لم يكن آلاف، اتصالات
insertالمتزامنة، فإن ذلك سيصبح في نهاية المطاف عاملًا مقيِّدًا وسيجعلinsertsأقل كفاءة — أي إن ClickHouse سيستهلك موارد أكثر للإبقاء على هذه الاتصالات. ويؤدي استخدامgatewaysإلى تقليل عدد الاتصالات وجعلinsertsأكثر كفاءة. - المعالجة عند الأطراف - يجب تنفيذ أي تحويلات أو معالجة للأحداث عند الأطراف أو داخل ClickHouse في هذه المعمارية. وإلى جانب كون ذلك مقيِّدًا، فقد يعني هذا إما إنشاء
materialized viewsمعقدة في ClickHouse أو نقل قدر كبير من العمليات الحسابية إلى الأطراف — حيث قد تتأثر الخدمات الحرجة وتكون الموارد شحيحة. - الدفعات الصغيرة وزمن الاستجابة - قد تجمع
collectorsالخاصة بـ agents عددًا قليلًا جدًا من الأحداث، كلٌّ على حدة. وهذا يعني عادةً أنها تحتاج إلى تهيئتها لتنفيذflushعلى فاصل زمني محدد للوفاء بـ اتفاقيات مستوى الخدمة الخاصة بالتسليم. وقد يؤدي ذلك إلى أن يرسلcollectorدفعات صغيرة إلى ClickHouse. وعلى الرغم من أن هذا يُعد عيبًا، فإنه يمكن التخفيف منه باستخدامAsynchronous inserts— انظر تحسينinserts.