السجلات في Kubernetes (وليس فقط) اليوم: التوقعات والواقع

السجلات في Kubernetes (وليس فقط) اليوم: التوقعات والواقع

نحن في عام 2019، وما زلنا لا نملك حلاً قياسيًا لتجميع السجلات في Kubernetes. في هذه المقالة، نود، باستخدام أمثلة من الممارسة الحقيقية، أن نشارك عمليات البحث التي أجريناها والمشكلات التي واجهناها وحلولها.

ومع ذلك، أولاً، سأحجز أن العملاء المختلفين يفهمون أشياء مختلفة جدًا من خلال جمع السجلات:

  • يريد شخص ما رؤية سجلات الأمان والتدقيق؛
  • شخص ما - التسجيل المركزي للبنية التحتية بأكملها؛
  • وبالنسبة للبعض، يكفي جمع سجلات التطبيق فقط، باستثناء الموازنات، على سبيل المثال.

فيما يلي المقطع أدناه حول كيفية تنفيذ "قوائم الرغبات" المختلفة وما هي الصعوبات التي واجهناها.

النظرية: حول أدوات التسجيل

خلفية عن مكونات نظام التسجيل

لقد قطع التسجيل شوطا طويلا، ونتيجة لذلك تم تطوير منهجيات جمع وتحليل السجلات، وهو ما نستخدمه اليوم. في الخمسينيات من القرن الماضي، قدم فورتران نظيرًا لتدفقات الإدخال/الإخراج القياسية، مما ساعد المبرمج على تصحيح أخطاء برنامجه. كانت هذه هي سجلات الكمبيوتر الأولى التي جعلت الحياة أسهل للمبرمجين في تلك الأوقات. اليوم نرى فيها المكون الأول لنظام التسجيل - مصدر أو "منتج" السجلات.

علوم الكمبيوتر لم تقف مكتوفة الأيدي: ظهرت شبكات الكمبيوتر، المجموعات الأولى. بدأت أنظمة معقدة تتكون من عدة أجهزة كمبيوتر في العمل. الآن، اضطر مسؤولو النظام إلى جمع السجلات من عدة أجهزة، وفي حالات خاصة يمكنهم إضافة رسائل kernel لنظام التشغيل في حالة الحاجة إلى التحقيق في فشل النظام. لوصف أنظمة جمع السجلات المركزية، تم نشره في أوائل العقد الأول من القرن الحادي والعشرين RFC 3164، الذي قام بتوحيد Remote_syslog. هكذا ظهر عنصر مهم آخر: جامع السجل وتخزينها.

مع زيادة حجم السجلات والإدخال الواسع النطاق لتقنيات الويب، نشأ السؤال حول السجلات التي يجب عرضها بسهولة للمستخدمين. تم استبدال أدوات وحدة التحكم البسيطة (awk/sed/grep) بأدوات أكثر تقدمًا سجل المشاهدين - المكون الثالث .

نظرًا للزيادة في حجم السجلات، أصبح هناك شيء آخر واضح: هناك حاجة إلى سجلات، ولكن ليس كلها. وتتطلب السجلات المختلفة مستويات مختلفة من الحفظ: يمكن فقدان بعضها في يوم واحد، بينما يحتاج البعض الآخر إلى تخزينه لمدة 5 سنوات. لذلك، تمت إضافة مكون لتصفية وتوجيه تدفقات البيانات إلى نظام التسجيل - دعنا نسميها منقي.

حقق التخزين أيضًا قفزة كبيرة: من الملفات العادية إلى قواعد البيانات العلائقية، ثم إلى التخزين الموجه نحو المستندات (على سبيل المثال، Elasticsearch). لذلك تم فصل المخزن عن المجمع.

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

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

السجلات في Kubernetes (وليس فقط) اليوم: التوقعات والواقع
إذا كانت المطبوعات العادية ذات يوم كافية لـ "نظام التسجيل"، فقد تغير الوضع الآن كثيرًا.

Kubernetes والسجلات

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

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

  • شخص ما يفتح المكدس SFAO (Elasticsearch، Fluentd، Kibana)؛
  • هناك من يحاول إصداره مؤخرًا لوكي أو الاستخدامات عامل التسجيل;
  • لنا (وربما ليس نحن فقط؟..) أنا راض إلى حد كبير عن تطوري - منزل السجل...

كقاعدة عامة، نستخدم الحزم التالية في مجموعات K8s (للحلول ذاتية الاستضافة):

ومع ذلك، لن أسهب في الحديث عن تعليمات التثبيت والتكوين. وبدلاً من ذلك، سأركز على أوجه القصور والاستنتاجات الأكثر عالمية حول الوضع مع السجلات بشكل عام.

