KUBERNETES

عیب‌یابی Kubernetes با kubectl؛ بررسی CrashLoopBackOff، Pending و ImagePullBackOff

روش منظم عیب‌یابی Pod، Service و Ingress با kubectl (دستورهای describe، logs، events و debug)، همراه با علت و راه‌حل CrashLoopBackOff، Pending، ImagePullBackOff و OOMKilled.

وقتی چیزی در Kubernetes کار نمی‌کند، پیام‌های وضعیت (CrashLoopBackOff، Pending، ImagePullBackOff) فقط علامت‌اند. عیب‌یابی سریع یعنی یک مسیر ثابت داشتن: وضعیت را بخوانید، رویدادها را ببینید، لاگ را بخوانید و در صورت نیاز از داخل Pod بررسی کنید. این راهنما همین مسیر را همراه با علت‌ها و راه‌حل‌های رایج هر وضعیت فهرست می‌کند.

مسیر ثابت عیب‌یابی

kubectl get pods -o wide                       # 1) وضعیت، نود، IP
kubectl describe pod <pod>                     # 2) Events انتهای خروجی را بخوانید
kubectl logs <pod> [-c <container>] [--previous]  # 3) لاگ کانتینر
kubectl get events --sort-by=.lastTimestamp    # 4) رویدادهای namespace
kubectl exec -it <pod> -- sh                   # 5) بررسی از داخل

اگر مانیفست‌ها را دستی می‌نویسید، پیش از apply ساختار YAML را بررسی کنید؛ بخش بزرگی از مشکل‌ها (تورفتگی، نام فیلد اشتباه) همان‌جا پیدا می‌شود.

YAMLYAML Validatorنحو YAML مانیفست را بررسی کنید و محل دقیق خطا را ببینید.

راهنمای سریع وضعیت‌ها

وضعیتیعنیاول چه ببینید
Pendingزمان‌بندی نشده یا هنوز منتظر منبع استEvents: منابع نود، PVC، taint
ContainerCreatingدر حال ساخت (دانلود ایمیج، mount volume)Events: خطای mount یا pull
ErrImagePull / ImagePullBackOffدانلود ایمیج شکست خوردهنام و تگ، imagePullSecrets، دسترسی شبکه
CrashLoopBackOffکانتینر بالا می‌آید و می‌میردlogs --previous، exit code
CreateContainerConfigErrorConfigMap یا Secret موجود نیستEvents: نام منبع و کلید
Running ولی 0/1 ReadyreadinessProbe شکست می‌خوردمسیر و پورت probe
OOMKilledحافظه از limit بیشتر شدlimits.memory، نشتی حافظه
Evictedنود منابع (حافظه/دیسک) کم آوردkubectl describe node
Terminating (گیرکرده)finalizer یا کانتینر پاسخ نمی‌دهدfinalizers، terminationGracePeriodSeconds

Pending: چرا Pod جایی برای اجرا پیدا نمی‌کند؟

خروجی kubectl describe pod معمولاً دلیل را در Events می‌نویسد، مثلاً:

Warning  FailedScheduling  0/3 nodes are available: 3 Insufficient cpu.
Warning  FailedScheduling  0/3 nodes are available: 3 node(s) had untolerated taint {dedicated: gpu}.
Warning  FailedScheduling  0/3 nodes are available: pod has unbound immediate PersistentVolumeClaims.
  • Insufficient cpu/memory: مجموع requests از ظرفیت نودها بیشتر است. requests را واقع‌بینانه کنید یا نود اضافه کنید. (kubectl describe node بخش «Allocated resources».)
  • untolerated taint: نودها taint دارند و Pod toleration مناسب ندارد.
  • nodeSelector / affinity: برچسب خواسته‌شده روی هیچ نودی نیست.
  • unbound PVC: kubectl get pvc؛ StorageClass وجود ندارد یا provisioner کار نمی‌کند.

ImagePullBackOff: دانلود ایمیج شکست خورده

kubectl describe pod <pod> | grep -A5 -i "failed to pull"

