OpenTelemetry Tail Sampling بالعربي: احتفظ بالأخطاء وقلل تكلفة الـ traces
هتعرف هنا إزاي تقلل حجم الـ traces المرسلة للـ backend بدون ما ترمي الأخطاء والطلبات البطيئة اللي محتاجها وقت التحقيق.
مستوى القارئ: متوسط
المشكلة باختصار
لو عندك microservices بتنتج 800 trace في الثانية، وإنت بتبعت 100% من الـ traces إلى Grafana Tempo أو Jaeger أو أي backend مدفوع، فأنت بتدفع على طلبات سليمة مش هتفتحها غالبًا. الطريقة الشائعة هي head sampling من داخل التطبيق. دي بتفشل في نقطة مهمة: القرار بيتاخد في أول الطلب، قبل ما تعرف هل الطلب هيطلع error أو هيعدي 5 ثواني.
Tail Sampling بيأخر القرار لآخر الـ trace تقريبًا. المعنى البسيط: بدل ما تسأل البوابة في أول الدخول “هحتفظ بالزائر ده ولا لأ؟”، تستنى تشوف الرحلة كاملة. لو حصل خطأ في الدفع أو latency عالي، تحتفظ بالرحلة. لو الرحلة عادية، تحتفظ بنسبة صغيرة فقط.
الفكرة الأساسية: القرار بعد ما الصورة تبان
OpenTelemetry Collector فيه processor اسمه tail_sampling. حسب توثيق OpenTelemetry، الـ processors بتغير أو ترشح بيانات القياس داخل pipeline، والـ Tail Sampling Processor موجود في توزيعات contrib وK8s للـ traces. المصدر: OpenTelemetry processors documentation.
الإعدادات المهمة هنا ثلاثة: decision_wait، وnum_traces، وexpected_new_traces_per_sec. توثيق الـ processor يوضح إن decision_wait هو وقت الانتظار قبل قرار العينة، وnum_traces هو عدد الـ traces المحفوظة في الذاكرة، وexpected_new_traces_per_sec يساعد في تجهيز هياكل البيانات. المصدر: Tail Sampling Processor README.
ركز في الرقم ده: لو عندك 800 trace/sec وdecision_wait: 20s، فأنت محتاج تحتفظ تقريبًا بـ 16,000 trace في الذاكرة قبل اتخاذ القرار. ضيف هامش 25% وخلي num_traces حوالي 20,000. الافتراض إن متوسط trace عندك صغير، مثل 8 إلى 15KB، وإن الـ Collector عنده ذاكرة كافية.
إعداد عملي للـ Collector
السيناريو الواقعي: متجر إلكتروني عنده 50K زائر يوميًا. أغلب الطلبات سليمة، لكن وقت الضغط يحصل خطأ في checkout أو طلبات تعدي 3 ثواني. المطلوب: احتفظ بكل الأخطاء، وكل الطلبات الأبطأ من 3000ms، واحتفظ بـ 2% من باقي الطلبات للمراقبة العامة.
receivers:
otlp:
protocols:
grpc:
endpoint: 0.0.0.0:4317
http:
endpoint: 0.0.0.0:4318
processors:
memory_limiter:
check_interval: 5s
limit_percentage: 75
spike_limit_percentage: 20
tail_sampling:
decision_wait: 20s
num_traces: 20000
expected_new_traces_per_sec: 800
policies:
- name: keep-errors
type: status_code
status_code:
status_codes: [ERROR]
- name: keep-slow-requests
type: latency
latency:
threshold_ms: 3000
- name: keep-small-baseline
type: probabilistic
probabilistic:
sampling_percentage: 2
batch:
timeout: 2s
send_batch_size: 1024
exporters:
otlphttp:
endpoint: https://tempo.example.com/otlp
service:
pipelines:
traces:
receivers: [otlp]
processors: [memory_limiter, tail_sampling, batch]
exporters: [otlphttp]
لو بتثبت الـ Collector على Kubernetes، Helm chart الرسمي بيدعم تشغيله كـ Deployment أو DaemonSet أو StatefulSet، ولازم تحدد mode. المصدر: OpenTelemetry Collector Helm Chart.
تتأكد إنه شغال إزاي
- ابعت traces عادية لمدة 10 دقائق، وسجل عدد الـ spans قبل وبعد الـ Collector.
- اعمل request يرجع HTTP 500، وتأكد إن trace الخطأ ظهر كامل في الـ backend.
- اعمل request بطيء يتعدى 3000ms، وتأكد إنه ظهر حتى لو باقي الطلبات اترمت.
- راقب ذاكرة الـ Collector. لو بدأت تقرب من الحد، قلل
decision_waitأو زود replicas مع routing صحيح.
قياس متوقع: لو 4% فقط من الطلبات errors أو slow، ومعاهم 2% baseline، حجم التخزين ممكن ينزل من 100% إلى حوالي 6%. في نظام كان بيدفع 300 دولار شهريًا على traces، الرقم ممكن ينزل نظريًا إلى 18 إلى 30 دولار، حسب تسعير الـ backend وحجم الـ spans. الرقم تقديري، لكنه مفيد قبل ما تعمل rollout.
الـ trade-off هنا
المكسب واضح: ضوضاء أقل، تكلفة أقل، وتحقيقات إنتاج مركزة على traces فعلاً مهمة. الثمن: الـ Collector محتاج ذاكرة أعلى لأنه بيحتفظ بالـ traces مؤقتًا. كمان القرار يتأخر 20 ثانية في المثال ده، فمش هتشوف كل trace فورًا.
في نقطة أهم: Tail Sampling لازم يشوف كل spans الخاصة بنفس trace في نفس مكان القرار. لو عندك أكثر من Collector وكل span رايح instance مختلفة، القرار هيبقى ناقص. الحل عادة يكون routing حسب trace ID أو طبقة Collectors أمامية وخلفية. لو تجاهلت النقطة دي، هتلاقي traces مقطوعة وتستنتج استنتاجات غلط.
متى لا تستخدم هذه الطريقة
لا تستخدم Tail Sampling لو عندك traffic قليل وتكلفة التخزين مش مشكلة. التعقيد مش مستاهل. لا تستخدمه كأول خطوة لو لسه instrumentation نفسها ضعيفة ومفيش attributes واضحة مثل route وstatus وservice.name. كمان لا تستخدمه لو مطلوب منك compliance يحتفظ بكل trace حرفيًا لمدة معينة؛ هنا sampling ممكن يبقى ضد المتطلب.
لو عايز حل أبسط لتطبيق صغير، استخدم head sampling بنسبة ثابتة مؤقتًا. هتكسب بساطة، وهتخسر القدرة على الاحتفاظ بكل الأخطاء تلقائيًا.
المصادر
- OpenTelemetry: Collector Processors
- OpenTelemetry Collector Contrib: Tail Sampling Processor
- OpenTelemetry Blog: Tail Sampling
- OpenTelemetry Collector Helm Chart
الخطوة التالية
افتح إعداد الـ Collector عندك، واحسب expected_new_traces_per_sec * decision_wait. لو الرقم أكبر من num_traces الحالي، عدله قبل ما تضيف policies جديدة.