تدرب مع السجلات في K8s

السجلات في Kubernetes (وليس فقط) اليوم: التوقعات والواقع

“سجلات كل يوم” كم منكم هناك؟..

يتطلب التجميع المركزي للسجلات من بنية تحتية كبيرة إلى حد ما موارد كبيرة، والتي سيتم إنفاقها على جمع السجلات وتخزينها ومعالجتها. أثناء تشغيل المشاريع المختلفة، واجهنا متطلبات مختلفة ومشاكل تشغيلية ناشئة عنها.

دعونا نجرب ClickHouse

دعونا نلقي نظرة على وحدة تخزين مركزية في مشروع باستخدام تطبيق يقوم بإنشاء سجلات بنشاط كبير: أكثر من 5000 سطر في الثانية. لنبدأ العمل مع سجلاته، وإضافتها إلى ClickHouse.

بمجرد الحاجة إلى الحد الأقصى من الوقت الفعلي، سيتم تحميل الخادم رباعي النواة المزود بـ ClickHouse بالفعل على النظام الفرعي للقرص:

السجلات في Kubernetes (وليس فقط) اليوم: التوقعات والواقع

يرجع هذا النوع من التحميل إلى حقيقة أننا نحاول الكتابة في ClickHouse في أسرع وقت ممكن. وتتفاعل قاعدة البيانات مع ذلك من خلال زيادة تحميل القرص، مما قد يسبب الأخطاء التالية:

DB::Exception: Too many parts (300). Merges are processing significantly slower than inserts

الحقيقة هي أن جداول MergeTree في ClickHouse (التي تحتوي على بيانات السجل) تواجه صعوباتها الخاصة أثناء عمليات الكتابة. تقوم البيانات المُدرجة فيها بإنشاء قسم مؤقت، والذي يتم بعد ذلك دمجه مع الجدول الرئيسي. ونتيجة لذلك، أصبح التسجيل متطلبًا للغاية على القرص، كما أنه يخضع أيضًا للقيود التي تلقينا إشعارًا بشأنها أعلاه: لا يمكن دمج أكثر من 1 قسم فرعي في ثانية واحدة (في الواقع، هذا هو 300 إدراج) في الثانية).

ولتجنب هذا السلوك، يجب أن يكتب إلى ClickHouse في قطع كبيرة قدر الإمكان وليس أكثر من مرة واحدة كل ثانيتين. ومع ذلك، فإن الكتابة على دفعات كبيرة تشير إلى أنه يجب علينا الكتابة بشكل أقل في ClickHouse. وهذا بدوره يمكن أن يؤدي إلى تجاوز سعة المخزن المؤقت وفقدان السجلات. الحل هو زيادة المخزن المؤقت Fluentd، ولكن بعد ذلك سيزداد استهلاك الذاكرة أيضًا.

لاحظ: كان الجانب الإشكالي الآخر لحلنا مع ClickHouse يتعلق بحقيقة أن التقسيم في حالتنا (loghouse) يتم تنفيذه من خلال جداول خارجية متصلة دمج الجدول. يؤدي هذا إلى حقيقة أنه عند أخذ عينات من فترات زمنية كبيرة، يلزم وجود ذاكرة وصول عشوائي زائدة، نظرًا لأن الجدول التعريفي يتكرر عبر جميع الأقسام - حتى تلك التي من الواضح أنها لا تحتوي على البيانات الضرورية. ومع ذلك، يمكن الآن الإعلان عن أن هذا الأسلوب قد أصبح قديمًا بالنسبة للإصدارات الحالية من ClickHouse (c 18.16).

ونتيجة لذلك، يصبح من الواضح أنه ليس كل مشروع لديه ما يكفي من الموارد لجمع السجلات في الوقت الحقيقي في ClickHouse (بتعبير أدق، لن يكون توزيعها مناسبا). وبالإضافة إلى ذلك، سوف تحتاج إلى استخدام بطارية، والذي سنعود إليه لاحقاً. الحالة الموصوفة أعلاه حقيقية. وفي ذلك الوقت لم نتمكن من تقديم حل موثوق ومستقر يناسب العميل ويسمح لنا بجمع السجلات بأقل قدر من التأخير...

ماذا عن Elasticsearch؟

من المعروف أن Elasticsearch يتعامل مع أعباء العمل الثقيلة. دعونا نحاول ذلك في نفس المشروع. الآن يبدو التحميل هكذا:

السجلات في Kubernetes (وليس فقط) اليوم: التوقعات والواقع

