استراتيجيات النشر والتسليم التدريجي

تحليل الكناري الآلي

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

تحليل الكناري الآلي

نشر الكناري يحوّل شريحة صغيرة من الحركة الحية إلى إصدار جديد وينتظر لمعرفة ما إذا كان هذا الإصدار يتصرف بقبول قبل المضي قُدُماً. كان قرار الترقية أو الإلغاء يعتمد على مهندس يحدق في لوحات المعلومات. تحليل الكناري الآلي يستبدل ذلك الإنسان بحلقة تحكم: وحدة تحكم تستعلم من منظومة المراقبة لديك بجدول زمني، وتُسجِّل الكناري مقابل سياسة، وتُرقّي أو تتراجع دون أن يضغط أحد على زر.

هذه هي الطريقة الفعلية التي تُشغِّل بها Uber وNetflix وGoogle SRE عمليات النشر على نطاق واسع. وحدة التحكم لا تثق في شارة CI خضراء — إنها تثق بإشارات الإنتاج الحية مقابل الخط الأساسي. عند تنفيذها بشكل صحيح، تلتقط التراجعات التي لا تستطيع اختبارات التكامل رؤيتها: ارتفاعات الكمون تحت أنماط الحركة الفعلية، زيادات معدل الأخطاء في مناطق جغرافية محددة، تسربات الذاكرة التي تظهر فقط تحت الحمل المستدام.

حلقة التحليل

كل نظام كناري آلي — سواء كان Argo Rollouts أو Flagger أو وحدة تحكم مخصصة — ينفذ نفس الحلقة الأساسية. فهمها بدقة هو المتطلب الأساسي لضبطها بشكل صحيح.

Automated canary analysis control loop Rollout Controller Argo Rollouts / Flagger Traffic Split Stable 90% / Canary 10% Stable (v1) Baseline pods Canary (v2) New-version pods Metrics Providers Prometheus / Datadog / CloudWatch AnalysisRun score each interval Promote (100%) Abort + Rollback set weight spawn query pass/fail all steps pass any step fails
حلقة تحليل الكناري الآلي: وحدة تحكم Rollout تقسم الحركة، تُنشئ AnalysisRun يستعلم من موفري المقاييس في كل فترة، وتُرقّي أو تُلغي بناءً على اجتياز عتبات المقاييس.

تمر الحلقة بأربع مراحل في كل فترة: الانتظار (جمع بيانات كافية)، الاستعلام (جلب المقاييس من منظومة المراقبة)، التقييم (مقارنة الكناري مع الخط الأساسي وفق السياسة)، القرار (زيادة الوزن أو التوقف أو الإلغاء). عدد الفترات وزيادة الوزن لكل خطوة هما مقابس الضبط الأساسيين.

Argo Rollouts: الترقية القائمة على المقاييس

Argo Rollouts يوسّع Kubernetes بـ CRD اسمه Rollout يحل محل Deployment العادي. قسم استراتيجية الكناري يحدد أوزان الخطوات ويشير إلى AnalysisTemplate يحتوي على استعلامات المقاييس.

