ImagePullBackOff يعني Kubernetes حاول تحميل صورة الحاوية ولكن فشل. ينتظر pod، يعيد المحاولة، ويفشل مرة أخرى في حلقة exponential backoff. يوضح هذا الدليل كيفية تشخيص وإصلاح الأسباب الأكثر شيوعاً.
الأعراض
حالة pod تظهر ImagePullBackOff. تشغيل kubectl describe pod يكشف:
Name: example-app-5f7c9d4bfb-7x8kq
Namespace: default
Status: Pending
Events:
Type Reason Age Message
---- ------ ---- -------
Normal Pulling 2m15s Pulling image "example:latest"
Warning Failed 2m12s Failed to pull image "example:latest":
rpc error: code = Unknown desc = Error response from daemon: manifest not found
Warning Failed 2m8s Failed to pull image "example:latest": rpc error: code = Unknown desc = Error response from daemon: manifest not found
Normal BackOff 115s Back-off pulling image "example:latest"
لماذا يحدث
مرتبة حسب الاحتمالية:
- اسم صورة أو وسم خاطئ — الصورة لا تكون موجودة في التسجيل. خطأ في الاسم، الوسم غير موجود، أو الصورة لم تُدفع.
- تسجيل خاص بدون imagePullSecrets — Kubernetes لا يستطيع المصادقة على التسجيل ويتم رفضه برسالة “unauthorized” أو “authentication required”.
- حد معدل Docker Hub مصاب — السحب غير المصرح من Docker Hub محدود. بعد 100 سحبة لكل 6 ساعات، يفشل السحب الجديد.
- معمارية أو نظام تشغيل خاطئ — الصورة مبنية لمعمارية CPU مختلفة (مثل arm64 مقابل amd64) أو نظام تشغيل، والتشغيل لا يستطيع تنفيذها.
- التسجيل معطل أو بعيد أو محجوب — سياسة الشبكة، جدار حماية، DNS، أو خادم التسجيل نفسه معطل أو غير مهيأ.
- بيانات الاعتماد منتهية الصلاحية أو غير صحيحة — بيانات اعتماد التسجيل في Secret تغيرت أو انتهت صلاحية الرمز.
الحل
1. اسم صورة أو وسم خاطئ
تحقق مما حددته:
kubectl get pod <pod-name> -o yaml | grep -A 2 "image:"
تحقق من وجود الصورة في التسجيل:
# لـ Docker Hub
docker pull example:latest
# لتسجيل خاص
docker pull registry.example.com/example:latest
# إذا فشل السحب محلياً، سيفشل في Kubernetes أيضاً
إذا لم توجد الصورة، ادفعها أولاً:
# ابنِ الصورة
docker build -t example:latest .
# وسمها لتسجيلك
docker tag example:latest registry.example.com/example:latest
# ادفعها
docker push registry.example.com/example:latest
ثم حدّث Deployment لاستخدام الصورة الصحيحة:
apiVersion: apps/v1
kind: Deployment
metadata:
name: example-app
spec:
template:
spec:
containers:
- name: app
image: registry.example.com/example:v1.0.0 # صورة صحيحة
طبّق وأعد المحاولة:
kubectl apply -f deployment.yaml
kubectl rollout restart deployment/example-app
2. تسجيل خاص بدون imagePullSecrets
إذا كان التسجيل خاص، يحتاج Kubernetes لبيانات الاعتماد. أنشئ docker-registry Secret:
kubectl create secret docker-registry myregistrycred \
--docker-server=registry.example.com \
--docker-username=myuser \
--docker-password=mypass \
--docker-email=me@example.com \
--namespace=default
أو من تكوين docker login:
# إذا كنت قد سجلت محلياً بالفعل، انسخ بيانات الاعتماد
kubectl create secret generic myregistrycred \
--from-file=.dockerconfigjson=$HOME/.docker/config.json \
--type=kubernetes.io/dockercfg
تحقق من إنشاء Secret:
kubectl get secret myregistrycred -o yaml
ثم اجعل مرجع له في مواصفات pod:
apiVersion: apps/v1
kind: Deployment
metadata:
name: example-app
spec:
template:
spec:
imagePullSecrets:
- name: myregistrycred
containers:
- name: app
image: registry.example.com/example:latest
بدلاً من ذلك، أضف Secret لحساب الخدمة بحيث تستخدمه جميع pods:
kubectl patch serviceaccount default -p '{"imagePullSecrets": [{"name": "myregistrycred"}]}'
تحقق:
kubectl get serviceaccount default -o yaml
ثم احذف وأنشئ من جديد pod لسحب السر الجديد:
kubectl delete pod <pod-name>
3. حد معدل Docker Hub مصاب
إذا كنت تسحب بدون مصادقة من Docker Hub، فقد وصلت لحد 100 سحبة لكل 6 ساعات. صادق بدلاً من ذلك:
أنشئ Docker Hub Personal Access Token (أو استخدم كلمة المرور) وأنشئ Secret:
kubectl create secret docker-registry dockerhubcred \
--docker-server=docker.io \
--docker-username=myusername \
--docker-password=dckr_pat_XXXXXXX \
--docker-email=me@example.com
حدّث Deployment لجعل مرجع له:
imagePullSecrets:
- name: dockerhubcred
أو أضفه لحساب الخدمة الافتراضي:
kubectl patch serviceaccount default -p '{"imagePullSecrets": [{"name": "dockerhubcred"}]}'
للحدود أعلى، استخدم حساب Docker Hub مدفوع أو تسجيل خاص. احذف وأنشئ pods من جديد لإعادة المحاولة مع بيانات الاعتماد الجديدة:
kubectl delete pod <pod-name>
4. معمارية أو نظام تشغيل خاطئ
الصورة مبنية لمعمارية CPU أو نظام تشغيل مختلف. تحقق من معمارية العقدة:
kubectl get nodes -o wide | grep -i arch
# أو:
kubectl describe node <node-name> | grep "kubernetes.io/arch"
تحقق من معمارية الصورة. ابنها لمعمارية صحيحة باستخدام Docker BuildX:
# ابنِ لمعماريات متعددة
docker buildx build --platform linux/amd64,linux/arm64 \
-t registry.example.com/example:latest \
--push .
# أو ابنِ فقط للهدف
docker buildx build --platform linux/arm64 \
-t registry.example.com/example:arm64 .
يمكنك أيضاً استخدام صورة أساسية متعددة المعماريات:
FROM node:20-alpine # يدعم بالفعل amd64, arm64, إلخ
COPY . /app
WORKDIR /app
CMD ["node", "server.js"]
إذا كانت الصورة مبنية بالفعل لمعمارية خاطئة، أعد بناءها أو استخدم صورة أساسية مختلفة.
5. التسجيل معطل أو بعيد أو محجوب
اختبر اتصال الشبكة للتسجيل:
# من العنقود
kubectl run debug --image=busybox -it --rm -- sh
# داخل الحاوية:
wget https://registry.example.com
nc -zv registry.example.com 443
تحقق من قرار DNS:
kubectl run debug --image=busybox -it --rm -- sh
# داخل:
nslookup registry.example.com
تحقق من سياسات الشبكة التي قد تحجب egress:
kubectl get networkpolicy -A
تحقق من قواعد جدار حماية العنقود. إذا كان التسجيل داخل العنقود (مثل instance Harbor خاص)، تأكد من:
– pod التسجيل يعمل: kubectl get pod -n registry
– خدمة التسجيل يمكن الوصول إليها: kubectl get svc -n registry
انتظر عودة التسجيل، ثم احذف pod لإعادة المحاولة:
kubectl delete pod <pod-name>
6. بيانات الاعتماد منتهية الصلاحية أو غير صحيحة
إذا حدثت بيانات الاعتماد في التسجيل الخاص، حدّث Secret في Kubernetes:
# احذف Secret القديم
kubectl delete secret myregistrycred
# أنشئ واحد جديد مع بيانات اعتماد محدثة
kubectl create secret docker-registry myregistrycred \
--docker-server=registry.example.com \
--docker-username=myuser \
--docker-password=mynewpass \
--docker-email=me@example.com
أجبر pods على إعادة المصادقة بحذفها:
kubectl delete pod <pod-name>
للمصادقة المستندة لرمز (مثل رموز حساب الخدمة Kubernetes المستخدمة بواسطة التسجيلات داخل العنقود)، أنشئ الرمز من جديد وحدّث Secret.
كيف تتجنبه
-
ادفع الصورة قبل النشر:
docker build -t registry.example.com/app:v1.0.0 . docker push registry.example.com/app:v1.0.0 # ثم انشر مع هذا الوسم الدقيق -
استخدم وسوم محددة، وليس
latest:image: registry.example.com/app:v1.2.3 # جيد image: registry.example.com/app:latest # محفوف بالمخاطر -
صادق التسجيلات الخاصة:
kubectl create secret docker-registry myregistrycred ... # واستخدمه في جميع Deployments -
سحب مسبق الصور على العقد باستخدام DaemonSets للصور المستخدمة بكثرة، مما يقلل وقت السحب والفشل.
-
استخدم مرآة التسجيل الخاص أو ذاكرة تخزين مؤقت (مثل Harbor، Artifactory) لتقليل الاعتماد على حدود معدل Docker Hub.
-
اختبر سحب الصورة قبل الالتزام بـ Kubernetes:
docker run --rm registry.example.com/app:v1.0.0 /bin/sh -c "echo 'works'"
الأسئلة الشائعة
كيف أتحقق من سبب فشل سحب الصورة؟
شغّل kubectl describe pod <pod-name> وانظر إلى قسم Events. يظهر خطأ التسجيل بالضبط، مثل ‘not found’ أو ‘authentication required’.
ما الفرق بين ImagePullBackOff و CrashLoopBackOff؟
ImagePullBackOff يعني فشل تنزيل الصورة قبل بدء الحاوية حتى. CrashLoopBackOff يعني الحاوية بدأت ولكن انهارت. تحقق من kubectl describe pod لترى أيهما لديك.
كيف أستخدم تسجيل Docker خاص في Kubernetes؟
أنشئ docker-registry Secret، ثم اجعل مرجع له في imagePullSecrets في مواصفات pod أو حساب الخدمة. اعرض أمر kubectl create secret مع بيانات اعتماد التسجيل.
لماذا يفشل السحب من Docker Hub أحياناً؟
Docker Hub له حدود معدل: 100 سحبة لكل 6 ساعات للمستخدمين غير المصرح، 200 لكل 6 ساعات للحسابات المجانية. بعد الوصول للحد، يفشل السحب برسالة ‘Too Many Requests’. صادق بحساب Docker Hub أو استخدم تسجيل خاص.
كيف تم التحقق من هذا الدليل
كُتب يدويًا من الوثائق الرسمية، وأُعيد فحص كل أمر، ثم رُوجع مرة ثانية قبل النشر. آخر مراجعة في .
المصادر الرسمية التي قُرئت لهذا المقال

