Kubernetes و کانتینرمهندسانسطح متوسط

پاد Kubernetes در وضعیت Terminating گیر کرده؛ آزادسازی امن

پاد در حالت Terminating گیر کرده و با kubectl delete پاک نمی‌شود؟ این مقاله علت واقعی (finalizer باقی‌مانده، نود NotReady، ولوم گیرکرده) و راه‌حل گام‌به‌گام امن را نشان می‌دهد.

تحریریه کلودیکپ ۶ دقیقه مطالعه
پاد Kubernetes در وضعیت Terminating گیر کرده؛ آزادسازی امن

در محیط عملیاتی Kubernetes یکی از آزاردهنده‌ترین صحنه‌ها این است که kubectl delete pod با موفقیت برمی‌گردد، اما پاد نه‌فقط پاک نمی‌شود بلکه ساعت‌ها در وضعیت Terminating می‌ماند و مقدار AGE آن مدام بالا می‌رود. در این مقاله دقیقاً همین حالت را کالبدشکافی می‌کنیم: چرا رخ می‌دهد، چطور بدون خراب‌کردن چیزی آزادش کنیم، و چطور جلوی تکرارش را بگیریم.

شرح مسئله: پادی که نمی‌خواهد برود

ماجرا معمولاً بعد از یک deploy یا یک عملیات مقیاس‌دهی شروع می‌شود. دستور حذف اجرا می‌شود، API Server هم تایید می‌کند، ولی پاد همچنان سر جایش است:

$ kubectl delete pod web-7d9f8c6b5-4xk2p -n production
pod "web-7d9f8c6b5-4xk2p" deleted

$ kubectl get pods -n production
NAME                   READY   STATUS        RESTARTS   AGE
web-7d9f8c6b5-4xk2p    1/1     Terminating   0          3d2h
web-7d9f8c6b5-9qztv    1/1     Running       0          4d

نشانه کلیدی این است که deletionTimestamp ثبت شده اما شیء پاد هنوز در etcd باقی مانده و کانتینر در نود هم ممکن است هنوز در حال اجرا باشد. نکته مهم‌تر اینکه kubectl describe pod معمولاً چیز جدیدی به شما نمی‌گوید؛ چون در این مرحله دیگر رویداد تازه‌ای تولید نمی‌شود و Events خشک شده است.

$ kubectl get pod web-7d9f8c6b5-4xk2p -n production \
  -o jsonpath='{.metadata.deletionTimestamp}{"\n"}{.metadata.finalizers}{"\n"}{.spec.nodeName}{"\n"}'
2024-05-11T07:42:10Z
[example.com/backup-protection]
ip-10-0-14-23

همین سه خط معمولاً کل داستان را لو می‌دهد: زمان حذف ثبت شده، یک finalizer باقی مانده، و نودی که پاد روی آن اجرا می‌شود.

علت ریشه‌ای: چرخه حذف پاد کجا می‌شکند

پاد زمانی از لیست خارج می‌شود که دو شرط هم‌زمان برقرار باشند:

  1. API Server مقدار metadata.deletionTimestamp را ثبت کند و بعد از آن، آرایه metadata.finalizers خالی شود.
  2. kubelet روی نود میزبان تایید کند که کانتینرهای پاد متوقف و منابعش آزاد شده‌اند.

اگر هر کدام از این دو نیمه‌کاره بماند، پاد در وضعیت Terminating قفل می‌شود. رایج‌ترین دلایل در عمل این‌ها هستند:

  • نود NotReady یا kubelet بی‌پاسخ: kubelet نمی‌تواند SIGTERM را پردازش کند و تایید نهایی را به API Server برنگرداند. این حالت بعد از کرش‌کردن نود یا پرشدن دیسک نود خیلی شایع است.
  • finalizer باقی‌مانده از یک اپراتور: بعضی اپراتورها یا webhookها یک finalizer به پاد اضافه می‌کنند تا قبل از حذف، کار پاک‌سازی انجام دهند. اگر آن کنترلر خودش از کار افتاده باشد، finalizer هرگز برداشته نمی‌شود.
  • گیرکردن attach/detach ولوم: اگر پاد از یک PVC استفاده کند و CSI driver یا VolumeAttachment در حالت detach بماند، پاد منتظر آزادسازی ولوم می‌ماند.
  • preStop hook بلاک‌شده: یک hook طولانی یا منتظر روی endpoint خارجی، کل مسیر خاتمه را متوقف می‌کند.
  • هنگ‌کردن containerd: گاهی خود runtime در تمیزکردن کانتینر گیر می‌کند.

راه‌حل گام‌به‌گام

قبل از هر چیز: بدون بررسی علت، --force نزنید. حذف اجباری می‌تواند پاد را از etcd پاک کند در حالی که کانتینر روی نود هنوز زنده است و همان پورت‌ها را گرفته — نتیجه‌اش یک نود zombie و خطای «address already in use» در پاد بعدی است.

