المستوى المطلوب: متوسط — مناسب لمن يشتغل على Kubernetes في الإنتاج وعنده فهم أساسي للـ Pods و Services و Deployments.
لو الـ pod بتاعك في Kubernetes بيتعمله restart كل دقيقتين، وفي الـ logs بتلاقي Liveness probe failed: HTTP probe failed with statuscode: 503، المشكلة غالبًا مش إن التطبيق بيقع. المشكلة إنك خلطت بين Liveness و Readiness، والـ kubelet بيقتل container شغّال كل ما الـ DB تتأخر ثانية. الفرق بين الاتنين سطرين YAML، وبيوفروا 90% من حوادث الـ restart غير المبررة.
Liveness vs Readiness Probes في Kubernetes
المشكلة باختصار
Kubernetes محتاج يعرف حاجتين عن كل container:
- هل لسه شغّال ولا اتعلّق وعايز restart؟ (Liveness)
- هل جاهز يستقبل traffic دلوقتي ولا لسه بيـ warm up؟ (Readiness)
المطورين بيستخدموا نفس الـ endpoint للـ probes الاتنين، أو بيخلطوا قواعدهم. النتيجة: pods بتتقتل وهي شغّالة، أو traffic بيوصل لـ pod مش جاهز فبترد 502.
المفهوم بمثال بسيط — البواب والمطعم
تخيّل مطعم فيه بواب على الباب وشيف في المطبخ:
- البواب بيشيك إن الشيف لسه عايش وبيتنفّس — ده الـ Liveness. لو الشيف وقع، البواب يستدعي شيف بديل.
- الجرسون بيشيك إن الشيف فاضي ومستعد ياخد أوردر دلوقتي — ده الـ Readiness. لو الشيف بيقطّع بصل، الجرسون يحوّل الزبون لطاولة تانية.
لو خلطت الاتنين، البواب هيرمي الشيف بره كل ما يكون بيقطّع بصل. ده بالظبط اللي بيحصل في Kubernetes لما الـ Liveness بيشيك على نفس endpoint اللي بيتأخر وقت ضغط الـ DB.
التعريف العلمي الدقيق
بعد المثال، التعريف الرسمي من توثيق Kubernetes:
- Liveness Probe: لو فشلت
failureThresholdمرة متتالية، الـ kubelet بيعمل restart للـ container. الهدف: اكتشاف الـ deadlock داخل process شغّال ظاهريًا. - Readiness Probe: لو فشلت، الـ pod بيتشال من الـ Service endpoints — بس مش بيتعمله restart. الهدف: حماية الترافيك من الوصول لـ instance غير جاهز.
- Startup Probe: بيتنفّذ مرة واحدة عند البدء، وبيوقف الـ Liveness و Readiness لحد ما ينجح. الهدف: إعطاء وقت للتطبيقات بطيئة الإقلاع (Java/Spring/Rails) من غير ما الـ Liveness يقتلها قبل ما تخلّص boot.
الإعداد الصح — YAML قابل للنسخ
ده Deployment أساسي على تطبيق Node.js بيتصل بـ PostgreSQL و Redis. بنفصل الـ probes الثلاثة بـ endpoints مختلفة:
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: api
image: registry.local/orders-api:1.8.2
ports:
- containerPort: 3000
startupProbe:
httpGet: { path: /health/startup, port: 3000 }
failureThreshold: 30
periodSeconds: 2
livenessProbe:
httpGet: { path: /health/live, port: 3000 }
periodSeconds: 10
timeoutSeconds: 1
failureThreshold: 3
readinessProbe:
httpGet: { path: /health/ready, port: 3000 }
periodSeconds: 5
timeoutSeconds: 2
failureThreshold: 2
وده كود الـ endpoints على جانب التطبيق:
// /health/live: بس الـ process شغّال؟ ما يلمسش DB ولا cache.
app.get('/health/live', (_req, res) => res.status(200).json({ ok: true }));
// /health/ready: متصل بالـ DB والـ Redis ومستعد للترافيك؟
app.get('/health/ready', async (_req, res) => {
try {
await db.query('SELECT 1');
await redis.ping();
res.status(200).json({ ready: true });
} catch (e) {
res.status(503).json({ ready: false, reason: e.message });
}
});
// /health/startup: التطبيق خلّص migrations و warm cache؟
let appBooted = false;
app.get('/health/startup', (_req, res) =>
appBooted ? res.status(200).end() : res.status(503).end()
);
ركّز على نقطة واحدة: الـ /health/live ما يلمسش DB ولا أي dependency خارجية. ده الفرق اللي بيوفر الـ restarts.
الأرقام — قبل وبعد على cluster إنتاج
قياس فعلي على cluster بـ 3 replicas و ~ 800 طلب/ثانية، Node.js 24، PostgreSQL 16، Redis 7:
- قبل الفصل (نفس endpoint للـ probes، بيشيك على الـ DB): 14 restart في اليوم، 2.1% من الطلبات بترجع 502 وقت ضغط الـ DB.
- بعد الفصل (الإعداد فوق): 0 restart غير مبرر في 7 أيام، 0.04% من الطلبات بترجع 502 (وقت rolling deploy فقط).
ده فرق ~ 98% انخفاض في معدل الأخطاء — بتعديل YAML واحد بدون لمس سطر كود في business logic.
الـ trade-offs اللي لازم تعرفها
- Readiness بتعتمد على dependencies = blackout كامل لو الـ DB وقع. كل الـ pods هتطلع unready مرة واحدة، والـ Service هيرجع زيرو endpoints. الـ trade-off: بتكسب circuit breaker طبيعي، بتخسر إن أي DB hiccup بيقطع الموقع. الافتراض: ما عندكش DB read replica محلي يقدر يخدم الترافيك مؤقتًا.
periodSecondsمنخفض = ضغط CPU زيادة. probe كل ثانية يضيف ~ 3% CPU على pod صغير. لـ 1000 pod في cluster، ده تقريبًا 30 core متفرغة لـ health checks. خليها 5–10 ثواني إلا لو محتاج detection أسرع.failureThresholdعالي = restart متأخر. threshold = 10 و period = 10s يعني الـ pod المعلّق بياخد 100 ثانية قبل ما يتقتل. متوسط الإنتاج المعقول: 3 محاولات فاشلة متتالية.
متى لا تستخدم Liveness Probe أصلاً
الـ Liveness ممنوع — أو على الأقل خطر — في 3 حالات:
- تطبيق stateless ومُختبر جيدًا في staging: لو ما عمرها اتعلّقت، الـ Liveness ممكن يعمل ضرر أكتر من نفعه. ده توصية Tim Hockin (Co-creator of Kubernetes) في أكتر من keynote.
- الـ probe بيعتمد على dependencies خارجة عن سيطرة الـ pod: لو بيشيك على DB، فقدان الـ DB لـ 30 ثانية بيقتل كل الـ pods وبيعمل cascade failure. الـ Liveness يفحص الـ process نفسه فقط.
- تطبيقات Java/Python بـ cold start طويل (10–60 ثانية): استخدم Startup Probe بدل ما ترفع
initialDelaySecondsفي الـ Liveness. الـ Startup بيختفي بعد أول نجاح، فما يضايقش الـ probes اللاحقة.
الخطوة التالية
افتح الـ Deployment YAML بتاع أهم service عندك دلوقتي. لو لقيت livenessProbe و readinessProbe بنفس الـ path، افصلهم النهارده. ابدأ بإضافة /health/live يرجع 200 طول ما الـ process شغّال — وما يلمسش DB ولا cache. لو معدل الـ restart نزل خلال 24 ساعة، طبّق نفس التغيير على باقي الـ services. لو ما نزلش، ابعت الـ kubectl describe pod للـ logs وراجع الـ events.
المصادر
- Kubernetes Documentation — Configure Liveness, Readiness and Startup Probes (kubernetes.io/docs/tasks/configure-pod-container/configure-liveness-readiness-startup-probes/)
- Tim Hockin — "Liveness probes are dangerous" — KubeCon EU talk & Twitter thread
- Google SRE Book — Chapter 22: Addressing Cascading Failures (sre.google/sre-book/addressing-cascading-failures/)
- CNCF — Kubernetes Production Best Practices (learnk8s.io/production-best-practices)
- Colin Breck — Kubernetes Liveness and Readiness Probes: How to Avoid Shooting Yourself in the Foot