اگر سرویس بدون پیام معمول برنامه بسته میشود و در log عبارتهایی مانند Out of memory یا Killed process دیده میشود، احتمالاً kernel یا محدودیت cgroup برای بازیابی حافظه یک process را متوقف کرده است. process کشتهشده الزاماً عامل اصلی مصرف نیست؛ ممکن است فقط بر اساس امتیاز انتخاب OOM قربانی شده باشد.
پاسخ سریع: زمان توقف را با log kernel و unit/container تطبیق دهید، مشخص کنید OOM در سطح کل host رخ داده یا داخل cgroup، سپس مصرف حافظه پیش از رخداد، swap، limit و تعداد processها را بررسی کنید. restart فقط سرویس را موقتاً بازمیگرداند؛ بدون رفع فشار، چرخه تکرار میشود.
OOM دقیقاً چه زمانی رخ میدهد؟
وقتی تخصیص حافظه انجام نمیشود و reclaim کافی نیست، kernel برای آزاد کردن حافظه تصمیم میگیرد. این وضعیت میتواند از تمام شدن حافظه host، رسیدن container یا systemd unit به limit، رشد بدون سقف برنامه یا concurrency زیاد ایجاد شود. مقدار free پایین بهتنهایی اثبات OOM نیست؛ Linux بخشی از RAM را برای cache قابلبازیابی استفاده میکند.
نشانههای معمول
- سرویس ناگهان exit میکند و دوباره توسط supervisor بالا میآید
- کاربر 502، connection reset یا قطعی کوتاه میبیند
- در kernel journal رویداد OOM و نام process وجود دارد
- container با exit مرتبط با kill حافظه restart میشود
- پیش از رخداد available پایین و swap/reclaim بالا بوده است
- چند سرویس همزمان کند یا ناپایدار میشوند
شواهد را قبل از Restartهای بیشتر نگه دارید
free -h
cat /proc/meminfo
journalctl -k -b
systemctl --failed
این بررسیها وضعیت و log را میخوانند. خروجی kernel طولانی است؛ timestamp رخداد را محدود و اطلاعات hostname یا مسیر حساس را پیش از اشتراک حذف کنید. اگر سیستم reboot شده، boot قبلی را نیز در journal موجود بررسی کنید؛ retention ناکافی ممکن است شواهد را از بین برده باشد.
Host OOM یا Cgroup OOM؟
در host OOM، کل ماشین زیر فشار است و انتخاب قربانی میان processهای واجد شرایط انجام میشود. در cgroup OOM، یک container یا unit به سقف خود رسیده، حتی اگر host هنوز RAM available داشته باشد. افزایش RAM ماشین برای محدودیت کوچک container اثری ندارد؛ حذف limit نیز میتواند دامنه خرابی را به کل host گسترش دهد.
Process قربانی با عامل فشار فرق دارد
kernel بر اساس مصرف، قابلیت reclaim و تنظیماتی مانند امتیاز OOM تصمیم میگیرد. دیتابیس بزرگ ممکن است قربانی شود، در حالی که burst صدها worker عامل فشار بوده است. فقط نام Killed process را مبنای مقصر دانستن قرار ندهید؛ timeline مصرف همه سرویسها و رویداد تخصیص را ببینید.
RAM لینوکس را درست تفسیر کنید
ستون available، swap activity، page cache و memory سطح cgroup را کنار هم قرار دهید. VSZ معادل RAM واقعی نیست و جمع RSS میتواند حافظه shared را چندبار حساب کند. راهنمای تشخیص مصرف RAM سرور تفاوت RSS، PSS، cache و pressure را توضیح میدهد.
Swap چه نقشی دارد؟
swap میتواند در burst کوتاه فرصت واکنش بدهد، اما latency آن برای workload حساس بالا است و جای ظرفیت کافی نیست. نبود swap ممکن است OOM را زودتر کند؛ swap سنگین نیز سرور را وارد thrashing میکند. نرخ ورود و خروج swap و پاسخ سرویس از مقدار مصرفشده مهمتر است.
سناریوی PHP-FPM
هر worker PHP حافظه خودش را دارد و مجموع workerهای همزمان میتواند از بودجه عبور کند. memory_limit سقف بالقوه هر request است، نه تضمین مصرف کم pool. اگر pm.max_children بدون اندازهگیری افزایش یابد، burst ترافیک RAM را تمام میکند. endpoint سنگین، افزونه یا import نیز اندازه worker را بالا میبرد.
دیتابیس و Cache
MySQL/PostgreSQL و Redis عمداً از حافظه برای cache استفاده میکنند. علاوه بر bufferهای سراسری، connection و عملیات میتوانند مصرف اضافی داشته باشند. تنظیمات نمونه اینترنت را کور اعمال نکنید؛ dataset، connection، query و RAM لازم برای سیستم و برنامه باید در یک بودجه مشترک قرار گیرند.
Docker و Container
مصرف و limit هر container، event OOM، restart count و policy را بررسی کنید. restart policy میتواند failure را بهصورت loop پنهان کند. healthcheck نیز ممکن است در زمان pressure شکست بخورد و restart بیشتری بسازد. log و metric host و container باید timestamp قابل تطبیق داشته باشند.
Memory Leak یا Burst؟
leak با رشد پایدار در workload قابل مقایسه و بازنگشتن حافظه شناخته میشود. burst پس از پایان کار فروکش میکند، و warming cache معمولاً در یک سطح تثبیت میشود. نمودار process/cgroup را با request rate، job، deploy و GC مقایسه کنید. snapshot پس از حادثه برای تشخیص leak کافی نیست.
نشانههای پیش از OOM
OOM معمولاً آخر زنجیره فشار است. افت available، افزایش reclaim، swap activity، طولانی شدن latency و شکست allocation میتوانند زودتر دیده شوند. اگر فقط رخداد نهایی را alert کنید، زمان کافی برای کاهش بار یا متوقف کردن job غیرضروری ندارید. روند و سرعت نزدیک شدن به limit را نیز پایش کنید.
فشار حافظه ممکن است پیش از kill، CPU و I/O را بالا ببرد؛ بنابراین کندی شدید قبل از restart بخشی از همان incident است. metric حافظه را جدا از latency و queue تفسیر نکنید. در workload حساس، synthetic check کمک میکند اثر واقعی reclaim و swap روی کاربر دیده شود.
چرا Restart موقتاً جواب میدهد؟
restart حافظه process و queue را آزاد میکند، اما request سنگین، leak، تعداد worker یا limit نامناسب باقی میماند. اگر تنها اقدام cron برای restart شبانه باشد، هم downtime تکرار میشود و هم نشانه رشد واقعی پنهان میماند. restart باید بخشی از بازیابی کنترلشده باشد، نه اصلاح نهایی.
راهحل کوتاهمدت امن
- اثر کاربر و شواهد kernel/cgroup را ثبت کنید.
- سرویس حیاتی و تولیدکننده فشار را تفکیک کنید.
- ورودی یا job غیرضروری را طبق runbook محدود کنید.
- سرویس قربانی را پس از ایجاد headroom کنترلشده بازیابی کنید.
- سلامت داده، queue و تراکنش نیمهتمام را بررسی کنید.
- memory و نرخ restart را برای بازگشت incident پایش کنید.
kill دستی process یا قطع دیتابیس میتواند write را ناقص کند. اگر نیاز به توقف است، از روش graceful همان سرویس و برنامه rollback استفاده کنید.
اصلاح ریشهای بر اساس علت
- worker بیشازحد: محاسبه concurrency بر اساس اندازه واقعی process
- leak: profile در staging، fix یا rollback نسخه
- Query/گزارش بزرگ: محدودسازی داده یا تبدیل به job پسزمینه
- cache بدون سقف: policy حافظه و eviction متناسب
- limit کوچک cgroup: بازتنظیم با حفظ مرز خرابی
- RAM ناکافی واقعی: افزایش ظرفیت پس از حذف ناکارآمدی
- jobهای همزمان: توزیع زمان و محدودسازی concurrency
آیا OOM Score را تغییر دهیم؟
محافظت افراطی از چند process میتواند kernel را مجبور کند قربانی کماهمیتتر اما ناکافی انتخاب کند یا کل سیستم ناپایدار بماند. تغییر امتیاز باید بر اساس اولویت سرویس، dependency و تست failure انجام شود. این ابزار ظرفیت ایجاد نمیکند و عامل مصرف را رفع نمیکند.
ظرفیتسنجی عملی
بودجه RAM را میان kernel، دیتابیس، cache، runtime، worker و عملیاتهایی مثل backup تقسیم کنید. صدک بالای حافظه هر process و concurrency اوج را بسنجید و headroom برای burst و deploy نگه دارید. مجموع limitهای نظری نباید بسیار فراتر از ظرفیت فیزیکی باشد، مگر کنترل overload روشن باشد.
اشتباههای رایج
- مقصر دانستن خودکار process کشتهشده
- افزایش worker بدون اندازهگیری حافظه
- حذف همه limitهای container
- خاموش کردن swap روی سیستم تحت فشار
- restart زمانبندیشده به جای رفع leak
- پاک کردن cache و ایجاد بار ناگهانی دیتابیس
- نادیده گرفتن memory host چون container limit خورده است
- تغییر score بدون طراحی اولویت سرویس
پیشگیری و Alert
available، swap activity، memory pressure، cgroup usage/limit، OOM event، restart count و queue را مانیتور کنید. هشدار باید پیش از رسیدن به سقف فرصت عمل دهد و runbook آن شامل حفظ شواهد باشد. تست بار کنترلشده در staging باید memory اوج و رفتار overload را آشکار کند.
چه زمانی کمک تخصصی لازم است؟
اگر OOM تکرار میشود یا میان PHP، دیتابیس، Redis و container منبع فشار روشن نیست، افزایش limit میتواند خرابی را فقط جابهجا کند. مدیریت ماهانه سرور میتواند timeline حافظه، cgroup و workload را تحلیل و ظرفیت را بدون حدس تنظیم کند.
پرسشهای متداول
آیا OOM Killer یک خطای نرمافزاری است؟
خود OOM Killer سازوکار حفاظتی kernel است؛ علت فشار میتواند leak، ظرفیت کم، concurrency یا limit نامناسب باشد.
چرا دیتابیس کشته شد؟
انتخاب قربانی به امتیاز و وضعیت processها وابسته است؛ این موضوع ثابت نمیکند دیتابیس عامل اصلی بوده است.
آیا اضافه کردن Swap مشکل را حل میکند؟
ممکن است فرصت واکنش بدهد، اما leak یا کمبود ظرفیت را رفع نمیکند و میتواند latency را بالا ببرد.
چرا فقط container ریاستارت میشود؟
احتمال دارد cgroup همان container به limit رسیده باشد، حتی اگر host حافظه available داشته باشد.