تمكنت Elasticsearch من استيعاب دفق البيانات، ومع ذلك، فإن كتابة مثل هذه المجلدات إليها تستخدم وحدة المعالجة المركزية بشكل كبير. يتم تحديد ذلك من خلال تنظيم المجموعة. من الناحية الفنية، هذه ليست مشكلة، ولكن اتضح أنه لتشغيل نظام جمع السجلات فقط، فإننا نستخدم بالفعل حوالي 8 مراكز ولدينا مكون إضافي عالي التحميل في النظام...

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

ومن ثم يطرح سؤال طبيعي:

ما هي السجلات المطلوبة حقا؟

السجلات في Kubernetes (وليس فقط) اليوم: التوقعات والواقع دعونا نحاول تغيير النهج نفسه: يجب أن تكون السجلات مفيدة في نفس الوقت وليست تغطية كل حدث في النظام.

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

لذلك وصلنا إلى نتيجة مفادها أن التسجيل المركزي ليس له ما يبرره دائمًا. في كثير من الأحيان، يرغب العميل في جمع كل السجلات في مكان واحد، على الرغم من أنه في الواقع، من السجل بأكمله، لا يلزم سوى 5٪ مشروطة من الرسائل المهمة للأعمال:

  • في بعض الأحيان يكفي تكوين حجم سجل الحاوية ومجمع الأخطاء (على سبيل المثال، Sentry) فقط.
  • غالبًا ما يكون إشعار الخطأ والسجل المحلي الكبير كافيين للتحقيق في الحوادث.
  • كانت لدينا مشاريع تتعلق بالاختبارات الوظيفية وأنظمة جمع الأخطاء بشكل حصري. لم يكن المطور بحاجة إلى سجلات في حد ذاتها - فقد رأوا كل شيء بدءًا من آثار الأخطاء.

رسم توضيحي من الحياة

قصة أخرى يمكن أن تكون بمثابة مثال جيد. لقد تلقينا طلبًا من فريق الأمان الخاص بأحد عملائنا الذي كان يستخدم بالفعل حلاً تجاريًا تم تطويره قبل فترة طويلة من تقديم Kubernetes.

كان من الضروري "تكوين صداقات" لنظام جمع السجلات المركزي باستخدام مستشعر اكتشاف المشكلات الخاص بالشركة - QRadar. يمكن لهذا النظام استلام السجلات عبر بروتوكول syslog واسترجاعها من FTP. ومع ذلك، لم يكن من الممكن على الفور دمجه مع البرنامج المساعد Remote_syslog لـ Fluentd (كما اتضح، نحن لسنا وحدنا). تبين أن المشكلات المتعلقة بإعداد QRadar كانت من جانب فريق الأمان الخاص بالعميل.

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

مثال آخر يدل تمامًا على ما لا يجب فعله. أحد عملائنا للتجهيز كل الأحداث القادمة من المستخدم، مصنوعة متعددة الأسطر إخراج غير منظم المعلومات في السجل. كما قد تتخيل، كانت هذه السجلات غير ملائمة للغاية للقراءة والتخزين.

معايير السجلات

تؤدي مثل هذه الأمثلة إلى استنتاج مفاده أنه بالإضافة إلى اختيار نظام جمع السجلات، فإنك تحتاج إلى ذلك قم أيضًا بتصميم السجلات نفسها! ما هي المتطلبات هنا؟

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

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

2019-10-29 13:10:43 +0000 [warn]: dump an error event: error_class=Fluent::Plugin::ElasticsearchErrorHandler::ElasticsearchError error="400 - Rejected by Elasticsearch"

يعني الخطأ أنك ترسل حقلاً نوعه غير مستقر إلى الفهرس باستخدام تعيين جاهز. أبسط مثال هو حقل في سجل nginx يحتوي على متغير $upstream_status. يمكن أن تحتوي على رقم أو سلسلة. على سبيل المثال:

{ "ip": "1.2.3.4", "http_user": "-", "request_id": "17ee8a579e833b5ab9843a0aca10b941", "time": "29/Oct/2019:16:18:57 +0300", "method": "GET", "uri": "/staffs/265.png", "protocol": "HTTP/1.1", "status": "200", "body_size": "906", "referrer": "https://example.com/staff", "user_agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/78.0.3904.70 Safari/537.36", "request_time": "0.001", "cache_status": "-", "upstream_response_time": "0.001, 0.007", "upstream_addr": "127.0.0.1:9000", "upstream_status": "200", "upstream_response_length": "906", "location": "staff"}
{ "ip": "1.2.3.4", "http_user": "-", "request_id": "47fe42807f2a7d8d5467511d7d553a1b", "time": "29/Oct/2019:16:18:57 +0300", "method": "GET", "uri": "/staff", "protocol": "HTTP/1.1", "status": "200", "body_size": "2984", "referrer": "-", "user_agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/78.0.3904.70 Safari/537.36", "request_time": "0.010", "cache_status": "-", "upstream_response_time": "0.001, 0.007", "upstream_addr": "10.100.0.10:9000, 10.100.0.11:9000", "upstream_status": "404, 200", "upstream_response_length": "0, 2984", "location": "staff"}