# rollout.yaml — Argo Rollouts Rollout with canary analysis apiVersion: argoproj.io/v1alpha1 kind: Rollout metadata: name: api-service namespace: production spec: replicas: 10 selector: matchLabels: app: api-service template: metadata: labels: app: api-service spec: containers: - name: api-service image: ghcr.io/myorg/api-service:v2.4.1 resources: requests: cpu: "250m" memory: "256Mi" limits: cpu: "1" memory: "512Mi" strategy: canary: trafficRouting: istio: virtualService: name: api-service-vsvc routes: - primary steps: - setWeight: 5 # 5% حركة كناري - pause: {duration: 5m} - analysis: # تشغيل التحليل عند 5% templates: - templateName: success-rate - templateName: p99-latency - setWeight: 20 - pause: {duration: 10m} - analysis: templates: - templateName: success-rate - templateName: p99-latency - setWeight: 50 - pause: {duration: 10m} - analysis: templates: - templateName: success-rate - templateName: p99-latency canaryMetadata: labels: role: canary stableMetadata: labels: role: stable
# analysis-template.yaml — AnalysisTemplate يستعلم من Prometheus apiVersion: argoproj.io/v1alpha1 kind: AnalysisTemplate metadata: name: success-rate namespace: production spec: args: - name: service-name - name: canary-hash # يُحقنه Rollouts من pod-template-hash metrics: - name: success-rate interval: 1m count: 5 # 5 قياسات؛ يحتاج 4 من 5 للنجاح successCondition: result[0] >= 0.99 # معدل نجاح 99% على الأقل failureLimit: 1 provider: prometheus: address: http://prometheus.monitoring.svc:9090 query: | sum(rate(http_requests_total{ job="api-service", pod=~".*{{args.canary-hash}}.*", status!~"5.." }[2m])) / sum(rate(http_requests_total{ job="api-service", pod=~".*{{args.canary-hash}}.*" }[2m])) --- apiVersion: argoproj.io/v1alpha1 kind: AnalysisTemplate metadata: name: p99-latency namespace: production spec: metrics: - name: p99-latency interval: 1m count: 5 successCondition: result[0] < 0.300 # p99 أقل من 300 مللي ثانية failureLimit: 1 provider: prometheus: address: http://prometheus.monitoring.svc:9090 query: | histogram_quantile(0.99, sum(rate(http_request_duration_seconds_bucket{ job="api-service", pod=~".*{{args.canary-hash}}.*" }[2m])) by (le) )
دائماً استعلم من بودات الكناري مباشرةً، وليس من الخدمة. استعلام Prometheus يضرب الخدمة بأكملها سيحسب متوسط بودات الكناري والثابتة معاً ويُخفي تراجعاً خطيراً. صفِّ بعلامة pod باستخدام pod-template-hash الذي يُحقنه Rollouts كمعامل. وبالمثل، حدّد نطاق مقاييس Datadog الخاصة بك بعلامة version أو rollouts_pod_template_hash.

Flagger: البديل الأصيل لـ Kubernetes

Flagger مشروع CNCF تُحافظ عليه Weaveworks. بدلاً من CRD اسمه Rollout، يستخدم CRD اسمه Canary يُغلِّف Deployment موجوداً — Flagger ينشئ ويدير كائنات Deployment الأساسية والكناري نيابةً عنك. هذا يجعل إضافته على كلاستر موجود أسهل دون إعادة هيكلة المانيفستات.

# flagger-canary.yaml — Flagger Canary resource wrapping a Deployment apiVersion: flagger.app/v1beta1 kind: Canary metadata: name: api-service namespace: production spec: targetRef: apiVersion: apps/v1 kind: Deployment name: api-service progressDeadlineSeconds: 600 service: port: 8080 targetPort: 8080 gateways: - public-gateway.istio-system.svc.cluster.local hosts: - api.myorg.com analysis: interval: 1m # تقييم كل 60 ثانية threshold: 5 # إلغاء بعد 5 فشل متتالي maxWeight: 50 # لا تتجاوز 50% حركة كناري stepWeight: 10 # زيادة 10% لكل خطوة ناجحة metrics: - name: request-success-rate thresholdRange: min: 99 # يتطلب معدل نجاح >= 99% interval: 1m - name: request-duration thresholdRange: max: 500 # يتطلب p99 <= 500 مللي ثانية interval: 30s webhooks: - name: acceptance-test type: pre-rollout url: http://flagger-loadtester.test/ timeout: 30s metadata: type: bash cmd: "curl -sd 'test' http://api-service-canary.production/healthz | grep OK" - name: load-test url: http://flagger-loadtester.test/ timeout: 5s metadata: type: cmd cmd: "hey -z 1m -q 10 -c 2 http://api-service-canary.production/"
دائماً أوصِل حملاً اصطناعياً إلى الكناري أثناء التحليل. بدون حمل، تُعيد استعلامات Prometheus بيانات فارغة، وهو ما يعامله Flagger وArgo Rollouts على أنه نجاح افتراضي. استخدم webhook محمل Flagger أو مهمة k6 / hey منفصلة تضرب نقطة نهاية الكناري باستمرار خلال نافذة التحليل. لا تعتمد أبداً على حركة الإنتاج المحيطة وحدها — خاصةً في الخطوات المبكرة عندما يكون وزن الكناري 5-10% فقط.

اختيار المقاييس: ماذا تقيس

