Kubernetes Probes بالعربي: امنع restart loop قبل الإنتاج
مستوى القارئ: متوسط
هتخرج من المقال بإعداد واضح يخلي Kubernetes يستنى التطبيق وهو بيبدأ، يفتح الترافيك في الوقت الصح، ويعمل restart بس لما العملية تبقى فعلًا ميتة.
المشكلة باختصار
الطريقة الشائعة الغلط هي إنك تحط livenessProbe بسرعة وتفتكر إنك كده زودت الاعتمادية. اللي بيحصل فعلاً: التطبيق يبدأ ببطء، الـ probe يفشل أول 10 أو 15 ثانية، Kubernetes يقتله، وبعدها يبدأ من الصفر. النتيجة restart loop من غير bug حقيقي في الكود.
الافتراض إن عندك خدمة Web داخل Kubernetes، زمن الإقلاع الطبيعي بين 20 و35 ثانية، والـ endpoint الصحي اسمه /healthz. لو عندك موقع عليه 50K زيارة يوميًا، غلط صغير هنا ممكن يخلي rollout جديد يعمل downtime واضح لمدة 3 إلى 5 دقائق بدل ما يمر بسلاسة.
افهم الفرق بمثال بسيط
ركز في المثال ده: مطعم لسه بيفتح الصبح. startupProbe هو سؤال: هل المطبخ خلص تجهيز؟ readinessProbe هو سؤال: هل نستقبل طلبات العملاء دلوقتي؟ livenessProbe هو سؤال أخطر: هل المطعم قفل ومحتاج نعيد تشغيله؟ لو سألت السؤال التالت بدري، هتقفل المطعم وهو لسه بيرتب الترابيزات.
علميًا، Kubernetes يستخدم probes من kubelet عشان يعرف حالة الـ container. حسب توثيق Kubernetes الرسمي، startupProbe يعطل فحوصات liveness وreadiness إلى أن ينجح. دي نقطة مهمة لأنها تمنع قتل التطبيق أثناء الإقلاع البطيء. مصدر: Kubernetes: Configure Liveness, Readiness and Startup Probes.
الإعداد العملي
أفضل طريقة هنا: قيس زمن الإقلاع، ثم ادي startupProbe نافذة أكبر من الزمن الطبيعي بنسبة أمان 30% تقريبًا. لو التطبيق يبدأ في 25 ثانية، خلي نافذة startup حوالي 40 إلى 60 ثانية. بعد النجاح، readiness يفتح الترافيك، وliveness يحمي التشغيل الطويل.
apiVersion: apps/v1
kind: Deployment
metadata:
name: checkout-api
spec:
replicas: 3
selector:
matchLabels:
app: checkout-api
template:
metadata:
labels:
app: checkout-api
spec:
containers:
- name: app
image: ghcr.io/example/checkout-api:1.8.0
ports:
- containerPort: 8080
startupProbe:
httpGet:
path: /healthz
port: 8080
periodSeconds: 5
failureThreshold: 12
readinessProbe:
httpGet:
path: /ready
port: 8080
periodSeconds: 5
failureThreshold: 2
livenessProbe:
httpGet:
path: /healthz
port: 8080
initialDelaySeconds: 10
periodSeconds: 10
failureThreshold: 3
الحساب هنا بسيط: startupProbe عنده periodSeconds: 5 وfailureThreshold: 12. يعني Kubernetes هيدي التطبيق حوالي 60 ثانية قبل ما يعتبر الإقلاع فشل. لو الإقلاع الطبيعي 25 ثانية، عندك هامش 35 ثانية للـ cold start أو تحميل cache.
قِس قبل ما تزود الأرقام
بدل ما تزود initialDelaySeconds عشوائيًا، شغّل rollout واقيس أول نجاح للـ readiness. الأمر ده يديلك صورة سريعة:
kubectl rollout status deployment/checkout-api
kubectl get pods -l app=checkout-api -w
kubectl describe pod -l app=checkout-api | grep -A4 -E "Startup|Readiness|Liveness"
لو أول pod بقى Ready بعد 28 ثانية، ابدأ بـ startup window بين 45 و60 ثانية. لو بقى Ready بعد 90 ثانية، المشكلة غالبًا مش في probes فقط. راجع تحميل migrations، اتصال قاعدة البيانات، أو warmup cache. مصدر حالة الـ Pod ومعنى Ready موجود في توثيق Kubernetes الرسمي: Kubernetes: Pod Lifecycle.
الـ trade-off هنا
لما تزود نافذة startupProbe، بتكسب حماية من restart loop وقت الإقلاع. بتخسر إن Kubernetes هيستنى فترة أطول قبل ما يعلن إن النسخة فشلت. لو failureThreshold بيدي 60 ثانية، أي image مكسورة ممكن تستهلك دقيقة كاملة قبل الاستبدال. ده مقبول في تطبيق يبدأ ببطء، لكنه مش مناسب لخدمة المفروض تبدأ في أقل من 3 ثواني.
كمان خليك حذر من جعل /healthz يفحص كل التبعيات الخارجية. لو endpoint الصحة بيفشل لمجرد إن خدمة خارجية تأخرت 300ms، هتعمل restarts من غير سبب. الأفضل غالبًا: /healthz يفحص إن العملية حية، و/ready يفحص جاهزية استقبال الترافيك والتبعيات الأساسية.
متى لا تستخدم هذه الطريقة
لا تستخدم startupProbe كحل لتطبيق عالق في migration بتاخد دقيقتين كل مرة. أصلح migration أو انقلها Job منفصل. ولا تستخدم livenessProbe لإجبار التطبيق يعمل restart عند أي خطأ business. ده يخفي المشكلة ويكسر الطلبات الجارية. لو التطبيق CLI قصير العمر أو Job مش خدمة مستمرة، probes غالبًا مش هيضيفوا قيمة حقيقية.
مصادر المقال
الخطوة التالية
افتح Deployment واحد في مشروعك النهارده، قيس أول وقت يصل فيه الـ Pod إلى Ready، وبعدها اضبط startupProbe بنافذة أكبر 30% من الرقم الحقيقي بدل ما تزود initialDelaySeconds بالظن.