شبكة الخدمات: Istio وLinkerd

قابلية الملاحظة في الـ Mesh

18 دقيقة الدرس 7 من 27

قابلية الملاحظة في الـ Mesh

من أكثر مزايا الـ service mesh التي تُقدَّر بأقل مما تستحق هو ما يمنحك إياه مجاناً في اللحظة التي تحقن فيها sidecars: طبقة قابلية ملاحظة كاملة ومتسقة لا تتطلب أي تغييرات على التطبيق. كل pod يحصل على بروكسي Envoy يُصدر تلقائياً إشارات المراقبة الأربع الذهبية من Google SRE — زمن الاستجابة (Latency)، حجم الحركة (Traffic)، الأخطاء (Errors)، والتشبع (Saturation) — لكل حافة بين الخدمات في topology الشبكة. لا حاجة لـ SDK instrumentation. لا تكوين OTel لكل فريق. لا code reviews لتسجيلات مقاييس منسية. الـ mesh يرى كل بايت TCP وكل تبادل HTTP ويعرف من أرسله، ومن استقبله، وهل نجح، وكم استغرق.

في الإنتاج، هذا يهم كثيراً. في Lyft — الشركة التي أنشأت Envoy أصلاً — كانت طبقة Telemetry الخاصة بالـ mesh أحد المبررات الرئيسية للاستثمار متعدد السنوات. قبل الـ mesh، كان كل فريق يُعدّ المقاييس بطريقة مختلفة. بعده، كل خدمة تُصدر تلقائياً istio_requests_total وistio_request_duration_milliseconds وistio_tcp_sent_bytes_total بمخطط تسميات متسق. أصبح زمن p99 عبر الخدمات قابلاً للملاحظة كأولوية أولى دون أي جهد هندسي فردي.

الإشارات الذهبية من الـ Sidecar

كل من Envoy (في Istio) وlinkerd2-proxy (في Linkerd) يُصدران الإشارات الأربع الذهبية على طبقتي L4 وL7 تلقائياً. مقاييس Prometheus الرئيسية التي ستستخدمها يومياً في الإنتاج:

  • حجم الحركة (معدل الطلبات): istio_requests_total — عداد بتسميات source_workload وdestination_workload وresponse_code وrequest_protocol. اشتق معدل الطلبات في الثانية بـ rate(istio_requests_total[1m]).
  • الأخطاء: فلتر istio_requests_total بـ response_code!~"2.." للحصول على معدلات أخطاء العميل/الخادم. الـ mesh يُبلّغ عن الأخطاء على طبقة النقل — رفض الاتصال، انتهاء مهلة الخادم الأعلى، فتح قاطع الدائرة.
  • زمن الاستجابة: istio_request_duration_milliseconds_bucket — هيستوغرام يكشف التوزيع الكامل. استخدم histogram_quantile(0.99, rate(istio_request_duration_milliseconds_bucket[5m])) للحصول على p99 لكل زوج workload. هذا هو زمن الاستجابة للطلب الوكيل كما يراه الـ sidecar، ويشمل وقت معالجة التطبيق.
  • التشبع: مشتق من envoy_cluster_upstream_cx_active (الاتصالات النشطة) مقابل envoy_cluster_upstream_cx_overflow (تجاوز connection pool). ارتفاع الـ overflow هو أبكر مؤشر إنذار للحمل الزائد قبل أن يتدهور زمن الاستجابة بشكل واضح.
الفكرة الأساسية — اتساق التسميات بلا تكلفة: كل مقياس من كل sidecar يحمل مجموعة تسميات موحدة: source_workload، destination_workload، source_namespace، destination_namespace، destination_service، request_protocol، response_code، response_flags. تسمية response_flags خاصة بـ Envoy ولا غنى عنها — تُرمّز سبب الاستجابة غير 2xx: UH (لا خادم صالح في الأعلى)، UT (انتهاء مهلة الخادم الأعلى)، UC (فشل اتصال الخادم الأعلى)، URX (تجاوز حد إعادة المحاولة)، DC (إنهاء اتصال الخادم الأدنى). الفلترة على response_flags في تنبيه PromQL تُميّز بين "خدمتك ترجع 500" و"Envoy ينتهي بانتهاء المهلة انتظاراً لخدمتك."

تكوين Prometheus القياسي لـ Istio يستخدم annotations الـ pod. تكشف istiod telemetry API المقاييس على منفذ 15020 لكل sidecar. تكامل Istio-Prometheus (أو PodMonitor إن استخدمت Prometheus Operator) يجمع هذا المنفذ.

