أحمال عمل Kubernetes وإعدادها

الفحوصات: الحيوية والجاهزية والبدء

18 دقيقة الدرس 3 من 32

الفحوصات: الحيوية والجاهزية والبدء

لا تستطيع Kubernetes قراءة أفكار تطبيقك. تعرف أن الحاوية "تعمل" فور بدء العملية — لكن "تعمل" و"بصحة جيدة" أمران مختلفان تمامًا. قد تحتاج خدمة Java إلى 45 ثانية لتهيئة ذاكرتها المؤقتة. وقد يُصاب برنامج Go بحالة إغلاق تام دون أن يتعطل. وقد يبدأ خادم ويب باستقبال حركة المرور قبل أن يكون مجمّع اتصالات قاعدة البيانات جاهزًا. الفحوصات (Probes) هي الآلية التي تستخدمها Kubernetes للتمييز بين Pod سليم وجاهز، وآخر معطل أو في طور التهيئة أو مثقّل مؤقتًا.

الإعداد الخاطئ للفحوصات هو أحد أكثر أسباب حوادث الإنتاج شيوعًا في Kubernetes. إما أن تكون عدوانية للغاية فتتسبب في قتل Pods سليمة خلال ذروة حركة المرور، أو متساهلة للغاية فتوجّه الحركة إلى Pod غير قادر على خدمة الطلبات. تغطي هذه الدرس الأنواع الثلاثة للفحوصات وأوضاع فشلها وما تُعدّه فرق SRE في كبرى شركات التقنية فعليًا.

الأنواع الثلاثة للفحوصات

  • فحص الحيوية (Liveness) — يجيب عن السؤال: "هل هذه الحاوية لا تزال حية؟" إذا فشل، تقوم kubelet بإنهاء الحاوية وإعادة تشغيلها وفقًا لـ restartPolicy الخاص بالـ Pod.
  • فحص الجاهزية (Readiness) — يجيب عن السؤال: "هل هذه الحاوية جاهزة لاستقبال حركة المرور؟" إذا فشل، تُحذف الـ Pod من نقاط النهاية (Endpoints) الخاصة بكل Service مطابقة (توقف الحركة عنها) لكنها لا تُعاد تشغيلها.
  • فحص البدء (Startup) — يجيب عن السؤال: "هل انتهت هذه الحاوية من عملية الإقلاع البطيئة؟" طوال فترة انتظاره، يُعلَّق فحصا الحيوية والجاهزية. يعمل مرة واحدة عند الإطلاق، وبمجرد نجاحه لا يعمل مجددًا.
الفكرة الجوهرية: يتحكم فحص الجاهزية في حركة المرور، بينما يتحكم فحص الحيوية في إعادة التشغيل. وهما مستقلان. يمكن أن تكون الـ Pod حية لكن غير جاهزة (في طور الإحماء أو التصريف أو المثقّلة مؤقتًا) دون أن تُقتل. الخلط بين الاثنين هو أكبر خطأ في إعداد الفحوصات في بيئة الإنتاج.

آليات الفحص

تدعم الأنواع الثلاثة من الفحوصات ثلاث آليات للتحقق:

  • httpGet — ترسل kubelet طلب HTTP GET. أي استجابة 2xx أو 3xx تعني النجاح. استخدمها لخدمات HTTP لأنها تختبر الطبقة الكاملة أيضًا.
  • tcpSocket — تفتح kubelet اتصال TCP. النجاح = المنفذ مفتوح. استخدمها للبروتوكولات غير HTTP مثل gRPC وRedis وPostgres.
  • exec — تنفّذ kubelet أمرًا داخل الحاوية. كود الخروج 0 = نجاح. استخدمها عند الحاجة لفحوصات على مستوى التطبيق. تجنّبها للفحوصات عالية التكرار: تنشئ exec عملية جديدة في كل مرة.
  • grpc (k8s نسخة 1.24+) — تستدعي kubelet بروتوكول gRPC لفحص الصحة. مثالية للخدمات الأصيلة للـ gRPC.