تُظهر السجلات أن الخادم 10.100.0.10 استجاب بخطأ 404 وتم إرسال الطلب إلى مخزن محتوى آخر. ونتيجة لذلك، أصبحت القيمة في السجلات كما يلي:

"upstream_response_time": "0.001, 0.007"

هذا الوضع شائع جدًا لدرجة أنه يستحق فصلاً منفصلاً المراجع في الوثائق.

ماذا عن الموثوقية؟

هناك أوقات تكون فيها كافة السجلات دون استثناء حيوية. ومع هذا، فإن مخططات جمع السجلات النموذجية لـ K8s المقترحة/التي تمت مناقشتها أعلاه بها مشاكل.

على سبيل المثال، لا يمكن لـfluentd جمع السجلات من الحاويات قصيرة العمر. في أحد مشاريعنا، عاشت حاوية ترحيل قاعدة البيانات لمدة تقل عن 4 ثوانٍ ثم تم حذفها - وفقًا للتعليق التوضيحي المقابل:

"helm.sh/hook-delete-policy": hook-succeeded

ولهذا السبب، لم يتم تضمين سجل تنفيذ الترحيل في وحدة التخزين. يمكن للسياسة أن تساعد في هذه الحالة. before-hook-creation.

مثال آخر هو تدوير سجل Docker. لنفترض أن هناك تطبيقًا يكتب بنشاط في السجلات. في ظل الظروف العادية، نتمكن من معالجة جميع السجلات، ولكن بمجرد ظهور مشكلة - على سبيل المثال، كما هو موضح أعلاه بتنسيق غير صحيح - تتوقف المعالجة، ويقوم Docker بتدوير الملف. والنتيجة هي احتمال فقدان السجلات المهمة للأعمال.

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

وأخيرا، يجب ألا ننسى ذلك من المهم مراقبة أي نظام فرعي بشكل صحيح. بخلاف ذلك، فمن السهل الوقوع في موقف يكون فيه Fluentd في حالة ما CrashLoopBackOff ولا يرسل أي شيء، وهذا يعد بفقدان معلومات مهمة.

النتائج

في هذه المقالة، نحن لا ننظر إلى حلول SaaS مثل Datadog. لقد تم بالفعل حل العديد من المشكلات الموصوفة هنا بطريقة أو بأخرى من قبل الشركات التجارية المتخصصة في جمع السجلات، ولكن لا يمكن للجميع استخدام SaaS لأسباب مختلفة (أهمها التكلفة والامتثال لـ 152-FZ).

تبدو عملية جمع السجلات المركزية للوهلة الأولى وكأنها مهمة بسيطة، ولكنها ليست كذلك على الإطلاق. من المهم أن نتذكر أن:

  • يجب تسجيل المكونات المهمة فقط بالتفصيل، بينما يمكن تكوين المراقبة وجمع الأخطاء للأنظمة الأخرى.
  • يجب أن تظل سجلات الإنتاج في حدها الأدنى حتى لا يتم إضافة حمل غير ضروري.
  • يجب أن تكون السجلات قابلة للقراءة آليًا، وموحدة، ولها تنسيق صارم.
  • يجب إرسال السجلات المهمة حقًا في دفق منفصل، والذي يجب فصله عن السجلات الرئيسية.
  • يجدر التفكير في مُراكم السجل، والذي يمكن أن ينقذك من رشقات الحمل العالية ويجعل الحمل على وحدة التخزين أكثر اتساقًا.

السجلات في Kubernetes (وليس فقط) اليوم: التوقعات والواقع
هذه القواعد البسيطة، إذا تم تطبيقها في كل مكان، ستسمح للدوائر الموصوفة أعلاه بالعمل - على الرغم من أنها تفتقد مكونات مهمة (البطارية). إذا لم تلتزم بهذه المبادئ، فستقودك المهمة بسهولة أنت والبنية التحتية إلى مكون آخر عالي التحميل (وفي نفس الوقت غير فعال) من النظام.

PS

اقرأ أيضًا على مدونتنا:

المصدر: www.habr.com

إضافة تعليق