به گزارش از وبسایت cncf 😊

سرویس‌مش‌هایی مانند Istio همراه با Kiali در روزهای اول استفاده، مقدار زیادی قابلیت مشاهده در اختیار شما می‌گذارند. مش را نصب می‌کنید، Prometheus را به سمت آن نشانه می‌برید و ناگهان نرخ درخواست، تأخیر، نرخ خطا و تصویری کلی از ترافیک سرویس‌به‌سرویس خواهید داشت؛ بدون اینکه نیازی به تغییر کد برنامه‌ها باشد. این سطح از رؤیت‌پذیری اغلب برای پاسخ به اولین پرسش‌ها در زمان وقوع حادثه کافی است.

با این حال، وقتی همه‌چیز را با OpenTelemetry ابزارسنجی می‌کنید و به اجزای مختلف اکوسیستم CNCF از جمله Istio، Prometheus، لاگ‌ها و ابزارسنجی درون برنامه‌ها متصل می‌شوید، گاهی نمی‌توانید «نقاط» را به هم وصل کنید. در عمل وقتی به رابط ردیابی نگاه می‌کنید، می‌بینید که ردیابی‌ها از هم جدا شده‌اند و بخش‌هایی که باید جزء یک تراکنش باشند، در دو یا چند ردپای جداگانه قرار می‌گیرند؛ یعنی ردپای شما شکسته می‌شود و این موضوع بسیار ناامیدکننده است، زیرا هر جزء به‌تنهایی درست کار می‌کند.

پس از نصب Istio، Prometheus و سایر ابزارها، ممکن است در پایان همچنان تصویری منسجم نداشته باشید: درخواست‌ها و دامنه‌ها وجود دارند، اما تله‌متری به‌گونه‌ای به هم نمی‌رسد که تصویر یکپارچه‌ای بسازد. با این حال هنوز امید هست — و در واقع OpenTelemetry می‌تواند کمک عملی و استانداردی برای اتصال همه قطعات ارائه دهد.

برای روشن شدن مشکل و نحوه حل آن، فرض کنیم یک راه‌اندازی مشابه OpenTelemetry Demo داریم: برنامه‌هایی که با OpenTelemetry ابزارسنجی شده‌اند و یک OpenTelemetry Collector و Jaeger نیز در میان هستند. در این حالت ردیابی‌ها به‌خوبی کار می‌کنند. اما وقتی یک سرویس‌مش روی این تنظیمات نصب می‌شود و Envoy در مسیر قرار می‌گیرد، معیارها و لاگ‌ها همچنان مفید می‌مانند ولی ردیابی‌ها اغلب شکسته می‌شوند.

در این مطلب قصد داریم نشان دهیم چگونه با ایجاد هماهنگی میان مش و ابزارسنجی خودکار برنامه‌ها می‌توان مشکل یکپارچه‌سازی را حل کرد. بخش جالب تنها این نیست که ردیابی‌ها خراب می‌شوند؛ بخش جالب این است که چرا این اتفاق می‌افتد و چگونه OpenTelemetry یک راه‌حل عملی ارائه می‌دهد.

آنچه Istio فعلاً به شما می‌دهد بسیار ارزشمند است: پس از تزریق sidecarها، شما می‌بینید حجم درخواست بین سرویس‌ها، تأخیر، نرخ خطا، و توپولوژی از طریق گزارش‌های دسترسی Kiali. برای تیم‌های پلتفرم این اطلاعات فوراً مفید است؛ می‌توانید بفهمید ترافیک جریان دارد یا خیر، خطاها کجا متمرکز شده‌اند و چه پرشی ممکن است تأخیر اضافه کند. اما همچنان بین «شبکه ظاهراً ناسالم است» و «این درخواست کار نکرد چون فراخوانی خاصی دوبار رخ داده» شکاف وجود دارد — و این شکاف معمولاً با tracing بسته می‌شود. در نتیجه، جزئیات ادغام بین مش و ابزارسنجی برنامه اهمیت پیدا می‌کند.

در نسخه نمایشی OpenTelemetry، برنامه‌ها قبلاً با OpenTelemetry SDK ابزارسنجی شده‌اند و پیش از نصب Istio، Jaeger ردپای کامل از فراخوانی‌ها را نمایش می‌داد. اما بعد از افزودن sidecarهای Istio و فعال شدن tracing در Envoy، هنوز داده‌هایی وجود دارد اما یکپارچه نیستند: در Jaeger ممکن است دو نسخه از یک سرویس را ببینید — یکی از SDK داخل برنامه و دیگری از Envoy sidecar. در نگاه اول به نظر می‌رسد که اطلاعات بیشتری دارید، اما در عمل دو درخت ردیابی نامرتبط برای یک درخواست شکل می‌گیرد؛ مشکل اصلی یکپارچه‌سازی همین است.

ریشه این مشکل به نحوه propagation (انتشار متن ردیابی) بازمی‌گردد. برنامه‌ها با OpenTelemetry SDK گستره‌ها (spans) را منتشر می‌کنند و زمینه ردیابی را با هدرهای استاندارد W3C Trace Context منتقل می‌کنند. اما وقتی Envoy هم شروع به تولید spans می‌کند، اگر Envoy متن ورودی را استخراج نکند و همان trace را ادامه ندهد، یک root span جدید ایجاد می‌شود. در نتیجه دو روایت از یک درخواست شکل می‌گیرد: یکی مبتنی بر منطق داخلی برنامه و دیگری مبتنی بر پرش‌های شبکه و رفتار proxy. هیچ‌یک به‌تنهایی کافی نیست؛ برای اینکه همه‌چیز با هم کار کند، نیاز به یک نقطه ادغام و توافق بر سر فرمت‌های انتشار هست.