معاملات الضبط المهمة

  • initialDelaySeconds — المدة التي تنتظرها بعد بدء الحاوية قبل أول فحص. مطلوبة إذا لم يكن لديك فحص بدء.
  • periodSeconds — مدة الانتظار بين كل فحص وآخر. الافتراضي 10 ثوانٍ.
  • timeoutSeconds — المدة التي تنتظر فيها kubelet الاستجابة. الافتراضي ثانية واحدة — خطير للغاية لأي شيء يصل إلى قاعدة بيانات.
  • failureThreshold — عدد الفشل المتتالي قبل اعتبار الفحص فاشلًا. الافتراضي 3.
  • successThreshold — عدد النجاح المتتالي للانتقال من فاشل إلى ناجح. يجب أن يكون 1 لفحصي الحيوية والبدء.
Probe Timeline: Startup then Liveness and Readiness time 0s 30s 60s 90s Startup Probe (active) ✓ passes Liveness Probe (active) FAIL → restart Readiness: PASS FAIL → no traffic Liveness + Readiness suspended Readiness suspended Startup Liveness Readiness startup succeeds
أثناء نشاط فحص البدء، يُعلَّق فحصا الحيوية والجاهزية. بمجرد نجاح فحص البدء، يعمل كلاهما بشكل مستقل.

مانيفست جاهز للإنتاج

فيما يلي مانيفست Deployment واقعي لخدمة Spring Boot ذات إحماء مدته 40 ثانية. يستخدم الأنواع الثلاثة من الفحوصات بشكل صحيح:

apiVersion: apps/v1 kind: Deployment metadata: name: orders-api namespace: production spec: replicas: 3 selector: matchLabels: app: orders-api template: metadata: labels: app: orders-api spec: containers: - name: orders-api image: myregistry/orders-api:v2.4.1 ports: - containerPort: 8080 startupProbe: httpGet: path: /actuator/health/liveness port: 8080 # السماح بحد أقصى 60 ثانية للإقلاع (6 * 10s) failureThreshold: 6 periodSeconds: 10 timeoutSeconds: 3 livenessProbe: httpGet: path: /actuator/health/liveness port: 8080 periodSeconds: 15 timeoutSeconds: 5 failureThreshold: 3 # 45 ثانية من الفشل المتتالي قبل إعادة التشغيل readinessProbe: httpGet: path: /actuator/health/readiness port: 8080 periodSeconds: 10 timeoutSeconds: 3 failureThreshold: 2 # 20 ثانية قبل حذف الـ Pod من نقاط نهاية الـ Service successThreshold: 1
افصل بين نقطتَي نهاية الحيوية والجاهزية. يعرض Spring Boot Actuator وفحوصات صحة ASP.NET ومعظم الأطر مسارات مختلفة لهذا الغرض. يجب أن يتحقق مسار الحيوية فقط من أن العملية غير مجمّدة (دون استدعاءات DB). أما مسار الجاهزية فيتحقق من التبعيات الخارجية (DB، Cache، APIs) — من المقبول أن يفشل تحت الحمل العالي لكي يوجّه موزع الحمل حركة المرور نحو الأقران السليمين.

فحص البدء: لماذا وُجد؟

قبل ظهور فحوصات البدء (k8s < 1.16)، كانت الفرق تستخدم initialDelaySeconds كبيرًا على فحص الحيوية لتغطية الإقلاع البطيء. المشكلة: إذا تجمّد Pod أثناء الإقلاع، لن تكتشف kubelet ذلك حتى تنتهي مدة التأخير. تحل فحوصات البدء هذه المشكلة بأناقة — تمنح نافذة إقلاع سخية مع الكشف السريع عن التجمّد بعد الإقلاع.

الصيغة هي: أقصى وقت إقلاع = failureThreshold × periodSeconds. اضبطها على 150-200% من وقت الإقلاع البارد المقاس عند النسبة المئوية 99. للمانيفست أعلاه: 6 × 10s = 60 ثانية.

