أنماط المعمارية

نمطا Sidecar وAmbassador

18 دقيقة الدرس 6 من 10

نمطا Sidecar وAmbassador

مع تنامي تعقيد الخدمات، تتراكم قائمة طويلة من الاهتمامات التشغيلية التي لا علاقة لها بمنطق الأعمال: مصادقة TLS المتبادلة، وتتبع الطلبات الموزعة، وتسجيل الوصول، وإعادة المحاولات، وتحديد معدل الطلبات، وفحوصات الصحة. تاريخيًا، كانت الفرق تدمج كل ذلك داخل كل خدمة على حدة، ما يعني أن كل فريق يُعيد تطبيق الشيفرة المكررة ذاتها بلغات مختلفة محتملة. يحلّ نمطا Sidecar وAmbassador هذه المشكلة بتغليف تلك الاهتمامات المشتركة في عملية منفصلة تعمل بجانب الخدمة الرئيسية، لا داخلها.

الفكرة الجوهرية: يستغل كلا النمطين بدائية نشر بسيطة — تشغيل حاويتين في نفس الـ Pod أو على نفس المضيف، بحيث تتشاركان مساحة الشبكة (وأحيانًا وحدة تخزين مشتركة). تتولى الحاوية المساعدة مهام البنية التحتية؛ أما الحاوية الرئيسية فتتولى منطق الأعمال. ولا تعلم كل منهما بوجود الأخرى على مستوى الشيفرة.

نمط Sidecar

Sidecar هو حاوية مساعدة تُضيف قدرات أو تمدّد إمكانيات الخدمة الرئيسية دون تعديل شيفرتها. تعمل في وحدة النشر ذاتها (Kubernetes Pod، أو ECS Task، أو مجموعة عمليات على جهاز ظاهري) وتتشارك شبكة localhost مع الحاوية الرئيسية.

تشمل المسؤوليات الشائعة لنمط Sidecar ما يلي:

  • وكيل شفاف / شبكة الخدمات: يعترض Envoy أو Linkerd جميع حركة المرور الواردة والصادرة لفرض mTLS، وحقن رؤوس التتبع، وجمع المقاييس، وتطبيق منطق قاطع الدائرة — دون أي تغيير في شيفرة الخدمة.
  • شحن السجلات: يراقب sidecar مثل Fluentd أو Filebeat ملفات السجل التي تكتبها التطبيقات ويُعيد توجيهها إلى Elasticsearch أو Splunk. يكتب التطبيق نصًا عاديًا؛ ويتولى الـ sidecar الإرسال المنظّم والتخزين المؤقت والتحكم في الضغط العكسي.
  • تدوير الأسرار: يستطلع sidecar عميل Vault بانتظام HashiCorp Vault للحصول على بيانات اعتماد قصيرة العمر ويكتبها في وحدة تخزين مشتركة في الذاكرة. يقرأ التطبيق ملفًا؛ ويُجدّده الـ sidecar كل ساعة دون إعادة تشغيل.
  • مزامنة الإعدادات: يراقب sidecar إعادة تحميل ConfigMap التغييرات ويُرسل SIGHUP إلى العملية الرئيسية لتحفيز إعادة تحميل الإعداد دون نشر كامل.
Sidecar Pattern — pod with main service and sidecar proxy Kubernetes Pod (shared network namespace) Main Service Business Logic :8080 Sidecar Proxy Envoy :15001 Log Volume /var/log/app Client Prometheus / Jaeger Elasticsearch inbound traffic localhost metrics/traces log ship
Kubernetes Pod يضم خدمة رئيسية (منفذ 8080)، ووكيل Envoy كـ Sidecar (يعترض كل حركة المرور ويُصدّر المقاييس والتتبعات)، ووحدة تخزين مشتركة للسجلات تستهلكها Fluentd (غير مُظهرة) للإرسال إلى Elasticsearch.

مثال واقعي: شبكة خدمات Istio