پیام Events علت را می‌گوید. رایج‌ترین علت‌ها:

  • نام یا تگ اشتباه: «manifest unknown» یا «not found». نام را روی یک ماشین دیگر با docker pull امتحان کنید.
  • رجیستری خصوصی بدون احراز هویت: «unauthorized». Secret از نوع docker-registry بسازید و در imagePullSecrets Pod یا ServiceAccount بگذارید.
  • محدودیت نرخ یا دسترسی رجیستری: «toomanyrequests» یا «403» یا timeout. روی سرورهای ایران، دسترسی نودها به رجیستری‌های خارجی ممکن است قطع باشد؛ از آینه‌ی رجیستری یا رجیستری خصوصی داخلی استفاده کنید.
  • تگ latest و imagePullPolicy: با تگ ثابت و سیاست IfNotPresent فشار روی رجیستری و احتمال خطا کم می‌شود.
# امتحان دانلود مستقیم روی خود نود (containerd)
sudo crictl pull registry.example.com/app:1.8.2

برای شبکه‌ی نودها و رفع خطای 403 هنگام نصب بسته‌های سیستمی، راهنمای نصب از مخزن داخلی را ببینید.

CrashLoopBackOff: کانتینر بالا می‌آید و می‌میرد

kubectl logs <pod> --previous
kubectl describe pod <pod> | grep -A6 "Last State"

مقدار Exit Code در Last State سرنخ خوبی است:

Exit codeمعنی معمول
1خطای برنامه؛ لاگ را بخوانید
126 / 127فرمان قابل‌اجرا نیست / پیدا نشد (Command یا Entrypoint اشتباه)
137SIGKILL؛ اغلب OOMKilled (یا شکست liveness)
139Segmentation fault
143SIGTERM؛ توقف عادی یا شکست liveness probe

علت‌های رایج: متغیر محیطی یا فایل پیکربندی گم‌شده، اتصال به دیتابیس که هنوز آماده نیست، livenessProbe خیلی سخت‌گیرانه که برنامه را قبل از بالاآمدن می‌کشد (initialDelaySeconds یا startupProbe اضافه کنید)، و تنظیم command که ENTRYPOINT ایمیج را اشتباه جایگزین کرده است.

Running ولی Ready نیست

Pod اجرا شده ولی ستون READY مثل 0/1 است؛ یعنی readinessProbe موفق نمی‌شود و Pod از Service کنار گذاشته می‌شود. بررسی کنید مسیر و پورت probe درست باشد، برنامه روی 0.0.0.0 گوش بدهد (نه 127.0.0.1) و initialDelaySeconds کافی باشد. از داخل Pod آزمایش کنید:

kubectl exec -it <pod> -- wget -qO- http://localhost:8080/healthz

Service به Pod نمی‌رسد

اگر درخواست‌ها به Service جواب نمی‌گیرند، اول endpoints را ببینید؛ فهرست خالی یعنی Service هیچ Pod آماده‌ای پیدا نکرده:

kubectl get endpoints web
kubectl get pods -l app=web --show-labels
kubectl describe svc web
  1. selector با برچسب Pod نمی‌خواند (رایج‌ترین علت): مقدار spec.selector Service را با labels Pod مقایسه کنید.
  2. Pod آماده نیست: Podهای Not Ready در endpoints نمی‌آیند.
  3. targetPort اشتباه: باید پورتی باشد که برنامه واقعاً در آن گوش می‌دهد.

با یک Pod موقت از داخل کلاستر آزمایش کنید و سپس از بیرون:

kubectl run tmp --rm -it --image=busybox:1.36 --restart=Never -- wget -qO- http://web.default.svc
kubectl port-forward svc/web 8080:80       # دسترسی از ماشین خودتان

اگر نام سرویس resolve نمی‌شود، مشکل از CoreDNS است: kubectl -n kube-system get pods -l k8s-app=kube-dns و لاگ آن را بخوانید. برای مرور اصول DNS، راهنمای عیب‌یابی DNS و SSL را ببینید.