المقاييس التي تحللها هي أهم قرار تصميمي. استخدم هذا التدرج، مُرتباً حسب نسبة الإشارة إلى الضوضاء:

  • معدل أخطاء HTTP 5xx — الحد الأدنى. كناري يزيد معدل أخطاء الخادم من 0.1% إلى 2% يجب أن يفشل فوراً. قِسه كـ (الطلبات غير 5xx) / (إجمالي الطلبات) لتجنب القسمة على صفر عندما يكون الكناري بارداً.
  • شريحات كمون الطلب — p99 وp99.9 تلتقط تراجعات كمون الذيل التي يُخفيها متوسط الكمون. إصدار جديد يضاعف p99 مع إبقاء المتوسط كما هو سيُدهور تجربة 1% من المستخدمين — وهو ما يعني عند 100,000 طلب/ثانية 1,000 شخص في الثانية.
  • مقاييس الأعمال — للخدمات التي تواجه المستخدم: معدل بدء الدفع، معدل نجاح تسجيل الدخول، معدل النقر على نتائج البحث. هذه تلتقط تراجعات المنطق التي تُعيد 200 OK لكنها تحسب الإجابة الخاطئة.
  • إشباع الموارد — اتجاهات نمو CPU والذاكرة خلال نافذة التحليل. إصدار يبدو جيداً عند 10% من الحركة لكنه سيتعطل بسبب نفاد الذاكرة عند 100% قابل للاكتشاف مبكراً إذا تتبعت معدل نمو container_memory_working_set_bytes.

ميكانيكيات التراجع وأنماط فشل الإنتاج

عند فشل AnalysisRun، يحتاج كل من Argo Rollouts وFlagger إلى تصريف الكناري بنظافة. Argo Rollouts يضع وزن الكناري على 0، وينتظر حتى تنتهي الطلبات الجارية (يتحكم فيه progressDeadlineSeconds ومهلة تصريف شبكة الخدمة الخاصة بك)، ثم يُقلِّص ReplicaSet للكناري إلى 0. البودات الثابتة لم تتوقف عن الخدمة أبداً، لذا يعاني المستخدمون فقط من زيادة الكمون خلال نافذة الكناري — وليس انقطاعاً.

التحليلات غير الحاسمة هي نمط فشل صامت. إذا أعاد استعلام Prometheus بيانات فارغة (هدف الفحص معطل، خطأ في اسم المقياس، عدم تطابق التسمية)، يُصنِّف Argo Rollouts المقياس على أنه غير حاسم، لا فاشل. بشكل افتراضي، ثلاثة نتائج غير حاسمة متتالية تُلغي الطرح — لكن كثيراً من الفرق تُغيِّر هذه العتبة دون فهم الآثار. استعلام مُعطل يُعيد دائماً بيانات فارغة سيُلغي كل كناري للسبب الخاطئ، مما يجعل الفريق يفقد الثقة في النظام ويُعطِّله. دائماً اختبر استعلامات PromQL يدوياً في واجهة Prometheus قبل وضعها في AnalysisTemplate.

أهم عادة تشغيلية هي مراقبة kubectl argo rollouts get rollout api-service -w خلال نشر. هذا يبث تقدم الخطوات، ونتائج التحليل، والقيم الدقيقة للمقاييس التي قادت كل قرار نجاح/فشل — مما يجعل تحليلات ما بعد الحوادث واضحة عند إلغاء الكناري:

# مراقبة طرح حي (Argo Rollouts) kubectl argo rollouts get rollout api-service -w -n production # إجبار الترقية على تجاوز الخطوة الحالية (استخدم بحذر — يتجاوز التحليل) kubectl argo rollouts promote api-service -n production # إلغاء فوري والتراجع kubectl argo rollouts abort api-service -n production # سرد جميع AnalysisRuns لطرح (فحص قيم المقاييس لكل تشغيل) kubectl get analysisrun -n production -l rollout=api-service \ --sort-by=.metadata.creationTimestamp # وصف أحدث AnalysisRun للحصول على نتائج مقاييس تفصيلية kubectl describe analysisrun \ $(kubectl get analysisrun -n production -l rollout=api-service \ -o jsonpath='{.items[-1].metadata.name}')

تحليل الكناري الآلي ليس حلاً سحرياً — إنه جيد بقدر المقاييس التي تُغذيه. الانضباط يكمن في تصميم عتبات ضيقة بما يكفي لالتقاط التراجعات الحقيقية، ومتساهلة بما يكفي لتحمّل تباين الحركة الطبيعي. ابدأ محافظاً (إلغاء عند زيادة معدل الخطأ 1%، زيادة p99 بـ 50 مللي ثانية)، اجمع البيانات على معدلات الإنذارات الكاذبة عبر عدة إصدارات، وشدِّد أو خفِّف استناداً إلى الأدلة. بعد أشهر قليلة من التحليل الجيد الضبط، يتوقف فريقك عن الخوف من عمليات نشر مساء الجمعة.