تحقن Istio، أكثر شبكات الخدمات انتشارًا، وكيل Envoy كـ sidecar في كل pod تلقائيًا عبر خطاف القبول المتحوّل في Kubernetes. من تلك اللحظة فصاعدًا:

  • تُعاد توجيه جميع حركة TCP بشفافية عبر Envoy باستخدام قواعد iptables — دون أي تغيير في استدعاءات التطبيق مثل dial("localhost:3306").
  • يفرض Envoy mTLS على كل اتصال، ما يُلغي الحاجة إلى أي شيفرة TLS في الخدمات.
  • يحصل كل طلب على معرّف تتبع مُحقن في رؤوسه HTTP، مما يُتيح التتبع الموزع الشامل في Jaeger.
  • تُضبط إعادة المحاولات والمهل وقاطع الدائرة في موارد Istio المخصصة، لا في شيفرة التطبيق.

على نطاق Airbnb، يُضيف تشغيل sidecar في كل pod من آلاف الـ Pods عبئًا حقيقيًا — نحو 50 MB من الذاكرة العشوائية و0.1 نواة معالج ظاهري لكل pod كنقطة بداية. هذا هو ثمن هذا المستوى من التجريد. في 1,000 Pod، يعني ذلك 50 GB من الذاكرة مخصصة للوكلاء فحسب. قِس قبل أن تلتزم.

مخاطرة — كُمون Sidecar: يمر كل اتصال شبكي الآن عبر قفزتَي Envoy (خروجًا من جانب المُرسِل، ودخولًا من جانب المُستقبِل). تُضيف كل قفزة نحو 0.2 إلى 1 ميلي ثانية من الكُمون في الممارسة العملية. بالنسبة لمعظم الخدمات هذا هامشي، لكن للمسارات الحرجة للكُمون — محرك التداول، أو خادم الألعاب الآنية — قِسه بعناية. يُوفر Istio وضع gRPC بلا وكيل لهذه الحالات تحديدًا.

نمط Ambassador

Ambassador هو sidecar متخصص مهمته العمل كـ وكيل خارجي ذكي — ممثل محلي يتعامل مع تعقيد التواصل مع الخدمات الخارجية. تُرسل الخدمة الرئيسية طلباتها إلى localhost:<port> كما لو كانت الخدمة المستهدفة محلية؛ وتتولى حاوية Ambassador فعليًا إدارة الاتصال: التجميع، والمصادقة، وإعادة المحاولات، واكتشاف الخدمات، وترجمة البروتوكول.

فكّر في Ambassador كمرافق شخصي للخدمة في مكالماتها الصادرة.

حالات استخدام Ambassador:

  • جسر البروتوكولات القديمة: خدمة gRPC حديثة تحتاج إلى استدعاء نقطة نهاية SOAP قديمة. يُترجم Ambassador من gRPC إلى SOAP حتى لا تتعامل الخدمة الجديدة مع XML. وعند استبدال النظام القديم في نهاية المطاف، تتغير إعدادات Ambassador فحسب.
  • توجيه قاعدة البيانات عبر سحابات متعددة: يتحدث التطبيق إلى localhost:5432 (PostgreSQL). يُوجّه Ambassador هذا إلى نموذج RDS الصحيح في المنطقة الصحيحة، مُضيفًا رؤوس مصادقة IAM التي كان التطبيق سيحتاج إلى إنتاجها بنفسه.
  • تجميع الاتصالات: يحافظ PgBouncer المنشور كـ ambassador على مجموعة صغيرة من الاتصالات الدائمة بقاعدة البيانات، مما يُتيح لمئات عمليات التطبيق مشاركة حفنة من اتصالات قاعدة البيانات الحقيقية — دون أن تمتلك كل عملية منطق تجميع خاصًا بها.
  • اكتشاف الخدمات الديناميكي: يُضمّن التطبيق localhost:9000. يُحلّ Ambassador العنوان الحالي للخدمة المستهدفة من Consul أو Kubernetes DNS ويوزّع الحمل على الحالات السليمة.
Ambassador Pattern — outbound proxy sidecar Pod (shared localhost) App Service calls localhost:5432 Ambassador PgBouncer :5432 local Primary DB us-east-1 Read Replica us-west-2 IAM / Auth token refresh pool route auth Inbound Traffic
نمط Ambassador: تُرسل App Service استدعاءً محليًا بسيطًا على المنفذ 5432؛ بينما يتولى PgBouncer Ambassador تجميع الاتصالات وتوجيه القراءة/الكتابة ومصادقة IAM تجاه نماذج قاعدة البيانات الحقيقية.

Sidecar مقابل Ambassador — متى تستخدم أيهما؟