# PodMonitor — جمع مقاييس Istio sidecar عبر Prometheus Operator apiVersion: monitoring.coreos.com/v1 kind: PodMonitor metadata: name: istio-proxy namespace: istio-system labels: release: kube-prometheus-stack spec: selector: matchLabels: security.istio.io/tlsMode: istio # كل pod محقون يحصل على هذا namespaceSelector: any: true # جمع من كل namespaces podMetricsEndpoints: - port: http-envoy-prom # منفذ 15090 (Envoy admin /stats/prometheus) path: /stats/prometheus scheme: http interval: 15s relabelings: - sourceLabels: [__meta_kubernetes_pod_label_app] targetLabel: app - sourceLabels: [__meta_kubernetes_namespace] targetLabel: namespace

في Linkerd، يستخدم تكوين Prometheus المكافئ annotations جمع بيانات Prometheus التي يكتبها linkerd inject على كل pod: prometheus.io/scrape: "true" وprometheus.io/port: "4191". تأتي إضافة linkerd-viz بلوحة Grafana جاهزة ترسم الإشارات الذهبية من البداية.

رسم بياني الخدمة وKiali

مقاييس الإشارات الذهبية تمنحك أرقاماً؛ Kiali يمنحك رسماً بيانياً حياً للخدمات يُربط تلك الأرقام بـ topology الشبكة. Kiali هو واجهة المستخدم الرسمية لمراقبة Istio. يستعلم من Prometheus وJaeger/Zipkin وكذلك Kubernetes API في آنٍ واحد ليرسم رسماً بيانياً للتبعيات في الوقت الفعلي حيث كل حافة مُعلَّقة بـ RPS ومعدل الأخطاء وزمن p99 وحالة mTLS للاتصال.

Mesh Observability Stack — from sidecar to dashboards Data Plane — Injected Sidecars frontend + Envoy proxy orders + Envoy proxy payments + Envoy proxy inventory + Envoy proxy metrics :15020 traces (OTLP/Zipkin) Collection Layer Prometheus (scrape :15020) OTel Collector / Zipkin Kubernetes API (topology) Prometheus / Thanos golden-signal metrics Jaeger / Tempo distributed traces Kiali graph store live service topology Grafana Dashboards Kiali Service Graph Jaeger / Tempo UI
منظومة قابلية الملاحظة في الـ mesh: الـ sidecars تُصدر المقاييس والتتبعات على طبقة البيانات؛ Prometheus وOTel Collector وKubernetes API تجمعها؛ Grafana وKiali وJaeger تعرضها للمهندسين.
ممارسة احترافية — استخدم Kiali للـ topology فقط، لا للتنبيهات: Kiali هو أداة تصور للقراءة فقط. لا غنى عنه أثناء التحقيق في الحوادث (يُظهر فوراً أي حواف حمراء) وأثناء عمليات الطرح (راقب ارتفاع p99 على canary في الوقت الفعلي). لكن لا تبني تنبيهات إنذار على بيانات Kiali. يجب أن يكون كل التنبيه قواعد PromQL مباشرة على مقاييس Prometheus — الكاش الداخلي لـ Kiali له تقادم قابل للتكوين وليس مصمماً لتقييم التنبيهات دون الدقيقة. النمط الذي تستخدمه المنصات الكبيرة: Kiali للاستكشاف، وPrometheus Alertmanager للإنذار.

تكامل التتبع الموزع

الـ mesh يتولى الجزء الأصعب من التتبع الموزع: ينشئ تلقائياً trace spans لكل طلب وكيل وينشر ترويسات W3C traceparent (أو Zipkin B3) بين الخدمات. لا تحتاج لتجهيز كود تطبيقك للحصول على spans بين الخدمات — Envoy sidecars تُنشئها. ما تحتاجه من التطبيق هو سلوك واحد فقط: نشر ترويسات التتبع الواردة على كل استدعاء صادر. Envoy يقرأ الترويسات عند الدخول، ينشئ server span، ثم يحقن الترويسات المحدّثة في الطلب الداخلي. إن ابتلع كود خدمتك الترويسات (لم يمررها)، فإن Envoy في الوقفة التالية يبدأ تتبعاً جديداً منفصلاً.

