OpenTelemetry Tail Sampling بالعربي: احتفظ بالتريس المهم
مستوى القارئ: متوسط
لو فاتورة الـ tracing بتزيد، المقال ده هيخليك تحتفظ بالـ traces اللي تهمك فعلاً: الأخطاء، الطلبات البطيئة، وجزء صغير من الطبيعي.
المشكلة باختصار
الطريقة الشائعة إنك تعمل head sampling بنسبة ثابتة، مثلاً 10% من أول الطلب. الطريقة دي بتفشل لما الخطأ النادر يحصل في الـ 90% اللي اترموا. ركز: المشكلة مش إن sampling غلط. المشكلة إن القرار اتاخد بدري.
الافتراض إن عندك تطبيق microservices بيستقبل حوالي 2,000 request في الدقيقة، وكل request ينتج 8 إلى 15 span. لو خزنت 100% من التريسز، أنت ممكن تبعت عشرات الآلاف من spans كل دقيقة لمنصة المراقبة. ده مفيد في التحقيق، لكنه مكلف ومزعج.
الفكرة: القرار بعد اكتمال الطلب
Tail Sampling في OpenTelemetry Collector بيستنى شوية لحد ما معظم spans بتاعة trace توصل، وبعدها يقرر: هل التريس ده يستاهل يتخزن ولا لأ. مثال بسيط: لو عندك طلب checkout خلص بنجاح في 180ms، ممكن تحتفظ بـ 5% منه بس. لكن لو نفس الطلب رجع ERROR أو أخد أكثر من 2 ثانية، احتفظ به 100%.
اللي بيحصل فعلاً إن الـ Collector يستخدم processor اسمه tail_sampling. حسب توثيق OpenTelemetry، الـ processor ده موجود في توزيعة contrib وKubernetes Collector، وبيشتغل على traces فقط. إعداد decision_wait يحدد وقت الانتظار قبل القرار، وnum_traces يحدد عدد traces التي تبقى في الذاكرة أثناء الانتظار.
إعداد عملي في OpenTelemetry Collector
أفضل طريقة تبدأ بيها: خلي الأخطاء 100%، والطلبات البطيئة 100%، والباقي نسبة صغيرة. المثال التالي مناسب كبداية لفريق عنده 500 إلى 1,000 trace جديدة في الثانية على gateway collector واحد.
receivers:
otlp:
protocols:
grpc:
http:
processors:
memory_limiter:
check_interval: 1s
limit_mib: 1024
spike_limit_mib: 256
tail_sampling:
decision_wait: 10s
num_traces: 20000
expected_new_traces_per_sec: 1000
policies:
- name: keep-errors
type: status_code
status_code:
status_codes: [ERROR]
- name: keep-slow-requests
type: latency
latency:
threshold_ms: 2000
- name: keep-5-percent-normal
type: probabilistic
probabilistic:
sampling_percentage: 5
batch:
timeout: 5s
send_batch_size: 8192
exporters:
otlp:
endpoint: tempo:4317
tls:
insecure: true
service:
pipelines:
traces:
receivers: [otlp]
processors: [memory_limiter, tail_sampling, batch]
exporters: [otlp]
بالظبط، الترتيب مهم. memory_limiter قبل tail_sampling يحمي الـ Collector من الانفجار. وbatch بعده يقلل ضغط التصدير. المصادر الرسمية بتوضح إن تعريف processor لا يفعّله وحده؛ لازم تضيفه داخل pipeline في قسم service.
الأرقام التي تبدأ بها
استخدم معادلة عملية كبداية: num_traces = expected_new_traces_per_sec * decision_wait * 2. لو عندك 1,000 trace في الثانية وقرارك بعد 10 ثواني، ابدأ بـ 20,000. الضرب في 2 هامش أمان لأن traffic مش ثابت.
الـ trade-off هنا واضح: كل ما تزود decision_wait أو num_traces هتقلل احتمال رمي trace قبل قرار sampling، لكن هتزود استهلاك الذاكرة. لو Collector عنده 2GB RAM، لا تبدأ بـ 200,000 trace في الذاكرة. ابدأ صغير، راقب، ثم كبّر.
سيناريو واقعي: موقع SaaS عنده 50K زائر يوميًا، ومتوسط 300 trace في الثانية وقت الذروة. قبل Tail Sampling كان بيخزن 100% من traces. بعد الإعداد السابق، الأخطاء والبطيء اتخزنوا بالكامل، والطبيعي نزل إلى 5%. لو 90% من الطلبات طبيعية، حجم traces المصدرة ممكن ينخفض تقريبًا من 100 وحدة إلى 14.5 وحدة: 10 وحدات مهمة كاملة + 4.5 من الطبيعي. ده تقدير، لكنه كافي لاتخاذ قرار أولي.
متى لا تستخدم هذه الطريقة
لا تستخدم Tail Sampling كأول خطوة لو التطبيق نفسه لا يرسل spans صحيحة. أصلح instrumentation الأول. لا تستخدمه أيضًا على edge collector صغير داخل كل pod لو الذاكرة محدودة جدًا، لأن tail sampling يحتاج الاحتفاظ بالـ traces مؤقتًا. ولو عندك متطلبات قانونية أو forensic audit تحتاج 100% من كل request، sampling هنا هيخسرك اكتمال السجل.
بدل ما تبدأ بسياسة معقدة من أول يوم، ابدأ بثلاث سياسات فقط: errors، latency، probabilistic. بعد أسبوع راقب عدد traces المسقطة مبكرًا، ونسبة الأخطاء التي وصلت للBackend، واستهلاك RAM. لو عندك خدمات حرجة مثل payment، أضف attribute مثل service.criticality=critical واحتفظ بها بنسبة أعلى.
مصادر وتحقق
- توثيق OpenTelemetry عن Sampling يشرح الفرق بين head sampling وtail sampling: https://opentelemetry.io/docs/concepts/sampling/
- توثيق Collector Processors يوضح أن Tail Sampling Processor مخصص للـ traces وفي توزيعة contrib/K8s: https://opentelemetry.io/docs/collector/components/processor/
- README الرسمي للـ tailsamplingprocessor يشرح
decision_waitوnum_tracesوexpected_new_traces_per_sec: https://github.com/open-telemetry/opentelemetry-collector-contrib/blob/main/processor/tailsamplingprocessor/README.md - توثيق إعدادات Collector يوضح أن processor لا يعمل إلا عند إضافته داخل pipeline: https://opentelemetry.io/docs/collector/configuration/
الخطوة التالية
الخطوة التالية: خُد نسخة من إعداد الـ Collector عندك، وضيف tail_sampling بثلاث سياسات فقط: ERROR، latency فوق 2000ms، و5% probabilistic. شغّله يوم واحد في staging، وقارن عدد spans المصدرة واستهلاك RAM قبل ما تلمس الإنتاج.