وقتی وضعیت container بین Restarting و Up چند ثانیه جابهجا میشود، PID اصلی خارج میشود و Restart Policy دوباره آن را بالا میآورد. علت میتواند config یا secret نامعتبر، command اشتباه، permission، port conflict، OOM، dependency در دسترسنبودن یا پایان طبیعی یک job با policy نامناسب باشد. restart بیشتر بدون جمعآوری شواهد معمولاً علت را حل نمیکند.
پاسخ سریع: deploy و پاکسازی را متوقف کنید؛ وضعیت، restart count، exit code، OOMKilled و timestamp را با inspect ثبت کنید؛ log همان container و رویدادهای host را در بازه crash ببینید. سپس command/entrypoint، config، mount، user، منابع و dependency را بررسی کنید. اصلاح را در Compose انجام دهید و با policy محدود در staging بازتولید کنید.
اول دامنه حادثه را مشخص کنید
آیا فقط یک replica، یک سرویس یا تمام stack restart میشوند؟ آیا بعد از deploy، reboot، rotation secret یا پرشدن Disk شروع شده؟ اگر سرویس تراکنشی است، اثر روی job، پیام و write را بررسی کنید. rolloutهای خودکار را pause کنید تا نسخههای بیشتری خراب نشوند. از حذف container یا volume قبل از ثبت شواهد بپرهیزید.
چه دادهای را بدون تغییر جمع کنیم؟
- نام، image digest و command واقعی container
- State، exit code، error، OOMKilled و زمان شروع/پایان
- restart count و Restart Policy
- آخرین logها با بازه زمانی محدود
- mount، user، environment بدون چاپ secret و network
- eventهای Docker و log kernel/daemon در همان زمان
خروجی inspect ممکن است environment حساس داشته باشد؛ قبل از ارسال به تیکت یا فرد دیگر آن را پالایش کنید. از log کامل و حجیم کپی نکنید؛ نمونه نزدیک crash و الگوی تکرار کافیتر است.
Exit Code چه میگوید؟
exit code نقطه شروع است، نه تشخیص نهایی. کد صفر میتواند به معنی پایان طبیعی job باشد و با policy always loop بسازد. غیرصفر معمولاً شکست برنامه یا wrapper است. پایان با signal، OOM یا stop زماندار معانی دیگری دارد. مستندات خود برنامه و log قبل از exit را کنار State ببینید؛ جدول عمومی exit code بدون context ممکن است گمراه کند.
OOMKilled و کمبود حافظه
اگر State نشان میدهد OOM رخ داده، مصرف container، محدودیت cgroup، memory host و kernel log را بررسی کنید. افزایش limit بدون شناخت leak یا concurrency فقط زمان crash را عقب میاندازد و ممکن است host را قربانی کند. heap، worker، cache و request بزرگ را اندازه بگیرید. swap راهحل عمومی performance نیست؛ ظرفیت و رفتار load واقعی را تحلیل کنید.
Command یا Entrypoint اشتباه
فایل executable ناموجود، architecture غلط، shebang نامعتبر، line ending ویندوزی یا permission اجرا باعث exit فوری میشود. command مؤثر ممکن است توسط Compose override شده باشد و با Dockerfile فرق کند. image را با entrypoint اصلی و config مشابه در محیط امن اجرا کنید. برای debug، policy محدود یا no بگذارید تا log قابلمشاهده بماند؛ تغییر production را در منبع Compose ثبت کنید.
Config و Secret
متغیر اجباری غایب، فایل secret غیرقابلخواندن، format اشتباه یا credential منقضی از علل رایجاند. مقدار secret را چاپ نکنید؛ وجود فایل، owner/mode، طول یا نسخه reference را بدون افشا بررسی کنید. اگر secret rotate شده، آیا برنامه فایل تازه را میبیند و contract _FILE را پشتیبانی میکند؟ config validation پیش از startup خطای روشنتری میدهد.
Permission و Filesystem
تغییر image به user غیرroot میتواند نوشتن روی volume، cache یا socket را بشکند. mount read-only یا root filesystem محدود نیز مسیر temp را نیازمند تعریف میکند. owner UID/GID و mode مسیر داخل container و host را تطبیق دهید. chmod 777 عمومی نکنید؛ کمترین دسترسی لازم را تنظیم و migration ownership را مستند کنید.
Dependency هنوز آماده نیست
برنامه ممکن است با اولین خطای DNS یا دیتابیس exit کند و policy آن را بارها بازگرداند. depends_on با health میتواند startup اولیه را بهتر کند، ولی app باید قطع موقت runtime را با timeout و backoff تحمل کند. نام سرویس، network، port داخلی و TLS را بررسی کنید. loop سریع میتواند دیتابیس را با connection storm تحت فشار بگذارد.
Migration شکستخورده
اجرای migration در entrypoint هر replica میتواند lock، race یا failure تکراری بسازد. migration بهتر است step کنترلشده deployment با log و rollback مشخص باشد. اگر schema نیمهتغییر کرده، بازگرداندن صرف image قبلی ممکن است امن نباشد. backup و سازگاری نسخه را قبل از اقدام destructive بررسی کنید.
Port و Listener
داخل network معمولاً چند container میتوانند پورت داخلی یکسان داشته باشند؛ conflict بیشتر هنگام publish پورت host یا اجرای process دوم داخل همان namespace رخ میدهد. log bind error، config listen address و mapping Compose را بررسی کنید. برنامهای که فقط localhost را گوش میدهد ممکن است از proxy قابلدسترسی نباشد، اما این معمولاً بهتنهایی PID را خارج نمیکند مگر startup check آن را failure بداند.
Disk، Inode و Filesystem Read-only
نبود فضا میتواند نوشتن PID، database، temp یا log را خراب و برنامه را خارج کند. بایت و inode و kernel error را ببینید. پاکسازی کورکورانه volume خطرناک است؛ راهنمای تشخیص Disk Docker مسیر کمریسک را ارائه میدهد. بعد از آزادکردن headroom، علت رشد را نیز اصلاح کنید.
Healthcheck مقصر است؟
در Docker معمول، unhealthy شدن خودبهخود restart policy را فعال نمیکند. اگر container پس از health failure restart میشود، orchestrator، watchdog یا automation دیگری ممکن است دخالت داشته باشد. event و controller را شناسایی کنید. probe سنگین میتواند منابع را مصرف کند، اما رابطه زمانی را با metric و log ثابت کنید. راهنمای Healthcheck رفتار دقیق را توضیح میدهد.
آیا خود Docker Daemon یا Host Restart شده است؟
همه شروعهای مجدد ناشی از crash برنامه نیستند. زمان uptime host، log daemon، reboot history و زمان recreate container را با هم مقایسه کنید. deployment ممکن است container تازه با شناسه جدید ساخته باشد، در حالی که restart count نمونه فعلی پایین است. maintenance Engine، kernel panic یا restart سرویس Docker میتواند چند container را همزمان متأثر کند. اگر همه سرویسها در یک timestamp برگشتهاند، بهجای عیبیابی جداگانه هر app، ابتدا رخداد host را بررسی کنید.
تفاوت Restart، Recreate و Reschedule
Restart همان container را با config موجود برمیگرداند؛ recreate نمونه تازه از image/config میسازد؛ orchestrator ممکن است task را روی node دیگری reschedule کند. این تفاوت برای filesystem موقت، IP، log و شمارندهها مهم است. شناسه container، image digest و node را ثبت کنید. داده مهم نباید در writable layer وابسته به همان نمونه باشد.
Restart Policy نامناسب
یک command یکباره که با exit صفر تمام میشود، زیر always دائماً تکرار میشود. on-failure بدون سقف برای خطای دائمی نیز loop میسازد. نوع workload را مشخص و policy را مطابق آن انتخاب کنید. مقایسه Restart Policyها تفاوت رفتار پس از exit، توقف دستی و daemon restart را پوشش میدهد.
روند رفع مرحلهای
- نسخه خراب و زمان شروع incident را ثبت و rollout را متوقف کنید.
- inspect، event و log محدود را قبل از recreate ذخیره کنید.
- علت محتمل را بر اساس exit/OOM/config دستهبندی کنید.
- در clone یا staging با policy کنترلشده بازتولید کنید.
- فقط یک متغیر را اصلاح و startup/shutdown را تست کنید.
- با health gate deploy و restart count را مانیتور کنید.
- پس از پایداری، root cause و اقدام پیشگیرانه را ثبت کنید.
چه کارهایی نکنیم؟
- restart دستی پیدرپی بدون ثبت State
- حذف volume یا recreate کل stack برای یک خطای config
- نمایش environment و secret در کانال عمومی
- افزایش بیحد memory یا timeout
- غیرفعالکردن health و monitoring برای سبزشدن وضعیت
- rollback image بدون بررسی migration داده
- تغییر داخل container و اتکا به آن برای release بعدی
پیشگیری
config validation و migration gate در CI/CD، image digest ثابت، startup و shutdown test، secret contract، limit مبتنی بر ظرفیت، log rotation و alert restart count داشته باشید. برنامه باید failure دائمی را از خطای موقت جدا کند. runbook شامل فرمانهای خواندنی، owner سرویس و شرایط escalation باشد.
چه زمانی فوراً کمک بگیریم؟
اگر container به دیتابیس تراکنشی متصل است، migration نیمهکاره یا OOM در کل host دیده میشود، ادامه آزمونوخطا ریسک داده دارد. پشتیبانی ساعتی DevOps میتواند شواهد exit، منابع و dependency را بررسی و recovery کنترلشده اجرا کند.
پرسشهای متداول
چطور Restart Loop را موقتاً متوقف کنیم؟
ابتدا شواهد را ثبت کنید؛ سپس policy/سرویس هدف را طبق runbook متوقف کنید. روی نام یا container مبهم اقدام نکنید.
آیا Exit Code 0 یعنی مشکلی نیست؟
برای job پایانیافته بله، اما برای daemon یعنی command زود تمام شده یا policy workload اشتباه است.
آیا افزایش RAM مشکل OOM را حل میکند؟
ممکن است موقتاً، اما leak، worker زیاد یا request غیرعادی باید اندازهگیری و اصلاح شود.
چرا log قبلی را نمیبینم؟
rotation، recreate یا driver مرکزی میتواند تاریخچه را جابهجا کند؛ logging pipeline و retention را بررسی کنید.