مصيدة إنتاجية — تغيير التطبيق الواحد الذي لا يمكن تخطيه: كثير من الفرق تنشر الـ mesh، ترى التتبعات في Jaeger، وتفترض أن كل شيء يعمل. ثم يُقدّمون بلاغاً: "التتبعات مكسورة، أرى وقفتين فقط." السبب الجذري دائماً هو نفسه: خدمة ما لا تُمرر ترويسات التتبع. في Istio، الترويسات التي يجب نشرها هي: x-request-id، x-b3-traceid، x-b3-spanid، x-b3-parentspanid، x-b3-sampled، x-b3-flags، x-ot-span-context (أو فقط traceparent إن تحولت لوضع W3C). الحل بسيط، لكنه يتطلب لمس كل خدمة. افعل ذلك بشكل منهجي أثناء طرح الـ mesh؛ تركه لما بعد يعني مشروع تحديث عبر عشرات الفرق.

يتم تكوين Istio لإرسال التتبعات إلى backend Jaeger (أو OTel Collector) عبر مورد Telemetry API. يضبط MeshConfig في ConfigMap الـistio معدل أخذ العينات العالمي ومزود التتبع:

# istio ConfigMap — تفعيل التتبع مع أخذ عينات رأسية بنسبة 1% # kubectl -n istio-system edit configmap istio apiVersion: v1 kind: ConfigMap metadata: name: istio namespace: istio-system data: mesh: | enableTracing: true defaultConfig: tracing: sampling: 1.0 # 1% رأسي؛ تجاوز لكل namespace عبر Telemetry CR zipkin: address: otel-collector.observability:9411 # مستقبل متوافق مع Zipkin على OTel Collector extensionProviders: - name: otel-tracing opentelemetry: service: otel-collector.observability.svc.cluster.local port: 4317 # OTLP gRPC --- # مورد Telemetry لكل namespace — 100% أخذ عينات لـ namespace التجهيز apiVersion: telemetry.istio.io/v1alpha1 kind: Telemetry metadata: name: staging-full-trace namespace: staging spec: tracing: - providers: - name: otel-tracing randomSamplingPercentage: 100.0

في الإنتاج عند 10,000 طلب في الثانية، معدل أخذ عينات رأسي بنسبة 1% ينتج 100 تتبع في الثانية — أكثر من كافٍ لتحليل زمن الاستجابة وتصحيح الحوادث. الترقية الحرجة للإنتاج هي أخذ عينات ذيلية: أخذ 100% من تتبعات الأخطاء والتتبعات البطيئة (p99 أكبر من الحد) بصرف النظر عن المعدل العالمي، وأخذ عينات التتبعات السريعة/الناجحة بنسبة 1%. يُطبّق ذلك tail sampler في Tempo أو OTel Collector، مما يضمن عدم تفويت أي تتبع فاشل مع الإبقاء على تكاليف التخزين محدودة.

لوحات Grafana: المنظومة الرسمية

يُشحن مشروع Istio بلوحات Grafana جاهزة يمكن استيرادها من كتالوج grafana.com (أو مباشرة من مستودع istio/istio على GitHub تحت samples/addons/grafana). اللوحات الأربع التي ستستخدمها في الإنتاج:

  • Istio Mesh Dashboard (ID 7639) — عرض عالمي: إجمالي RPS، معدل الأخطاء العالمي، زمن p50/p90/p99 عبر الـ mesh بأكمله. الشاشة الأولى التي تفتحها أثناء الحادث.
  • Istio Service Dashboard (ID 7636) — تعمق في كل خدمة: RPS الداخلي حسب المصدر، RPS الخارجي حسب الوجهة، معدل الخطأ مقسّماً حسب كود الاستجابة وعلامة استجابة Envoy، هيستوغرامات زمن الاستجابة. كافٍ لمعظم تحقيقات السبب الجذري للحوادث.
  • Istio Workload Dashboard (ID 7630) — عرض على مستوى الـ pod: مفيد حين تخدم deployments متعددة نفس الخدمة (تحليل canary، تقسيم حركة متعدد الإصدارات).
  • Istio Performance Dashboard (ID 11829) — صحة control plane: CPU/memory لـ istiod، معدل xDS push، زمن توزيع التكوين. ضروري لتشخيص مشكلات طبقة الـ mesh مقابل مشكلات التطبيق.