گام ۱ — جمع‌آوری شواهد

اول مشخص کنید پاد روی کدام نود است و آن نود چه وضعیتی دارد:

kubectl get pod web-7d9f8c6b5-4xk2p -n production -o wide
kubectl get nodes
kubectl describe node ip-10-0-14-23 | grep -i -E "Conditions|Ready|NotReady"

سپس رویدادهای مربوط به همان پاد را ببینید:

kubectl get events -n production \
  --field-selector involvedObject.name=web-7d9f8c6b5-4xk2p \
  --sort-by=.lastTimestamp

گام ۲ — اگر نود NotReady است، اول نود را درست کنید

وقتی نود در وضعیت NotReady است، راه‌حل درست این نیست که پاد را force delete کنید؛ باید kubelet و runtime را روی نود سر حال برگردانید. روی خود نود:

# روی نود مشکل‌دار
ssh ip-10-0-14-23

systemctl status kubelet --no-pager
journalctl -u kubelet -n 100 --no-pager

# اگر containerd هنگ کرده باشد
systemctl restart containerd
systemctl restart kubelet

دقت کنید که systemctl restart containerd روی همه کانتینرهای آن نود اثر می‌گذارد؛ اگر نود پرترافیک است، اول آن را kubectl cordon کنید تا بار جدید نگیرد.

گام ۳ — اگر finalizer گیر کرده است

اگر نود سالم است و فقط یک finalizer باقی مانده، می‌توانید آن را از متادیتای پاد حذف کنید:

kubectl patch pod web-7d9f8c6b5-4xk2p -n production \
  --type=merge -p '{"metadata":{"finalizers":null}}'

این کار باید با آگاهی انجام شود: اگر finalizer متعلق به یک کنترلر پاک‌سازی واقعی است، حذفش یعنی منابع پشت آن (بکاپ، رکورد DNS، رکورد ابری) ممکن است برای همیشه یتیم بمانند. اول بفهمید کدام اپراتور آن را گذاشته است.

گام ۴ — اگر ولوم گیر کرده است

پادهای دارای PVC وقتی نود از دسترس خارج شود، اغلب به خاطر ولوم در Terminating می‌مانند. وضعیت اتصال‌ها را ببینید:

kubectl get volumeattachments
kubectl describe volumeattachment 

اگر VolumeAttachment در حالت detach گیر کرده و مطمئنید پاد دیگر روی هیچ نودی اجرا نمی‌شود، finalizer آن را بردارید:

kubectl patch volumeattachment 

گام ۵ — حذف اجباری به‌عنوان آخرین راه

اگر نود برای همیشه رفته (مثلاً ماشین ابری terminate شده) و می‌خواهید فقط شیء پاد را از etcd پاک کنید:

kubectl delete pod web-7d9f8c6b5-4xk2p -n production \
  --grace-period=0 --force

این دستور فقط شیء API را می‌کوبد و هیچ کاری روی کانتینر واقعی انجام نمی‌دهد. اگر نود زنده است، قبلاً آن را cordon و drain کنید.

بررسی اینکه مشکل واقعاً حل شده است

بعد از هر گام، بدون حدس‌زدن تاییدیه بگیرید:

# ۱. پاد باید کاملاً از لیست رفته باشد
kubectl get pods -n production

# ۲. نباید پاد مرده‌ای باقی مانده باشد
kubectl get pods -A --field-selector status.phase!=Running | grep -i terminating

# ۳. VolumeAttachmentهای یتیم نباید باقی بمانند
kubectl get volumeattachments

# ۴. روی نود نباید کانتینر یتیم باقی مانده باشد
kubectl describe node ip-10-0-14-23 | grep -i -A5 "Non-terminated Pods"

در نهایت مطمئن شوید که Deployment جایگزین سالم بالا آمده و endpoints سرویس پر است:

kubectl rollout status deployment/web -n production
kubectl get endpoints web -n production

اگر Endpoints سرویس پادهای سالم را نشان می‌دهد و هیچ پاد Terminating در کلاستر نمی‌بینید، کار تمام است.

پیشگیری و بهترین روش‌ها

  • مقدار مناسب برای terminationGracePeriodSeconds: نه خیلی کوتاه (که باعث SIGKILL زودهنگام شود) و نه آن‌قدر بلند که drain نود را قفل کند. برای اکثر سرویس‌ها ۳۰ تا ۶۰ ثانیه کافی است.
  • preStop hook کوتاه و بدون انتظار روی سرویس خارجی: یک sleep 5 برای تخلیه connection کافی است؛ انتظار روی health check یک API بیرونی، فقط چرخه حذف را شکننده می‌کند.
  • مراقب finalizerهای سفارشی باشید: هر اپراتوری که به پاد finalizer اضافه می‌کند باید خودش پایش شود. اپراتور از کار افتاده یعنی پادهای گیرکرده در آینده.
  • برای تعمیرات از drain استفاده کنید، نه حذف نود: kubectl drain <node> --ignore-daemonsets --delete-emptydir-data مسیر خاتمه را مرتب طی می‌کند و ولوم‌ها را تمیز detach می‌کند.
  • هشدار مانیتورینگ برای پادهای گیرکرده: با kube-state-metrics می‌توانید پادهایی را که مدت طولانی در حال حذف هستند هشدار دهید:
