عندما يدخل pod في Kubernetes إلى حالة CrashLoopBackOff، تبدأ الحاوية ثم تنطلق فوراً. ينتظر Kubernetes، يرى الانطلاق، وينعشها مرة أخرى—حلقة بلا نهاية. يوضح هذا الدليل كيفية العثور على السبب الجذري وإصلاحه.
الأعراض
حالة pod تظهر CrashLoopBackOff. عدد الإعادات مستمر في الزيادة. الأحداث تظهر رسائل مثل:
Name: example-app-5f7c9d4bfb-7x8kq
Namespace: default
Status: Running
Restarts: 47 (2 minutes ago)
Last State: Terminated
Reason: Error
Exit Code: 1
أو في kubectl get events:
pod-name CrashLoopBackOff exit status 1
pod-name BackOff Back-off restarting failed container
لماذا يحدث
مرتبة حسب الاحتمالية:
- التطبيق يتعطل عند البدء — الكود يحتوي على خطأ، يرمي استثناء غير معالج، أو ينهار قبل الوصول لحلقة الخادم الرئيسية.
- أمر أو وسائط خاطئة —
commandأوargsفي مواصفات pod خاطئة، تفتقد علامات اقتباس، أو تشير لملف تنفيذي غير موجود. - متغير بيئة مفقود، ملف إعدادات، أو سري — التطبيق يتوقع ملف إعدادات أو متغير بيئة غير مثبت أو معين.
- مسبار الحياة أو بدء التشغيل يفشل فوراً — يقتل Kubernetes pod معتقداً أنه غير صحي، حتى وإن لم ينته من البدء.
- نفاد الذاكرة (OOM) — التطبيق يخصص ذاكرة كثيرة جداً بسرعة والنواة تنهيه (رمز الخروج 137).
- التبعية غير جاهزة — التطبيق يحاول الاتصال بقاعدة بيانات أو خدمة لم تبدأ بعد ويفشل بشدة بدلاً من الإعادة.
- إذن مرفوض أو مشكلة نظام ملفات — التطبيق لا يستطيع الكتابة للدلائل المطلوبة أو قراءة ملفات الإعدادات بسبب ملكية أو قواعد الإذن.
الحل
1. التطبيق يتعطل عند البدء
احصل على رسالة الخطأ الفعلية:
# اعرض آخر إخراج قبل التعطل
kubectl logs <pod-name> --previous
# كرّر أمر السجلات عدة مرات لمراقبة دورات إعادة تشغيل متعددة
kubectl logs <pod-name> --previous
kubectl logs <pod-name> --previous
إذا أعادت --previous لا شيء (لا توجد حاوية سابقة)، تعطل pod بسرعة جداً بحيث لم تُكتب السجلات. استخدم حاوية تصحيح مؤقتة لفحص نظام الملفات:
# ابدأ pod تصحيح بنفس نقاط التثبيت والصورة الأساسية
kubectl debug <pod-name> -it --image=<same-base-image>
داخل حاوية التصحيح، تحقق من:
– أخطاء البناء في نصوص البدء: cat /app/start.sh | bash -x
– المكتبات المفقودة: ldd /app/binary
– هيكل الدليل: ls -la /app
2. أمر أو وسائط خاطئة
افحص الأمر الدقيق:
kubectl get pod <pod-name> -o yaml
ابحث عن حقول command وargs:
containers:
- name: app
command: ["/bin/sh"]
args: ["-c", "node /app/server.js --port=8080"]
الأخطاء الشائعة:
– علامة وقيمتها منقسمة بالخطأ: args: ["--config", "=/etc/app/config.yaml"] يجب أن تكون args: ["--config=/etc/app/config.yaml"] أو args: ["--config", "/etc/app/config.yaml"]
– مسار خاطئ: command: ["/bin/bash"] لكن الصورة مبنية على Alpine (استخدم /bin/sh بدلاً من ذلك)
– شرطة مفقودة: command: ["node"] بدلاً من command: ["/usr/bin/node"]
أصلحها في Deployment:
apiVersion: apps/v1
kind: Deployment
metadata:
name: example-app
spec:
template:
spec:
containers:
- name: app
image: example:latest
command: ["/bin/sh"]
args: ["-c", "exec node /app/server.js"]
ثم أعد النشر:
kubectl rollout restart deployment/example-app
3. متغير بيئة مفقود، ملف إعدادات، أو سري
تحقق مما يتوقعه التطبيق:
kubectl logs <pod-name> --previous | grep -i "not found\|undefined\|missing"
تحقق من وجود جميع متغيرات البيئة والتثبيتات:
kubectl get pod <pod-name> -o yaml | grep -A 20 "env:\|volumeMounts:"
أضف الإعدادات المفقودة كـ ConfigMap:
# أنشئ ConfigMap من ملف
kubectl create configmap app-config --from-file=config.yaml
ثبتها في Deployment:
containers:
- name: app
volumeMounts:
- name: config
mountPath: /etc/app
readOnly: true
volumes:
- name: config
configMap:
name: app-config
للأسرار (مفاتيح API، كلمات المرور)، استخدم Secret:
kubectl create secret generic app-secrets --from-literal=DB_PASSWORD=mypass
ثم ارجع إليه:
containers:
- name: app
env:
- name: DB_PASSWORD
valueFrom:
secretKeyRef:
name: app-secrets
key: DB_PASSWORD
أعد النشر:
kubectl rollout restart deployment/example-app
4. مسبار الحياة أو بدء التشغيل يفشل فوراً
المسبار يقتل pod قبل أن يكون لديه وقت للبدء. تحقق من المسبار الحالي:
kubectl get pod <pod-name> -o yaml | grep -A 10 "livenessProbe:\|startupProbe:"
المشكلة الأكثر شيوعاً: initialDelaySeconds قصير جداً. إذا استغرق التطبيق 10 ثوان للبدء والمسبار يعمل بعد 5 ثوان، سيفشل و pod سيعاد تشغيله.
أصلحها في Deployment:
containers:
- name: app
livenessProbe:
httpGet:
path: /health
port: 8080
initialDelaySeconds: 30 # أعط التطبيق وقتاً للبدء
periodSeconds: 10 # تحقق كل 10 ثوان
failureThreshold: 3 # أعد التشغيل بعد 3 فشل
startupProbe:
httpGet:
path: /health
port: 8080
failureThreshold: 30 # السماح بما يصل إلى 300 ثانية (30 * 10s) للأصحة
periodSeconds: 10
لقاعدة بيانات تستغرق وقتاً للتهيئة:
startupProbe:
exec:
command: ["pg_isready", "-h", "localhost"]
failureThreshold: 30
periodSeconds: 10
livenessProbe:
exec:
command: ["pg_isready", "-h", "localhost"]
initialDelaySeconds: 5
periodSeconds: 10
أعد النشر والمراقبة:
kubectl rollout restart deployment/example-app
kubectl describe pod <pod-name>
5. نفاد الذاكرة (OOM)
رمز الخروج 137 مع السبب OOMKilled يعني أن النواة أنهت العملية لاستخدام ذاكرة كثيرة جداً. تحقق من استخدام الذاكرة:
# إذا كان metrics-server مثبتاً
kubectl top pod <pod-name>
# أو وصف لترى إذا كان OOMKilled في الحالة
kubectl describe pod <pod-name> | grep -A 5 "Last State"
مواصفات pod على الأرجح تحتوي على حد ذاكرة منخفض جداً. زده:
containers:
- name: app
resources:
requests:
memory: "256Mi"
cpu: "100m"
limits:
memory: "512Mi" # زد هذا
cpu: "500m"
إذا كان لديك تطبيق JVM، عيّن أيضاً حدود الكومة:
JAVA_OPTS="-Xmx512m -Xms256m"
لـ Node.js، عيّن max old space size:
node --max-old-space-size=512 app.js
طبّق المواصفات المحدّثة:
kubectl apply -f deployment.yaml
kubectl rollout restart deployment/example-app
6. التبعية غير جاهزة
التطبيق يفشل بشدة عندما لا يتمكن من الاتصال بقاعدة بيانات أو خدمة أخرى عند البدء. استخدم init container للانتظار للتبعية:
containers:
- name: app
image: example:latest
initContainers:
- name: wait-for-db
image: busybox:1.36
command: ['sh', '-c', 'until nc -z postgres.default.svc.cluster.local 5432; do echo waiting for postgres; sleep 2; done']
أو أضف منطق الإعادة لبدء التطبيق:
for i in {1..30}; do
if psql -h postgres.default.svc.cluster.local -U user -d mydb -c "SELECT 1"; then
break
fi
echo "Database not ready, retry $i/30"
sleep 2
done
exec node /app/server.js
تأكد من وجود Service للتبعية وتشغيله:
kubectl get svc postgres
kubectl get pod -l app=postgres
7. إذن مرفوض أو مشكلة نظام ملفات
التطبيق لا يستطيع قراءة أو كتابة ملفات مطلوبة. تحقق من المستخدم الذي يعمل عليه pod وأذونات الملف:
kubectl debug <pod-name> -it --image=<same-image>
داخل حاوية التصحيح:
# من أنت؟
id
# هل يمكنك قراءة ملف الإعدادات؟
ls -la /etc/app/config.yaml
cat /etc/app/config.yaml
# هل يمكنك الكتابة لدليل البيانات؟
touch /data/test.txt
rm /data/test.txt
إذا تم رفع الإذن، أصلحه في Dockerfile أو مواصفات pod:
FROM alpine:3.20
RUN adduser -D appuser
RUN mkdir -p /app /data && chown -R appuser:appuser /app /data
USER appuser
أو في Deployment:
containers:
- name: app
securityContext:
runAsUser: 1000
runAsGroup: 3000
fsGroup: 2000
volumeMounts:
- name: data
mountPath: /data
volumes:
- name: data
emptyDir: {}
كيف تتجنبه
-
اختبر الحاوية محلياً أولاً:
docker run --rm example:latestدعها تعمل لمدة 10 ثوان على الأقل لالتقاط التعطلات الفورية.
-
عيّن طلبات وحدود الموارد الصحيحة لتجنب OOM:
resources: requests: memory: "256Mi" cpu: "100m" limits: memory: "512Mi" cpu: "500m" -
استخدم مسابير البدء إذا استغرق التطبيق وقتاً للبدء:
startupProbe: httpGet: path: /health port: 8080 failureThreshold: 30 periodSeconds: 10 -
شغّل التطبيق كمستخدم غير root في Dockerfile.
-
استخدم init containers للانتظار للتبعيات قبل بدء التطبيق الرئيسي.
-
سجّل أخطاء البدء إلى stderr بحيث تظهر في
kubectl logs --previous.
الأسئلة الشائعة
كيف أرى سبب كون pod في حالة CrashLoopBackOff؟
شغّل kubectl logs <pod> --previous لترى آخر تعطل. ثم kubectl describe pod <pod> لترى عدد الإعادات وفشل المسبارات. أخيراً، kubectl get events لترى التحذيرات الحديثة.
هل CrashLoopBackOff يعني أن سحب الصورة فشل؟
لا، هذا سيكون ImagePullBackOff. CrashLoopBackOff يعني أن الحاوية بدأت ولكن انطلقت برمز خطأ غير صفري. تحقق من kubectl logs --previous لترى الخطأ الفعلي.
كم من الوقت ينتظر Kubernetes قبل الإعادة بعد تعطل؟
يستخدم exponential backoff: 10 ثانية، 20 ثانية، 40 ثانية، 80 ثانية، 160 ثانية، 300 ثانية (محدود بـ 5 دقائق). لذا بعد 5 دقائق من التعطلات، تحدث الإعادات كل 5 دقائق.
هل يمكن لمسبار الحياة أن يسبب CrashLoopBackOff؟
نعم. إذا فشل المسبار و restartPolicy هو Always، يعيد Kubernetes تشغيل pod. زد initialDelaySeconds لإعطاء التطبيق وقتاً للبدء. اعرض تكوين المسبار باستخدام kubectl get pod -o yaml.
كيف تم التحقق من هذا الدليل
كُتب يدويًا من الوثائق الرسمية، وأُعيد فحص كل أمر، ثم رُوجع مرة ثانية قبل النشر. آخر مراجعة في .
المصادر الرسمية التي قُرئت لهذا المقال

