EduFlow

الوحدة 5 · التشغيل في الإنتاج

التحديثات المتدرّجة والتراجع وHelm

20 دقراءة 4 داملأ الفراغات

الدرس في لمحة

اضبط التحديث المتدرّج لـ notes-api حتى لا تقلّل الإصدارات السعة أبدًا، وراقب rollout سيئًا وتراجع عنه بـ kubectl، وغلّف الـ manifests في Helm chart بقيم خاصة بكل بيئة.

  1. 1كيف يعمل التحديث المتدرّج
  2. 2انشر وراقب وتراجع
  3. 3التغليف بـ Helm

لا فيديو هنا، بل الشيء الحقيقي: درس مكتوب وأمثلة قابلة للتشغيل واختبار وتدريب.

الإصدار الذي علّمني احترام الـ rollouts خرج يوم جمعة في الرابعة عصرًا (أعرف). كان في الإصدار الجديد خطأ إملائي في اسم متغيّر بيئة، فلم يستطع الوصول إلى قاعدة البيانات. لم يكن في الـ Deployment أي readiness probe، وكانت إعدادات الـ rollout افتراضية، فاستبدل Kubernetes بكل سرور كل Pod يعمل بآخر يُرجع أخطاء 500. استغرق الأمر ست دقائق لنلاحظ، وأربعين ثانية لنتراجع. هذا الدرس عن جعل الجزء الأول مستحيلًا والجزء الثاني روتينيًا.

كيف يعمل التحديث المتدرّج

حين تغيّر أي شيء في قالب الـ Pod في Deployment، وعادةً الـ image، ينشئ الـ Deployment ReplicaSet جديدًا للقالب الجديد ويبدأ في نقل النسخ من القديم إلى الجديد. إعدادان يحدّدان الوتيرة:

k8s/deployment.yaml
spec:
  revisionHistoryLimit: 10
  minReadySeconds: 10
  progressDeadlineSeconds: 300
  strategy:
    type: RollingUpdate
    rollingUpdate:
      maxSurge: 1
      maxUnavailable: 0
  • maxSurge: كم Pod فوق العدد المرغوب يمكن أن يوجد أثناء التحديث. مع 4 نسخ وmaxSurge: 1، يمكن أن يوجد 5 Pods في وقت واحد.
  • maxUnavailable: كم Pod تحت العدد المرغوب يُتسامح معه. عند 0 لا تنخفض السعة أبدًا.
  • minReadySeconds: يجب أن يبقى الـ Pod الجديد جاهزًا هذه المدة قبل أن يُعدّ متاحًا. هذا يلتقط الإصدارات التي تجتاز الـ readiness ثم تنهار بعد عشر ثوانٍ.
  • progressDeadlineSeconds: إذا لم يحرز الـ rollout أي تقدّم طوال هذه المدة، يُبلغ الـ Deployment بأنه فشل.

القيم الافتراضية هي 25% للزيادة و25% لعدم الإتاحة. أنا أضبط maxUnavailable: 0 على كل خدمة تواجه المستخدمين: لا يُزال Pod قديم إلا بعد أن يصبح Pod جديد جاهزًا. ومع الـ readiness probe من الدرس 5.1، لا يستطيع إصدار معطوب أخذ الحركة من إصدار يعمل. إنه يتوقّف مع Pod جديد واحد غير جاهز بينما تخدم الـ Pods القديمة.

أربع خطوات من تحديث متدرّج لـ 4 نسخ. كل خطوة تضيف Pod واحدًا من الإصدار الجديد، وتنتظر جاهزيته، ثم تزيل Pod واحدًا من الإصدار القديم، فيوجد دائمًا 4 Pods جاهزة على الأقل. البداية زيادة ‏Pod جديد جاهز تبديل إزالة Pod قديم انتهى الـ ReplicaSet القديم عند 0 رمادي: 1.4.0 · أخضر: 1.5.0 · كرّر حتى يُستبدل الكل
تحديث متدرّج مع maxSurge 1 وmaxUnavailable 0

الاستراتيجية الأخرى، Recreate، تحذف كل الـ Pods القديمة قبل تشغيل الجديدة. استخدمها فقط حين لا يستطيع الإصداران فعلًا العمل جنبًا إلى جنب، واقبل التوقّف.

انشر وراقب وتراجع

غيّر الـ image في k8s/deployment.yaml وطبّق، ثم راقب:

Terminal
kubectl apply -f k8s/deployment.yaml
kubectl rollout status deployment/notes-api --timeout=5m

الأمر rollout status ينتظر حتى يكتمل الـ rollout أو يفشل، ويخرج برمز غير صفري عند الفشل، فيستطيع الـ CI استخدامه كخطوة «هل نجح النشر؟». إذا فشل، أو صارت لوحات المتابعة حمراء بعد نجاحه:

Terminal
kubectl rollout history deployment/notes-api
kubectl rollout undo deployment/notes-api
kubectl rollout undo deployment/notes-api --to-revision=7

كل ReplicaSet قديم (حتى revisionHistoryLimit) هدف تراجع جاهز. الأمر undo يوسّع السابق من جديد باستخدام قواعد التحديث المتدرّج نفسها، فيكون التراجع آمنًا مثل التقدّم ويستغرق بضع ثوانٍ. ولتجعل history مقروءًا، سجّل سبب وجود كل revision: ‏kubectl annotate deployment/notes-api kubernetes.io/change-cause="1.5.0: bulk export".

