Liveness و Readiness Probes: الفرق اللي بيوقف موقعك أو ينقذه
يتطلب مستوى مبتدئ. بعد المقال ده هتعرف بالظبط إمتى Kubernetes بيقطع الترافيك عن تطبيقك، وإمتى بيعيد تشغيله، وهتبطّل تعمل الغلطة الشائعة اللي بتحوّل بطء بسيط في قاعدة البيانات لسقوط كامل للخدمة.
الحكاية كلها في سؤالين مختلفين: "هل التطبيق جاهز يستقبل طلبات دلوقتي؟" و"هل التطبيق لسه حيّ أصلاً؟". خلط السؤالين مع بعض هو السبب رقم واحد في حوادث الـ restart المتتالي.
المشكلة باختصار
لما تعمل deploy، الـ Pod بياخد وقت لحد ما التطبيق يجهز فعلاً. لو Kubernetes بعت ترافيك عليه قبل ما يجهز، المستخدم بياخد أخطاء 502/503. وعلى الناحية التانية، لو التطبيق "علّق" (deadlock) وفضل شغّال بالاسم بس مش بيرد على أي طلب، محدش هيصلّحه غير لو حد قرر يعيد تشغيله يدويًا. Kubernetes بيحل المشكلتين دول بأداتين منفصلتين، وأغلب الناس بتستخدمهم بالغلط.
مثال بسيط الأول
خُد الموقف ده: سلسلة مطاعم فتحت فروع جديدة. عند باب كل فرع فيه حارس بيبص جوه ويقرر حاجتين مختلفتين تمامًا. الأولى: هل الفرع جاهز يستقبل زباين دلوقتي؟ لو المطبخ لسه بيسخّن، الحارس بيمنع دخول الزباين مؤقتًا، لكنه مش بيقفل الفرع. الثانية: هل الموظفين جوه لسه صاحيين وبيشتغلوا، ولا الفرع اتشلّ وفضل مفتوح على الفاضي؟ لو اتشلّ، الحارس بيقفله ويفتحه من الأول.
الحارس ده بالظبط هو الـ kubelet. القرار الأول هو readinessProbe (امنع الترافيك مؤقتًا). القرار التاني هو livenessProbe (اقتل وأعد التشغيل). دلوقتي نشوف ده علميًا.
الشرح العلمي: مين بيعمل إيه
الـ kubelet هو الوكيل اللي بيدير الـ Pods على كل node، وهو اللي بيشغّل الفحوص على فترات منتظمة. كل فحص نتيجته نجاح أو فشل. الفرق الحقيقي في اللي بيحصل بعد الفشل:
- livenessProbe: لو فشل عدد مرات متتالية (failureThreshold)، الـ kubelet بيقتل الـ container ويعيد تشغيله، وعدّاد restartCount بيزيد. الهدف: إنقاذ عملية معلّقة.
- readinessProbe: لو فشل، الـ Pod بيتشال من قائمة الـ Endpoints بتاعة الـ Service، فالترافيك يبطّل يتوجّه له — من غير ما يتقتل. الهدف: امنع الترافيك طول ما التطبيق مش جاهز.
وفيه فحص تالت اسمه startupProbe، للتطبيقات اللي بتاخد وقت في الإقلاع. ده بيوقف الـ liveness من إنه يقتل التطبيق وهو لسه بيسخّن.
الغلطة اللي بتوقّع الخدمة كلها
الطريقة الشائعة الغلط: تربط livenessProbe على endpoint بيفحص قاعدة البيانات أو خدمة خارجية. تعالَ نشوف اللي بيحصل فعلاً. الافتراض إن عندك 12 Pod بيخدموا 2000 طلب/ثانية. أول ما Postgres تاخد لحظة بطء (بسبب vacuum، أو نتوة شبكة)، الـ liveness بيفشل على الـ 12 Pod في نفس اللحظة. Kubernetes بيقتلهم كلهم ويعيد تشغيلهم مع بعض، فالخدمة تقع بالكامل، والـ DB اللي كانت بتتنفس تتضرب تاني بموجة إعادة اتصال. كده حوّلت بطء 3 ثواني لسقوط دقيقة أو أكتر.
القاعدة اللي بتفرق: liveness لازم يبقى خفيف وداخلي — يرد 200 طول ما الـ process شغّال ومش معلّق، من غير ما يلمس أي اعتمادية. وreadiness هو اللي ممكن يفحص الاعتماديات. كده لو الـ DB بطّأت، الـ Pods تتشال من الترافيك مؤقتًا وترجع لوحدها من غير أي restart.
مثال تنفيذي قابل للنسخ
ده Deployment فيه الفحوص التلاتة مظبوطين صح:
apiVersion: apps/v1
kind: Deployment
metadata:
name: orders-api
spec:
replicas: 3
selector:
matchLabels: { app: orders-api }
template:
metadata:
labels: { app: orders-api }
spec:
containers:
- name: app
image: registry.example.com/orders-api:1.4.2
ports:
- containerPort: 8080
# بيسخّن؟ الـ startupProbe بيدّي التطبيق لحد 60 ثانية يقلع قبل ما liveness يبدأ
startupProbe:
httpGet: { path: /healthz, port: 8080 }
failureThreshold: 30
periodSeconds: 2
# liveness خفيف: بيرد 200 طول ما الـ process مش معلّق — من غير ما يلمس الـ DB
livenessProbe:
httpGet: { path: /healthz, port: 8080 }
periodSeconds: 10
timeoutSeconds: 1
failureThreshold: 3
# readiness ممكن يفحص الاعتماديات: لو الـ DB مش موجودة، شيلني من الترافيك بس متقتلنيش
readinessProbe:
httpGet: { path: /ready, port: 8080 }
periodSeconds: 5
timeoutSeconds: 2
failureThreshold: 3وتتحقق إنه شغّال بالأوامر دي:
# راقب الـ Pods وهي بتجهز، وركّز على عمود READY و RESTARTS
kubectl get pods -w
# شوف تفاصيل الفحوص وآخر نتيجة ليها
kubectl describe pod <pod-name> | grep -A5 -E "Liveness|Readiness|Startup"الأرقام الافتراضية مهمة: periodSeconds=10 و failureThreshold=3، يعني الـ liveness بياخد حوالي 30 ثانية عشان ياخد قرار القتل. لو عايز رد أسرع، صغّرها — بس بحذر زي ما هنشوف.
الـ trade-offs وما يجب الانتباه له
الـ trade-off هنا كله في الأرقام. failureThreshold عالي (زي 6) بيخليك متتأثرش بأي نتوة عابرة، بس بيأخّر اكتشاف العطل الحقيقي — ممكن توصل لأكتر من دقيقة قبل أول restart. failureThreshold واطي (زي 1) بيرد بسرعة، بس بيخليك عرضة لـ restart على كل بطء لحظي، وده اسمه flapping. الافتراض هنا إنك تبدأ من القيمة الافتراضية (3 مرات كل 10 ثواني ≈ 30 ثانية) وتعدّل بالقياس مش بالإحساس. وكمان الفحوص نفسها لها تكلفة بسيطة: كل probe طلب HTTP إضافي كل بضع ثواني على كل Pod.
متى لا تستخدم هذه الطريقة
لو التطبيق بيقلع في أقل من ثانية ومالوش حالة "مش جاهز" حقيقية (زي static file server بسيط)، الـ readiness ممكن يبقى زيادة مش محتاجها. والأهم: متخليش readinessProbe بتاع خدمة يعتمد على خدمة تانية بشكل دائري، عشان متعملش انهيار متسلسل تبقى فيه كل الخدمات not ready مع بعض في نفس اللحظة. وفي الـ Jobs قصيرة العمر مفيش داعي لـ liveness من الأساس.
الخطوة التالية
افتح الـ Deployment بتاعك دلوقتي، وبُص على الـ livenessProbe: هو بيضرب على endpoint بيفحص قاعدة البيانات ولا لأ؟ لو آه، افصلهم فورًا: خلّي liveness على /healthz خفيف ما يلمسش اعتماديات، وانقل فحص الاعتماديات لـ /ready. اعمل kubectl apply وراقب عمود RESTARTS بـ kubectl get pods -w وقت الضغط. لو الـ restarts وقفت، يبقى الغلطة دي كانت بتضربك من غير ما تعرف.
المصادر
- توثيق Kubernetes الرسمي — Configure Liveness, Readiness and Startup Probes: kubernetes.io/docs/tasks/configure-pod-container/configure-liveness-readiness-startup-probes
- توثيق Kubernetes — Pod Lifecycle (Container probes): kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle
- توثيق Kubernetes — Service و Endpoints وعلاقتها بالـ readiness: kubernetes.io/docs/concepts/services-networking/service