دو تغییر عملی برای رفع مشکل وجود دارد. اول اینکه OpenTelemetry Collector باید به‌عنوان نقطه‌ای که مش و برنامه‌ها در آن به هم می‌رسند در نظر گرفته شود: به‌جای اینکه Collector تنها یک هدف صادراتی دیگر باشد، باید فرمت‌هایی را بپذیرد که Envoy می‌تواند برای استخراج متن ردیابی استفاده کند. دوم اینکه برنامه‌ها و مش باید بر سر هدرها توافق داشته باشند؛ اگر برنامه یک فرمت و مش فرمت دیگری را انتظار داشته باشد، ردیابی تقسیم می‌شود. در این مورد، راه حل عملی افزودن انتشار B3 توسط برنامه‌ها در کنار tracecontext و baggage بود، تا Envoy بتواند از B3 برای ادامه ردیابی موجود استفاده کند و به‌جای شروع یک روت جدید، به ردیابی جاری بپیوندد.

از منظر پیکربندی Istio، ابتدا باید مطمئن شویم Envoy تمام spans را به Collector صادر می‌کند، و سرویس/پورت درست تعریف شده باشد. یک نمونه پیکربندی IstioOperator که Collector را معرفی می‌کند می‌تواند شبیه این باشد:

apiVersion: install.istio.io/v1alpha1
kind: IstioOperator
spec:
  meshConfig:
    extensionProviders:
    - name: otel-tracing
      opentelemetry:
        service: otel-collector.otel-demo.svc.cluster.local:4317

قدم عملی دیگر از سوی کاربر، تغییر tracer داخلی Envoy به Zipkin است. tracer پیش‌فرض OTel Envoy برای هر درخواست یک root span جدید ایجاد می‌کرد و هدر W3C را نمی‌خواند، در حالی که Zipkin (B3) متن B3 را استخراج کرده و زیر یک ردیابی موجود دامنه‌های فرزند ایجاد می‌کند. در همان meshConfig باید tracing را فعال و Zipkin را مشخص کنیم، مثلاً:

enableTracing: true
defaultConfig:
  tracing:
    zipkin:
      address: otel-collector.otel-demo.svc.cluster.local:9411

نیمه دوم حل مسئله از سمت برنامه است: باید انتشار B3 را نیز فعال کنید. در نسخه نمایشی OpenTelemetry، افزودن b3multi در کنار tracecontext و baggage به مقادیر منتشرشده توسط SDK کافی بود تا Envoy بتواند همان ردیابی را ادامه دهد به‌جای اینکه ردیابی جدیدی بسازد.

پس از اعمال این تغییرات، به‌جای دیدن دو ردپای جداگانه (یکی برای برنامه و دیگری برای sidecar)، همه spans در یک ردیابی واحد با هم دیده می‌شوند که هر دو نوع span را دربر می‌گیرد. در مثال عملی، به‌جای خوشه‌های ~14 و 2 span، سرویس پرداخت اکنون بین 51 تا 75 span را در یک ردیابی می‌بیند؛ این تفاوت بین «ما ردیابی داریم» و «می‌توانیم یک درخواست را اشکال‌زدایی کنیم» است.

چرا Collector در اینجا نقش مرکزی دارد؟ چون تولید شامل نرم‌افزارهای متنوع با نسخه‌ها و پیش‌فرض‌های مختلف است. حتی اگر برنامه‌ها، Collector و Jaeger به‌درستی پیکربندی شوند، ردیابی‌ها ممکن است به خاطر یک جزء که رفتار متفاوتی دارد شکسته شوند. در مورد ما، tracer داخلی OTel Envoy به‌صورت پیش‌فرض متن ورودی را نمی‌خواند و یک root جدید برای هر درخواست می‌ساخت؛ تعویض به Zipkin/B3 تنها نیمی از راه حل بود، زیرا همچنان نیاز به مکانی بود که دهانه‌هایی که به فرمت‌های مختلف می‌رسند را به یک زبان ترجمه کند. OpenTelemetry Collector همین کار را انجام می‌دهد: تله‌متری را در قالب‌های متعدد دریافت، به مدل OpenTelemetry نرمالایز و سپس به یک یا چند Backend صادر می‌کند.

این الگو فراتر از tracing است: Collector می‌تواند ترجمه قراردادها، نمونه‌برداری، کنترل مرکزی، تولید metrics از spans، ویرایش داده‌ها و صادرات همزمان به چندین مقصد را مدیریت کند. Istio یک سیستم قدرتمند است که routing، گردش گواهی و قابلیت مشاهده را تقریباً بدون پیکربندی فراهم می‌کند؛ اما برای داشتن یک تصویر کامل از درون درخواست‌ها، هماهنگی میان مش، ابزارسنجی برنامه و Collector لازم است — و OpenTelemetry این زنجیره را قابل اعتماد و عملی می‌سازد. ✨