امروزه، تصور پروژه مبتنی بر Kubernetes بدون پشته ELK غیرممکن است، که لاگ برنامهها و اجزای سیستم خوشه را ذخیره میکند. در عمل ما از پشته EFK با Fluentd به جای Logstash استفاده می کنیم.
فلوئنتد یک جمعآوری سیاههنگام مدرن و جهانی است که روز به روز محبوبیت بیشتری پیدا میکند و به بنیاد محاسبات بومی Cloud ملحق شده است، به همین دلیل است که بردار توسعه آن بر روی استفاده در ارتباط با Kubernetes متمرکز شده است.
واقعیت استفاده از Fluentd به جای Logstash، جوهر کلی بسته نرم افزاری را تغییر نمی دهد، با این حال، Fluentd با تفاوت های ظریف خاص خود که ناشی از تطبیق پذیری آن است مشخص می شود.
به عنوان مثال، زمانی که شروع به استفاده از EFK در یک پروژه پرمشغله با شدت لاگ بالا کردیم، با این واقعیت مواجه شدیم که در کیبانا برخی از پیام ها چندین بار به طور مکرر نمایش داده می شوند. در این مقاله به شما خواهیم گفت که چرا این پدیده رخ می دهد و چگونه مشکل را حل کنید.
مشکل کپی سند
در پروژههای ما، Fluentd بهعنوان یک DaemonSet (به طور خودکار در یک نمونه در هر گره از خوشه Kubernetes راهاندازی میشود) و لاگهای کانتینر stdout را در /var/log/containers نظارت میکند. پس از جمعآوری و پردازش، گزارشها در قالب اسناد JSON به ElasticSearch ارسال میشوند که به صورت خوشهای یا مستقل، بسته به مقیاس پروژه و الزامات عملکرد و تحمل خطا، جمعآوری میشوند. Kibana به عنوان رابط گرافیکی استفاده می شود.
هنگام استفاده از Fluentd با یک افزونه بافر خروجی، با وضعیتی مواجه شدیم که برخی از اسناد در ElasticSearch دقیقاً محتوای مشابهی داشتند و فقط در شناسه تفاوت داشتند. می توانید با استفاده از گزارش Nginx به عنوان مثال تأیید کنید که این یک تکرار پیام است. در فایل گزارش، این پیام در یک نسخه وجود دارد:
علاوه بر این، ممکن است بیش از دو تکرار وجود داشته باشد.
هنگام رفع این مشکل در لاگ های Fluentd، می توانید تعداد زیادی هشدار با محتوای زیر مشاهده کنید:
2020-01-16 01:46:46 +0000 [warn]: [test-prod] failed to flush the buffer. retry_time=4 next_retry_seconds=2020-01-16 01:46:53 +0000 chunk="59c37fc3fb320608692c352802b973ce" error_class=Fluent::Plugin::ElasticsearchOutput::RecoverableRequestFailure error="could not push logs to Elasticsearch cluster ({:host=>"elasticsearch", :port=>9200, :scheme=>"http", :user=>"elastic", :password=>"obfuscated"}): read timeout reached"
این اخطارها زمانی رخ میدهند که ElasticSearch نمیتواند پاسخی به یک درخواست در مدت زمان مشخص شده توسط پارامتر request_timeout برگرداند، به همین دلیل است که قطعه بافر ارسالشده پاک نمیشود. پس از این، Fluentd دوباره سعی می کند قطعه بافر را به ElasticSearch ارسال کند و پس از تعداد دلخواه تلاش، عملیات با موفقیت به پایان می رسد:
با این حال، ElasticSearch هر یک از قطعات بافر منتقل شده را منحصربهفرد میداند و مقادیر فیلد _id منحصربهفردی را در طول نمایهسازی به آنها اختصاص میدهد. به این ترتیب کپی پیام ها ظاهر می شوند.
در کیبانا به این صورت است:
راه حل
چندین گزینه برای حل این مشکل وجود دارد. یکی از آنها مکانیسمی است که در افزونه fluent-plugin-elasticsearch برای ایجاد یک هش منحصر به فرد برای هر سند ساخته شده است. اگر از این مکانیسم استفاده کنید، ElasticSearch تکرارها را در مرحله فوروارد تشخیص داده و از تکرار اسناد جلوگیری می کند. اما باید در نظر داشته باشیم که این روش حل مشکل با بررسی مشکل دارد و خطا را با کمبود تایم اوت برطرف نمی کند، بنابراین استفاده از آن را کنار گذاشتیم.
ما از یک پلاگین بافر در خروجی Fluentd استفاده می کنیم تا در صورت بروز مشکلات کوتاه مدت شبکه یا افزایش شدت گزارش، از دست دادن گزارش جلوگیری کنیم. اگر به دلایلی ElasticSearch نتواند فوراً یک سند را در فهرست بنویسد، سند در صف قرار می گیرد و روی دیسک ذخیره می شود. بنابراین، در مورد ما، برای از بین بردن منبع مشکلی که منجر به خطای توصیف شده در بالا می شود، لازم است مقادیر صحیحی را برای پارامترهای بافر تنظیم کنیم که در آن بافر خروجی Fluentd از اندازه کافی برخوردار باشد و در همان زمان موفق به پاکسازی در زمان تعیین شده می شود.
شایان ذکر است که مقادیر پارامترهای مورد بحث در زیر در هر مورد خاص استفاده از بافر در افزونههای خروجی جداگانه است، زیرا به عوامل زیادی بستگی دارد: شدت نوشتن پیامها در گزارش توسط سرویسها، عملکرد سیستم دیسک، شبکه بار کانال و پهنای باند آن بنابراین، برای به دست آوردن تنظیمات بافر مناسب برای هر مورد، اما نه اضافی، اجتناب از جستجوهای طولانی کورکورانه، می توانید از اطلاعات اشکال زدایی که Fluentd در لاگ خود در حین کار می نویسد استفاده کنید و نسبتاً سریع مقادیر صحیح را بدست آورید.
هنگام حل مشکل، مقادیر پارامترهای زیر به صورت دستی انتخاب شدند:
chunk_limit_size - اندازه تکه هایی که پیام های بافر به آنها تقسیم می شوند.
flush_interval - فاصله زمانی که پس از آن بافر پاک می شود.
queue_limit_length - حداکثر تعداد تکه ها در صف.
request_timeout زمانی است که برای آن ارتباط بین Fluentd و ElasticSearch برقرار می شود.
اندازه کل بافر را می توان با ضرب پارامترهای queue_limit_length و chunk_limit_size محاسبه کرد، که می تواند به عنوان "حداکثر تعداد تکه ها در صف، که هر کدام اندازه مشخصی دارند" تفسیر شود. اگر اندازه بافر کافی نباشد، اخطار زیر در گزارشها ظاهر میشود:
2020-01-21 10:22:57 +0000 [warn]: [test-prod] failed to write data into buffer by buffer overflow action=:block
یعنی بافر در زمان تعیین شده زمان پاک شدن را ندارد و اطلاعاتی که وارد بافر کامل می شود مسدود می شود که منجر به از دست رفتن بخشی از لاگ ها می شود.
شما می توانید بافر را به دو روش افزایش دهید: با افزایش اندازه هر تکه در صف یا تعداد تکه هایی که می توانند در صف باشند.
اگر اندازه chunk_limit_size را روی بیش از 32 مگابایت تنظیم کنید، ElasticSeacrh آن را نمی پذیرد، زیرا بسته دریافتی بسیار بزرگ خواهد بود. بنابراین، اگر نیاز به افزایش بیشتر بافر دارید، بهتر است حداکثر طول صف queue_limit_length را افزایش دهید.
هنگامی که بافر از سرریز شدن متوقف شد و فقط پیام ناکافی مهلت زمانی باقی ماند، می توانید شروع به افزایش پارامتر request_timeout کنید. با این حال، اگر مقدار را روی بیش از 20 ثانیه تنظیم کنید، هشدارهای زیر در گزارشهای Fluentd ظاهر میشوند:
2020-01-21 09:55:33 +0000 [warn]: [test-dev] buffer flush took longer time than slow_flush_log_threshold: elapsed_time=20.85753920301795 slow_flush_log_threshold=20.0 plugin_id="postgresql-dev"
این پیام به هیچ وجه بر عملکرد سیستم تأثیر نمی گذارد و به این معنی است که زمان شستشوی بافر بیشتر از مقدار تنظیم شده توسط پارامتر slow_flush_log_threshold طول کشیده است. این اطلاعات اشکال زدایی است و هنگام انتخاب مقدار پارامتر request_timeout از آن استفاده می کنیم.
الگوریتم انتخاب تعمیم یافته به شرح زیر است:
درخواست_timeout را روی مقداری که تضمین شده است بیشتر از مقدار لازم (صدها ثانیه) باشد، تنظیم کنید. در حین راه اندازی، معیار اصلی برای تنظیم صحیح این پارامتر، ناپدید شدن هشدارهای عدم وقفه خواهد بود.
منتظر پیام هایی در مورد فراتر رفتن از آستانه slow_flush_log_threshold باشید. متن هشدار در قسمت elapsed_time زمان واقعی پاک شدن بافر را نشان می دهد.
request_timeout را روی مقداری بیشتر از حداکثر مقدار elapsed_time بدست آمده در طول دوره مشاهده تنظیم کنید. ما مقدار request_timeout را به صورت elapsed_time + 50% محاسبه می کنیم.
برای حذف هشدارهای مربوط به فلاش های طولانی بافر از گزارش، می توانید مقدار slow_flush_log_threshold را افزایش دهید. ما این مقدار را به صورت elapsed_time + 25% محاسبه می کنیم.
مقادیر نهایی این پارامترها، همانطور که قبلا ذکر شد، به صورت جداگانه برای هر مورد به دست می آید. با پیروی از الگوریتم فوق، ما تضمین می کنیم که خطاهایی را که منجر به پیام های مکرر می شود، از بین ببریم.
جدول زیر نشان می دهد که چگونه تعداد خطاها در روز که منجر به تکرار پیام ها می شود، در روند انتخاب مقادیر پارامترهای شرح داده شده در بالا تغییر می کند:
گره-1
گره-2
گره-3
گره-4
قبل و بعد از
قبل و بعد از
قبل و بعد از
قبل و بعد از
نتوانست بافر را پاک کند
1749/2
694/2
47/0
1121/2
تلاش مجدد با موفقیت انجام شد
410/2
205/1
24/0
241/2
علاوه بر این شایان ذکر است که تنظیمات حاصل ممکن است ارتباط خود را با رشد پروژه از دست بدهند و بر این اساس تعداد لاگ ها افزایش می یابد. نشانه اولیه مهلت زمانی ناکافی، بازگشت پیامهای مربوط به یک بافر طولانی به گزارش Fluentd است، یعنی فراتر از آستانه slow_flush_log_threshold. از این مرحله به بعد، هنوز یک حاشیه کمی قبل از تجاوز از پارامتر request_timeout وجود دارد، بنابراین لازم است به موقع به این پیام ها پاسخ داده شود و فرآیند انتخاب تنظیمات بهینه که در بالا توضیح داده شد تکرار شود.
نتیجه
تنظیم دقیق بافر خروجی Fluentd یکی از مراحل اصلی پیکربندی پشته EFK، تعیین پایداری عملکرد آن و قرار دادن صحیح اسناد در فهرست ها است. بر اساس الگوریتم پیکربندی توصیف شده، میتوانید مطمئن باشید که همه گزارشها به ترتیب صحیح، بدون تکرار یا از دست دادن، در فهرست ElasticSearch نوشته میشوند.