أنماط الفشل الشائعة في الإنتاج

  • فحص الحيوية يصل إلى قاعدة البيانات — إذا كانت قاعدة البيانات بطيئة، يتجاوز الفحص المهلة الزمنية، تقتل kubelet Pods سليمة، وتدخل في حلقة تعطل تزيد الحمل على قاعدة البيانات. يجب أن يتحقق فحص الحيوية من العملية ذاتها فقط.
  • timeoutSeconds: 1 (الافتراضي) على فحص الجاهزية — يمكن لنقطة نهاية بمدة استجابة 200ms عند P99 أن تأخذ أحيانًا 2 ثانية تحت ضغط جمع البيانات غير المستخدمة. مهلة واحدة تُعدّ فشلًا. مع failureThreshold: 3 هذا 3 ثوانٍ فقط من التأخير قبل البدء بتصريف الـ Pod. اضبط timeoutSeconds على الأقل عند P99 × 3.
  • لا فحص بدء مع initialDelaySeconds صغير على JVM بطيء — تُطلق kubelet فحص الحيوية قبل انتهاء JVM من التحميل، ترى فشلًا، وتقتل الحاوية. تدخل الـ Pod في حلقة تعطل ولا تبدأ أبدًا. إضافة فحص بدء مع failureThreshold مناسب هو الحل.
  • نقطة نهاية جاهزية لا تفشل أبدًا — فحص جاهزية يُعيد 200 دائمًا حتى تحت الحمل الزائد لا يوفر أي حماية؛ تستمر حركة المرور في التوجه نحو Pod مشبعة. نفّذ منطق ضغط عكسي (مثلًا: أعد 503 عندما يتجاوز عمق طابور الطلبات حدًا معينًا).
لا تستخدم نقطة النهاية ذاتها لفحصي الحيوية والجاهزية إلا إذا فهمت العواقب تمامًا. إذا تحققت نقطة النهاية من قاعدة البيانات وكانت بطيئة، فإن فحص الجاهزية (بشكل صحيح) يسحب الـ Pod من حركة المرور. لكن فحص الحيوية أيضًا يفشل — وبعد failureThreshold من الفشل يقتل Pod سليمة تنتظر تبعية خارجية. وهكذا يتسبب انقطاع قاعدة البيانات في عاصفة إعادة تشغيل للـ Pods.

فحص حالة الفحوصات

استخدم kubectl describe pod <name> وانظر إلى قسم Events وكتلة Containers. تظهر أعطال الفحوصات كأحداث تحذيرية بسبب Unhealthy. يعكس العمود READY في kubectl get pod (مثل 0/1) حالة فحص الجاهزية.

# تدفق أحداث الفحص المباشر لـ Pod متعطلة kubectl describe pod orders-api-7d9f8c-xqr2k | grep -A 20 Events # مراقبة انتقالات جاهزية الـ Pod في الوقت الفعلي kubectl get pod -l app=orders-api -w # التحقق من إعداد الفحص كما تراه API Server kubectl get pod orders-api-7d9f8c-xqr2k -o jsonpath=\ '{.spec.containers[0].livenessProbe}'

فحص Exec للخدمات غير HTTP

بالنسبة لـ Redis sidecar أو Pod قاعدة بيانات، يُعدّ فحص exec الأسلوب الأمثل:

livenessProbe: exec: command: - redis-cli - ping initialDelaySeconds: 15 periodSeconds: 20 timeoutSeconds: 5
على نطاق كبرى شركات التقنية، يُشفَّر إعداد الفحوصات عادةً كقيم Helm ويُطبَّق عبر سياسات القبول في OPA/Kyverno التي ترفض Deployments بلا فحص جاهزية، أو ذات timeoutSeconds < 2، أو فحوصات حيوية تستعلم تبعيات خارجية. تُعامَل الفحوصات كعقد موثوقية، وليست لمسة نهائية.