- alert: PodStuckTerminating
  expr: (time() - kube_pod_deletion_timestamp) > 600
  for: 5m
  labels:
    severity: warning
  annotations:
    summary: "پاد {{ $labels.namespace }}/{{ $labels.pod }} بیش از ۱۰ دقیقه در حال حذف است"

و در آخر، نسخه kubelet، containerd و CSI driver را به‌روز نگه دارید؛ بخش قابل توجهی از پادهای گیرکرده در Terminating ریشه در باگ‌های همین لایه‌ها دارد که در نسخه‌های بعدی رفع شده‌اند.

اشتراک‌گذاری:

نویسنده

تحریریه کلودیکپ

تیم محتوای کلودیکپ

تیم فنی و محتوای کلودیکپ؛ مهندسانی که هر روز با DevOps، Kubernetes و زیرساخت ابری کار می‌کنند و تجربه‌هایشان را اینجا می‌نویسند.

سوالات متداول

سوالات متداول این مقاله

بله. حذف اجباری فقط شیء پاد را از etcd پاک می‌کند و هیچ کاری با کانتینر واقعی روی نود ندارد. اگر نود زنده باشد، کانتینر همچنان پورت‌ها و volume mountها را نگه می‌دارد و ممکن است پاد بعدی با خطای پورت در حال استفاده یا ناسازگاری ولوم روبه‌رو شود. فقط وقتی مطمئن شدید نود مرده است یا قبلاً آن را drain کرده‌اید از --force استفاده کنید.

CrashLoopBackOff یعنی کانتینر بالا می‌آید ولی مکرراً از کار می‌افتد و kubelet بین تلاش‌ها عقب‌نشینی می‌کند. Terminating یعنی پاد در حال حذف است اما فرایند حذف تمام نشده. این دو مشکل ریشه‌ای کاملاً متفاوت دارند و نباید با هم اشتباه گرفته شوند.

چون پاد توسط یک کنترلر مثل Deployment یا ReplicaSet مدیریت می‌شود. آن کنترلر تعداد replica مورد انتظار را دوباره برقرار می‌کند و یک پاد جدید با نام متفاوت می‌سازد. اگر می‌خواهید پاد برای همیشه برود، باید خود Deployment را scale کنید یا حذف کنید، نه فقط پاد را.

ادامه مطالعه

مقالات مرتبط

قطع شدن WebSocket در HAProxy هر ۵۰ ثانیه؛ علت و راه‌حل
Linux و مدیریت سرورمتوسط

قطع شدن WebSocket در HAProxy هر ۵۰ ثانیه؛ علت و راه‌حل

اگر WebSocket یا SSE پشت HAProxy هر ۵۰ ثانیه قطع می‌شود، مقصر تایماوت‌های پیش‌فرض فایل نمونه است. در این مقاله علت ریشه‌ای و پیکربندی درست timeout tunnel را بررسی می‌کنیم.

تحریریه کلودیکپ ۷ دقیقه مطالعه
رفع dkim=none در Postfix با راه‌اندازی OpenDKIM
ابزارهای Self-Hostedمتوسط

رفع dkim=none در Postfix با راه‌اندازی OpenDKIM

اگر ایمیل‌های سرور Postfix شما در Gmail به اسپم می‌روند و هدر پیام dkim=none نشان می‌دهد، یعنی هیچ امضای DKIM روی پیام‌ها درج نمی‌شود. در این مقاله OpenDKIM را نصب، پیکربندی و به Postfix وصل می‌کنیم و رکورد DNS را منتشر می‌کنیم.

تحریریه کلودیکپ ۶ دقیقه مطالعه
چرا همگام‌سازی JQL در Jira فقط ۵۰ ایشو برمی‌گرداند؟
CI/CD و اتوماسیونمتوسط

چرا همگام‌سازی JQL در Jira فقط ۵۰ ایشو برمی‌گرداند؟

اگر job همگام‌سازی Jira شما بدون خطا تمام می‌شود ولی فقط ۵۰ ایشو را می‌آورد یا در حلقه بی‌پایان گیر می‌کند، مشکل از JQL نیست؛ اندپوینت search به pagination مبتنی بر nextPageToken منتقل شده و startAt و total دیگر وجود ندارند.

تحریریه کلودیکپ ۸ دقیقه مطالعه

در پیاده‌سازی به کمک نیاز دارید؟

تیم کلودیکپ همین کار را هر روز برای تیم‌های دیگر انجام می‌دهد. اگر جایی گیر کرده‌اید، با ما صحبت کنید.