يستخدم كلا النمطين عملية مساعدة مجاورة، لكنهما يحلّان اهتمامات اتجاهية مختلفة:

  • Sidecar — يتعامل أساسًا مع حركة المرور الواردة والاهتمامات التشغيلية المشتركة (المراقبة، والأمان، والإعداد). يُغلّف الخدمة من منظور العالم الخارجي.
  • Ambassador — يتعامل أساسًا مع الاستدعاءات الصادرة. يُدير التعقيد نيابةً عن الخدمة حين تحتاج إلى الوصول إلى تبعيات خارجية. يُغلّف العالم الخارجي من منظور الخدمة.

في الواقع العملي، يؤدي Envoy Sidecar الخاص بـ Istio كلا الدورين — يعترض حركة المرور الواردة والصادرة. لكن التمييز المفاهيمي يظل مهمًا عند تصميم حل موجّه: إذا كنت تحتاج فقط إلى إدارة اتصال صادر أذكى (تجميع، مصادقة)، فإن ambassador وحده (مثل PgBouncer، أو Twistlock) يكون أبسط وأقل تكلفة من شبكة خدمات كاملة.

أفضل الممارسات — الفرق متعددة اللغات: يكون الـ sidecar أكثر قيمةً عندما تعمل مؤسستك بلغات برمجة متعددة. بدلًا من تطبيق منطق إعادة المحاولة في Go وJava وPython وRust، طبّقه مرة واحدة في فلتر Envoy وأدخله في كل مكان. كلما زادت اللغات التي تستخدمها، ارتفع عائد الاستثمار.

اعتبارات دورة الحياة والنشر

في Kubernetes، تعمل كل من الحاوية الرئيسية وحاوية Sidecar في نفس الـ Pod، وبالتالي تُجدوَلان وتُكبَّران وتُنهَيان معًا. لهذا عواقب عملية:

  • ترتيب بدء التشغيل: إذا كان يجب أن يكون الـ sidecar جاهزًا قبل أن تقبل الحاوية الرئيسية حركة المرور (مثلًا، يجب على Vault agent كتابة الأسرار أولًا)، استخدم init containers في Kubernetes للإعداد الأولي وـreadiness gate للصحة المستمرة.
  • ترتيب الإغلاق: عند إنهاء Pod، تتلقى جميع الحاويات SIGTERM في وقت واحد بشكل افتراضي. إذا خرج الـ sidecar (مثل Envoy) قبل انتهاء التطبيق من تصريف الاتصالات، تضيع الطلبات الجارية. يتعامل Istio 1.12+ مع هذا بخطاف إغلاق مخصص؛ كانت الإصدارات الأقدم تتطلب إيقاف preStop.
  • محاسبة الموارد: تُحتسب وحدة المعالجة المركزية والذاكرة الخاصة بالـ sidecar ضمن إجمالي الـ Pod. اضبط دائمًا requests وlimits على حاوية الـ sidecar حتى يتمكن المُجدوِل من وضع الـ Pod بشكل صحيح ولا يتمكن الـ sidecar من تجويع الحاوية الرئيسية.
الـ Sidecar الأصيل في Kubernetes 1.29: تخرّج حقل restartPolicy: Always على init containers إلى حالة مستقرة في Kubernetes 1.29، مما يُوفر ميزة sidecar container أصيلة. تبدأ حاويات الـ sidecar الآن قبل الحاوية الرئيسية وتنتهي بعدها — مما يُلغي أخطاء الترتيب التي كانت تُعاني منها عمليات النشر القديمة.

ملخص المقايضات

  • المزايا: لا تغييرات في الشيفرة للتبني؛ مستقل عن اللغة؛ تطبيق سياسات متسق عبر جميع الخدمات؛ فصل نظيف بين مخاوف البنية التحتية والأعمال.
  • العيوب: كُمون إضافي لكل قفزة (نحو 0.2–1 ميلي ثانية)؛ تكلفة ذاكرة ومعالج لكل pod؛ تعقيد تشغيلي (حاويات أكثر للمراقبة والترقية)؛ تصحيح الأخطاء أصعب حين يكون الوكيل في المنتصف.
  • غير مناسب حين: تشغّل خدمات قليلة جدًا (تفوق التكلفة الفائدة)؛ أو كانت خدماتك بالغة الحساسية للكُمون؛ أو لم تدعم بيئة النشر لديك العمليات المجاورة (مثل الدوال اللاسيرفرية).