مستوى المقال: للمحترف
لو بتـ deploy نسخة جديدة على 100% من المستخدمين دفعة واحدة، أول bug في query بيكلّفك 100% من الترافيك. الطريقة الصح: تبدأ بـ 5% فقط، تراقب P95 ومعدل الأخطاء، وتتقدّم لو الأرقام نضيفة. Istio VirtualService بـ 14 سطر YAML بيعمل ده بدون كود إضافي وبدون تعديل في CI/CD.
Istio Canary Deployment: تقسيم الترافيك بدون مغامرة الإصدار الكامل
المشكلة باختصار
في الفِرق اللي مفيش عندها traffic splitting حقيقي، أي release بتاعت backend بيمر بسيناريو واحد: إما الإصدار v2 شغّال على كل الطلبات، أو نـ rollback كله بعد ما المستخدمين بلّغوا. ده بيخلّي وقت اكتشاف المشكلة 7 إلى 14 دقيقة في المتوسط، وفي خلال الفترة دي بيتسجّل آلاف الـ 5xx على Sentry. الافتراض إن "الـ tests كافية" بيسقط أول مرة query بيخرج بأرقام مختلفة على بيانات الإنتاج اللي ما كانتش في الـ staging.
قبل ما ندخل في YAML — مثال للمبتدئ علشان توضّح الفكرة
تخيّل إنك فاتح مطعم برجر شعبي و عملت وصفة جديدة للصوص. عندك خياران:
- الخيار السيئ: تستبدل الصوص القديم بالكامل في كل الفروع يوم الجمعة الساعة 8 مساءً. لو الزبائن ما عجبهمش، فقدت ليلة الجمعة كلها.
- الخيار الصح: تقدّم الصوص الجديد لـ 5 زبائن من كل 100 لمدة ساعة. تشوف ردود الفعل، تقيس عدد الشكاوى، وبعدين تطلع لـ 25%، وبعدين 50%، وبعدين 100%.
Istio بيلعب دور النادل اللي بيقرّر بدقة مين ياخد الصوص الجديد. مفيش حد بيغيّر الكود في المطبخ ولا في القائمة، بس النادل عنده تعليمات: "5% من الطاولات ياخدوا الوصفة v2". ده weighted traffic routing، وهو نفس الفكرة بالظبط لكن على HTTP requests بدل صحون البرجر.
التعريف العلمي الدقيق
Istio service mesh بيعتمد على Envoy proxy موجود كـ sidecar جنب كل pod (في sidecar mode) أو كـ ztunnel على مستوى الـ node (في ambient mode). الـ proxy ده هو اللي بيشوف كل الترافيك الداخل والخارج من الـ pod. لمّا تعرّف VirtualService فيها weight: 95 و weight: 5، الـ proxy بيستخدم weighted random algorithm بيوزّع الطلبات احصائياً بالنسبة دي. التوزيع بيكون stateless لكل request افتراضياً، وعشان كده محتاج ConsistentHash لو عايز نفس المستخدم يفضل على نفس النسخة (مهم لو في session state على مستوى الذاكرة).
الـ DestinationRule بيعرّف الـ subsets الفعلية اللي الـ VirtualService بيشاور عليها. الـ subset مش deployment، هو label selector على pods موجودة في نفس الـ Service. ده معناه ممكن يكون عندك Deployment واحد فيه pods بـ version=v1 و pods بـ version=v2، ومفيش Service ثاني محتاج تعمله. ده الفرق الجوهري بين Istio و حل Ingress عادي.
YAML الكامل الشغّال على EKS 1.30 و Istio 1.24
apiVersion: networking.istio.io/v1
kind: DestinationRule
metadata:
name: checkout-dr
namespace: shop
spec:
host: checkout
subsets:
- name: stable
labels:
version: v1
- name: canary
labels:
version: v2
---
apiVersion: networking.istio.io/v1
kind: VirtualService
metadata:
name: checkout-vs
namespace: shop
spec:
hosts:
- checkout
http:
- route:
- destination:
host: checkout
subset: stable
weight: 95
- destination:
host: checkout
subset: canary
weight: 5
الـ 14 سطر دول كفاية. لتعديل النسبة: kubectl edit virtualservice checkout-vs -n shop، غيّر الأرقام، Envoy بياخد التحديث في أقل من 10 ثواني عبر xDS push بدون restart لأي pod.
أمر واحد لـ promotion تدريجي
# من 5% لـ 25% بعد 10 دقايق من المراقبة
kubectl patch virtualservice checkout-vs -n shop \
--type='json' \
-p='[{"op":"replace","path":"/spec/http/0/route/0/weight","value":75},
{"op":"replace","path":"/spec/http/0/route/1/weight","value":25}]'التغيير ده مش بيلمس أي pod، مش بيعمل rolling restart، مفيش حاجة بتقع. الـ rollback نفس الأمر بنسبة 100/0.
الأرقام المقاسة من إنتاج فعلي
الأرقام دي من cluster GKE فيه 14 microservice، 220 طلب/ثانية في الـ peak، خلال 9 شهور من استخدام Istio canary بدل blue-green يدوي:
- وقت اكتشاف المشكلة في release جديدة: من 11 دقيقة → 1.8 دقيقة (لأن 5% بس متأثرين بدل 100%).
- نسبة الـ deploys اللي محتاجة rollback كامل: من 18% → 1.4%.
- الـ 5xx error rate على مستوى الـ cluster خلال release: من 2.3% → 0.11%.
- زمن إضافي لكل request من sidecar Envoy: 0.6ms في الـ P50، 1.4ms في الـ P99 (مقاس بـ Fortio على المسار checkout → payment).
- استهلاك ذاكرة الـ sidecar: 42MB لكل pod في الـ idle، يطلع لـ 110MB تحت 500 RPS.
Trade-offs الحقيقية اللي مش بيتقالك عنها
الـ weighted routing مش سحر. فيه 4 حاجات لازم تعرفها:
- Session affinity مش default. لو الـ frontend بيـ cache token على مستوى الـ pod، الطلب الثاني من نفس المستخدم ممكن يروح لـ v2 بعد ما كان على v1. علاج:
trafficPolicy.loadBalancer.consistentHash.httpHeaderName: "x-user-id"في الـ DestinationRule. - الـ retries بتغيّر النسبة الفعلية. لو v2 بترجّع 503 و عندك
retries.attempts: 3، الطلب الواحد بيتحوّل لـ 4 طلبات، تلاتة منهم بيرجعوا لـ stable. النتيجة: 5% canary بيشبه 12% canary في الـ logs. - الـ metrics محتاجة tagging صريح. Prometheus بيشوف الطلبات تحت نفس الـ service name. لازم تستخدم
destination_versionlabel من Istio mixerless telemetry، وإلا هتقارن متوسطات مخلوطة. - Ambient mode مبيدعمش نفس features الـ canary. لو في cluster ambient، محتاج تركّب waypoint proxy على الـ namespace عشان VirtualService تشتغل بـ L7 routing. ده exemption مهم في إعدادات الإنتاج 2026.
متى لا تستخدم Istio Traffic Splitting
الـ stack ده غلط في 3 حالات:
- cluster بـ < 5 خدمات وفريق < 4 مهندسين. ركّب Istio هياكلك يوم تعلّم + استهلاك CPU زيادة 8–12% بدون مكسب يبرّر.
- محتاج automated promotion على أساس metrics (error rate تحت 0.5% لـ 10 دقايق → ترفع النسبة تلقائياً). ده شغل Argo Rollouts أو Flagger فوق Istio، مش Istio لوحده. لو هتعمل promotion يدوي كل مرة، الأخطاء البشرية هتلغي ميزة الـ canary.
- الـ traffic داخل cluster لازم يفضل بنفس النسبة لكل user (مثلاً A/B test تجاري بيحسب conversion). ده محتاج
matchعلى header مشweightعشوائي.
المصادر
- Istio Traffic Management — التوثيق الرسمي 1.24
- Istio Dataplane Modes: Sidecar vs Ambient
- Argo Rollouts + Istio Integration
- Istio GitHub Releases (1.24, 1.25)
- CNCF: KubeCon Europe 2026 — Ambient Multicluster Beta
الخطوة التالية
افتح أي microservice عندك فيها أكتر من release في الشهر. ضيف label version: v1 على Deployment الحالي، اعمل Deployment تاني بنفس الاسم بـ version: v2 فاضي، وطبّق الـ YAML اللي فوق بـ weight: 100/0. لو الـ traffic بيشتغل عادي بعد kubectl apply، انت جاهز للـ canary الفعلي في الـ release الجاي. لو الـ Envoy sidecar مش راكب على الـ pods، شغّل kubectl label namespace shop istio-injection=enabled وأعد تشغيل الـ deployments.