Ingress: خطای 404، 502 و 503

  • 404: هیچ rule با Host/path درخواست نمی‌خواند، یا ingressClassName با کنترلر نصب‌شده یکی نیست. با kubectl get ingress و describe بررسی کنید.
  • 503: Service endpoint ندارد (بخش قبل).
  • 502/504: کنترلر به Pod وصل شده ولی پاسخ نگرفته؛ پورت یا پروتکل backend (HTTP در برابر HTTPS) را بررسی کنید و لاگ کنترلر را بخوانید: kubectl -n ingress-nginx logs deploy/ingress-nginx-controller.

برای آزمایش بدون DNS، هدر Host را دستی بفرستید:

curl -sI -H "Host: example.com" http://<ingress-ip>/
HTTPHTTP Headers Viewerکد وضعیت، ریدایرکت و هدرهای پاسخ یک آدرس را از بیرون ببینید.

دیباگ داخل Pod و نود

ایمیج‌های کم‌حجم (distroless) shell ندارند و kubectl exec کار نمی‌کند. با کانتینر موقت می‌توانید ابزار خودتان را کنار کانتینر اصلی اجرا کنید:

kubectl debug -it pod/<pod> --image=busybox:1.36 --target=<container>
kubectl debug node/<node> -it --image=busybox:1.36      # دسترسی به نود (فایل‌سیستم میزبان در /host)

وقتی استقرار جدید مشکل ساخت

kubectl rollout status deployment/web
kubectl rollout history deployment/web
kubectl rollout undo deployment/web          # بازگشت به نسخه‌ی قبلی

اگر rollout گیر کرده، معمولاً Pod جدید Ready نمی‌شود (readinessProbe یا ایمیج اشتباه)؛ Deployment به‌خاطر maxUnavailable: 0 نسخه‌ی قبلی را نگه می‌دارد و سرویس قطع نمی‌شود. مشکل را رفع یا با rollout undo برگردید.

جمع‌بندی و مطالعه‌ی بعدی

بیشتر مشکل‌ها با describe و Events پیدا می‌شوند، نه با حدس زدن. مسیر ثابت (وضعیت، Events، لاگ، endpoints) را تمرین کنید و فهرست وضعیت‌ها را کنار دست نگه دارید. برای پیشگیری از Pendingهای ناشی از منابع و OOMKilled، تنظیم requests، limits و HPA را بخوانید و اگر هنوز مانیفست‌ها را می‌نویسید، راهنمای Deployment، Service و Ingress نقطه‌ی شروع است.

پرسش‌های متداول

اولین دستوری که باید برای یک Pod مشکل‌دار بزنم چیست؟
kubectl describe pod <name> و بخش Events انتهای خروجی. بیشتر خطاها (نبود منابع، خطای دانلود ایمیج، شکست probe) همان‌جا با پیام روشن نوشته می‌شوند. بعد سراغ kubectl logs بروید.
فرق ErrImagePull و ImagePullBackOff چیست؟
ErrImagePull یعنی همین الان دانلود شکست خورده؛ ImagePullBackOff یعنی kubelet بعد از چند شکست، فاصله‌ی تلاش‌ها را زیاد کرده است. علت هر دو یکی است و باید از پیام Events پیدا کنید.
لاگ Pod‌ی که مدام ری‌استارت می‌شود را چطور ببینم؟
با kubectl logs <pod> --previous لاگ اجرای قبلی (کانتینر کرش‌کرده) را می‌خوانید. لاگ اجرای فعلی اغلب خالی یا ناقص است، چون تازه شروع شده.
وقتی ایمیج shell ندارد چطور داخل Pod دیباگ کنم؟
با kubectl debug -it pod/<name> --image=busybox --target=<container> یک کانتینر موقت (ephemeral) کنار کانتینر اصلی اضافه می‌شود که فضای نام فرایندها را به اشتراک می‌گذارد و می‌توانید با ابزار خودتان بررسی کنید.