Tracing In Distributed Systems

در معماری میکروسرویس بر خلاف معماری مونولیتیک ما تعداد زیادی سرویس داریم که برخی از آن ها برای رفع نیاز هایشان از طریق sdk ها با هم در ارتباط هستند. در صورت بروز مشکل(ارور) در یکی از endpoint ها ، ما برای بر طرف کردن مشکل نیاز به این مورد داریم که بدانیم در کدام سرویس مشکل به وجود آمده است این جاست که ابزار های tracing به کمک ما می آیند و با نشان دادن life cycle یا مسیر طی شده توسط ریکویست ، سرویس دچار مشکل به راحتی مشخص میشود.البته این موضوع از طریق چک کردن لاگ سرویس ها هم قابل حل هست اما tracing راه حل آسانتر و سریعتری هست.Jaeger یکی از محبوب ترین ابزار های tracing هست در این مقاله به معماری آن اشاره میکنیم.در jaeger چند تعریف مهم وجود دارد .مورد اول trace نام دارد که در اصل مسیر ریکویست ما را نشان میدهد.مورد دوم span نام دارد که در واقع لاگ های مسیر را نشان میدهد . میتوان گفت که هر trace از تعدادی span تشکیل شده است که به تعبیر دیگر اگر مسیر ریکویست را یک گراف در نظر بگیریم که در حالات مختلف به مسیر های مختلف دارد span ها مانند راس های گراف هستند و trace هم در اصل خود گراف هست.مفهوم دیگر context ها هستند که در واقع در داخل span قرار دارند. هر context تشکیل شده است از span id ,trace id مورد دیگری به نام baggage که حاوی یک سری اطلاعات اضافه تر هست که توسط سرویس قابل اضافه و کم شدن هست.حال که با مفاهیم اولیه در jaeger آشنا شدیم به سراغ معماری آن میرویم.همان طور که در تصویر میبینید jaeger از قسمت های مختلفی تشکیل شده است. برای استفاده از jaeger همواره باید بر روی سرویس خود با استفاده از sdk مناسب (با توجه به زبان برنامه نویسی استفاده شده) یک jaeger client داشته باشید. با استفاده از jaeger client باید trace , span ها را برای ارتباط با jaeger-agent ایجاد کنید . jaeger-agent در این مرحله span ها را تحت پروتکل udp از jaeger-client دریافت میکند سپس اطلاعات دریافت شده را به jaeger-collector میفرستد .jaeger-collector ابتدا نقش ولیدیتور را ایفا میکند و ولید بودن اطلاعات را بررسی میکند سپس آن را به storage میفرستد در این مرحله از message broker ها هم استفاده کرد که این موضوع هم پیشنهاد میشود تا دیتا های jaeger-collector با اطمینان خاطر حتما به storage برسند. storage استفاده شده توسط jaeger قابل تغییر (configable) هست. سپس jaeger-query دیتا ها را از storage دریافت میکند و به ui میفرستد و در ui نمایش داده میشوند.**موارد مهم**برای آنکه تمامی trace ها به jaeger-agent فرستاده نشود میتوانید با استفاده از کانفیگ های JAEGER_SAMPLER_TYPE , JAEGER_SAMPLER_PARAM آن را کنترل کنید به این صورت که برای مثال اگر تعداد span ها زیاد بود میتوانید با ست کردن آن بر روی حالت probabilistic و ست کردن param بر روی یک مقدار کم مانند 0.01 تعداد کمتری از span ها را به jaeger-agent بفرستید(شرایط مثال به این معنا هست که به احتمال 1 درصد span فرستاده میشود) دقت کنید که برای span های یک trace به صورت واحد تصمیم گیری میشود یعنی یا همه ی آن ها به jaeger-agent فرستاده میشوند یا هیچ کدام ارسال نمیشوند.دقت کنید که در jaeger-agent و jaeger-collector یک صف(queue) وجود دارد که span ها ابتدا در آن ذخیره می شوند سپس برای ادامه روند به قسمت بعدی فرستاده میشوند در حالت عادی (default) این صف در jaeger-agent قابلیت ذخیره 1000 و در jaeger-collector قابلیت ذخیره 2000 span را دارد اما آن را میتوانید با استفاده از کانفیگ ها تغییر دهید و مقدار آن را افزایش یا کاهش دهید.دقت کنید در حالت کلی بهتر هست jaeger-agent و jaeger-client روی یک هاست باشند یا اگر از jaeger-client بر روی چند bare metal استفاده میکنید بر روی هر کدام یک jaeger-agent داشته باشید و تا حد امکان span ها مسیر کمتری را بر رسیدن به jaeger-agent طی کنند . دلیل این موضوع هم این هست که span ها تحت پروتکل udp به jaeger-agent فرستاده میشوند که سرعت بالایی دارد اما در این پروتکل تضمینی وجود ندارد که دیتا ها لزوما به مقصد رسیده اند.دقت کنید در حالت کلی با استفاده از کانفیگ ها میتوان طوری سناریو ها را تنظیم کرد که در آن دیتا ها از jaeger-client به طور مستقیم به jaeger-collector فرستاده شوند اما در scale بالا و در زمانی که تعداد ریکویست ها زیاد هست این عمل پیشنهاد نمیشود.این هم آدرس ریپوزیتوری jaeger هست : https://github.com/jaegertracing/jaegerموفق باشید