# استعلامات PromQL الأساسية لقابلية ملاحظة الـ mesh — استخدمها في لوحات Grafana والتنبيهات # --- حجم الحركة: طلبات في الثانية لكل زوج خدمات --- sum(rate(istio_requests_total{reporter="destination"}[1m])) by (destination_service_name) # --- الأخطاء: معدل غير 2xx لكل خدمة --- sum(rate(istio_requests_total{reporter="destination",response_code!~"2.."}[5m])) by (destination_service_name) / on(destination_service_name) sum(rate(istio_requests_total{reporter="destination"}[5m])) by (destination_service_name) # --- زمن الاستجابة: p99 لزوج خدمات محدد --- histogram_quantile(0.99, sum(rate(istio_request_duration_milliseconds_bucket{ reporter="destination", destination_service_name="payments" }[5m])) by (le) ) # --- التشبع: تجاوز connection pool (إشارة حمل زائد مبكرة) --- sum(rate(envoy_cluster_upstream_cx_overflow[5m])) by (pod, cluster_name) # --- علامات استجابة Envoy: تقسيم أسباب الأخطاء --- sum(rate(istio_requests_total{ reporter="destination", response_flags=~"UH|UT|UC|URX|UF" }[5m])) by (destination_service_name, response_flags)

فلتر reporter="destination" مهم: Envoy يُصدر مقاييس مكررة من كل من sidecar المصدر (reporter=source) وsidecar الوجهة (reporter=destination). استخدام destination يتجنب الحساب المزدوج ويمنحك زمن الاستجابة كما يراه الطرف المستقبِل، وهو العرض الصحيح للتوافق مع SLO. استخدم reporter=source فقط حين تحتاج تحديداً لزمن الاستجابة المُدرَك من العميل بما يشمل وقت عبور الشبكة.

قابلية ملاحظة Linkerd — نفس الإشارات، شكل مختلف: يُصدر Linkerd نفس الإشارات الذهبية عبر أسماء مقاييسه الخاصة: request_total، response_latency_ms_bucket، tcp_open_connections. تأتي إضافة Linkerd Viz بمجموعة من لوحات Grafana وواجهة سطر أوامر: linkerd viz stat deploy تمنحك جدولاً بيانياً حياً في الطرفية يعرض معدل النجاح وRPS وزمن p99 لكل deployment. linkerd viz edges تُظهر حالة mTLS لكل حافة pod-to-pod. linkerd viz tap deploy/payments --to deploy/database تبث تفاصيل الطلبات الحية — الطريقة، المسار، كود الاستجابة، زمن الاستجابة — دون الحاجة لـ backend تتبع كامل. ميزة tap هذه هي مكافئ Linkerd لسجلات وصول Envoy ولا غنى عنها أثناء التطوير.

ربط المقاييس والتتبعات والسجلات: Exemplars

القطعة الأخيرة في قابلية ملاحظة الـ mesh في الإنتاج هي ربط exemplar — القدرة على النقر على ارتفاع في لوحة زمن الاستجابة في Grafana والقفز مباشرة إلى تتبع تمثيلي. Prometheus 2.43 وما بعده يدعم exemplars أصلياً: يمكن أن يحمل bucket الهيستوغرام تسمية trace_id بجانب القيمة. Envoy 1.24 وما بعده يُصدر exemplars عند تفعيل التتبع.

مع Grafana 9 وما بعده، تُظهر لوحة زمن الاستجابة المبنية على هيستوغرام Prometheus مع exemplars نقاطاً مبعثرة فوق خط الـ quantile. كل نقطة هي طلب حقيقي بـ trace ID حقيقي. النقر على نقطة يفتح Jaeger أو Tempo مُفلتراً على ذلك التتبع. هذا يغلق الحلقة: ترى ارتفاع p99 في لوحة SLO، تنقر على أسوأ exemplar، تصل إلى شلال التتبع الذي يُظهر بالضبط أي خدمة وأي استدعاء داخلي أحدث الارتفاع. لا grep، لا ربط سجلات، لا تبديل سياق بين أربع أدوات.

في المؤسسات التي تُشغّل مئات الخدمات — Airbnb وPinterest وShopify — يُعدّ نمط ربط exemplar إلزامياً لأي لوحة SLO لزمن الاستجابة. الـ mesh يوفر trace IDs مجاناً؛ المتطلب الوحيد هو أن تجمع Prometheus مع دعم exemplar (علم --enable-feature=exemplar-storage على Prometheus) ومصدر بيانات Grafana مكوَّن لاستخدامها.