بعد الـ undo، يشغّل الـ cluster الإصدار 1.4.0 بينما يقول Git ‏1.5.0. اعكس الـ commit فورًا، وإلا فسيعيد الـ apply الروتيني التالي نشر الخلل.

التغليف بـ Helm

صار مجلد k8s/ الخاص بـ notes-api ستة ملفات، وبيئة staging تحتاج إلى عدد نسخ وموارد وtags مختلفة عن الإنتاج. Helm يغلّف الـ manifests في chart: قوالب فيها عناصر نائبة، مع ملف values.yaml للقيم الافتراضية تتجاوزه كل بيئة.

Text
charts/notes-api/
  Chart.yaml
  values.yaml
  templates/
    deployment.yaml
    service.yaml
    hpa.yaml
    configmap.yaml
charts/notes-api/templates/deployment.yaml
      containers:
        - name: api
          image: "{{ .Values.image.repository }}:{{ .Values.image.tag }}"
          resources:
            {{- toYaml .Values.resources | nindent 12 }}
charts/notes-api/values-prod.yaml
image:
  repository: ghcr.io/skylane/notes-api
  tag: "1.5.1"
resources:
  requests: { cpu: 250m, memory: 256Mi }
  limits: { memory: 512Mi }

الأوامر تحاكي ما فعلته بـ kubectl:

Terminal
helm lint charts/notes-api
helm template notes-api charts/notes-api -f charts/notes-api/values-prod.yaml
helm upgrade --install notes-api charts/notes-api -n notes -f charts/notes-api/values-prod.yaml
helm history notes-api -n notes
helm rollback notes-api 4 -n notes

الأمر helm template يولّد الـ YAML محليًا حتى تراجعه. وupgrade --install يثبّت في المرة الأولى ويرقّي بعدها، مسجّلًا release revision مرقّمة في كل مرة. وhelm rollback يعيد تطبيق المجموعة الكاملة من manifests ‏revision سابقة، بما فيها الـ ConfigMap، وهذا أوسع من kubectl rollout undo. ‏Helm 4، الصادر أواخر 2025، أعاد تسمية بعض الخيارات؛ فمثلًا --atomic، الذي يتراجع تلقائيًا حين تفشل الترقية، صار الآن --rollback-on-failure.

تستطيع الآن شحن إصدار ومراقبته وعكسه بثقة. الدرس الأخير يُدخل الحركة إلى الـ cluster كما يجب، ويجمع دليل التشخيص لتلك اللحظات التي يحدث فيها خلل رغم كل شيء.

أهم النقاط

  • التحديث المتدرّج يوسّع ReplicaSet جديدًا ويقلّص القديم، بوتيرة يضبطها maxSurge وmaxUnavailable، ولا يتقدّم إلا بقدر ما تسمح به الـ readiness.
  • مع maxUnavailable على 0، يتوقّف الإصدار الذي لا تصبح الـ Pods الخاصة به جاهزة أبدًا دون ضرر، بينما يستمر الإصدار القديم في الخدمة.
  • الأمر kubectl rollout undo يعود إلى الـ ReplicaSet السابق خلال ثوانٍ، لكن حدّث Git أيضًا وإلا فسيعيد الـ apply التالي نشر الإصدار السيئ.
  • التراجع عن الكود لا يتراجع عن قاعدة البيانات، لذا يجب أن تعمل تغييرات المخطّط مع الإصدارين القديم والجديد معًا.
  • ‏Helm يغلّف الـ manifests كقوالب مع قيم؛ والأمر helm upgrade --install ينشر، وhelm rollback يعود إلى release revision سابقة.

قراءات إضافية

تحقّق من فهمك

4 أسئلة

السؤال 1 من 4

يعمل notes-api بـ 4 نسخ مع maxSurge 1 وmaxUnavailable 0. ‏Pods الإصدار 1.5.0 تنهار عند البدء. ماذا يلاحظ المستخدمون؟

السؤال 2 من 4

شغّلت kubectl rollout undo deployment/notes-api أثناء حادث فتعافت الخدمة. ما الذي يجب أن يحدث بعد ذلك؟

السؤال 3 من 4

الإصدار 1.5.0 يعيد تسمية العمود body في قاعدة البيانات إلى content عبر migration. في 1.5.0 خلل فتتراجع إلى 1.4.0. ماذا يحدث؟

السؤال 4 من 4

تريد رؤية YAML ‏Kubernetes الدقيق الذي سينتجه Helm chart للإنتاج قبل تثبيته. أي أمر تستخدم؟

تدريب

املأ الفراغات

راقبه، ثم استرجعه

الإصدار 1.5.0 قيد النشر (rollout). املأ الفراغات لانتظار الـ rollout، وسرد revisions الـ Deployment، والتراجع إلى السابقة، وإعادة release ‏staging الذي يديره Helm إلى الـ revision رقم 4.

# Wait for the 1.5.0 rollout to finish (fails after 5 minutes) kubectl rollout deployment/notes-api --timeout=5m # It's failing in production: list revisions, then go back one kubectl rollout deployment/notes-api kubectl rollout deployment/notes-api # On the Helm-managed staging cluster, return to release revision 4 helm notes-api 4 -n notes