systemd میں process کی memory اور CPU کیسے محدود کریں
MemoryHigh، MemoryMax، CPUQuota اور TasksMax سے systemd unit کو محدود کریں۔ cgroup v2 کی exact limits لگائیں اور OOM kill کے بعد لاگ میں وجہ تلاش کریں۔
systemd drop-in کے ذریعے process کی memory اور CPU محدود کریں
Linux VPS پر process کی memory اور CPU محدود کرنے کے لیے اس unit میں چند لائنیں شامل کریں جو process چلاتی ہے۔ MemoryMax= memory کی سخت حد مقرر کرتا ہے۔ CPUQuota= processor time کی حد مقرر کرتا ہے۔ دونوں حدود cgroup v2 (control groups، version 2) نافذ کرتا ہے۔ یہ kernel feature ہے جسے systemd پہلے ہی سرور کی ہر service کا حساب رکھنے کے لیے استعمال کرتا ہے۔
sudo systemctl edit myapp.serviceاس سے ایک drop-in file کھلتی ہے جس کے comments میں ہدایات موجود ہوتی ہیں۔ ان ہدایات سے اوپر یہ شامل کریں:
[Service]
MemoryHigh=512M
MemoryMax=768M
MemorySwapMax=0
CPUQuota=80%
TasksMax=128sudo systemctl daemon-reload
sudo systemctl restart myapp.service
systemctl show myapp.service -p MemoryHigh -p MemoryMax -p CPUQuotaPerSecUSec -p TasksMaxsystemctl show کو آپ کے اعداد kernel کی اپنی units میں دوبارہ دکھانے چاہییں: MemoryMax=805306368 اور CPUQuotaPerSecUSec=800ms۔ اگر یہ MemoryMax=infinity دکھائے تو drop-in load نہیں ہوئی۔ تصدیق کریں کہ file /etc/systemd/system/myapp.service.d/override.conf پر موجود ہے اور اس کی ابتدا [Service] header سے ہوتی ہے، کیونکہ اس سے پہلے section کے بغیر settings line ہونے پر systemd Assignment outside of section. Ignoring. log کرتا ہے اور service کو بالکل limits کے بغیر شروع کرتا ہے۔
اس guide کے باقی حصے میں بتایا گیا ہے کہ یہ اعداد کیسے منتخب کریں اور limits مقرر کرنے کے بعد بھی کون سے مسائل پیش آ سکتے ہیں۔
ایک بے قابو process VPS کو کیسے منجمد کر دیتا ہے، حالانکہ وہ پوری memory استعمال نہیں کرتا
جو process سخت memory cap تک پہنچتا ہے، وہ تقریباً ایک second میں ختم ہو جاتا ہے اور service دوبارہ start ہو جاتی ہے۔ یہ بہتر صورت ہے۔ خراب صورت وہ ہے جس میں کچھ بھی ختم نہیں ہوتا: box ping کا جواب دیتا ہے، SSH connection قبول کرتا ہے، لیکن shell prompt کبھی ظاہر نہیں ہوتا۔ machine فعال اور مصروف ہوتی ہے، مگر اس کی کوئی بھی سرگرمی مفید نہیں ہوتی۔
یہ طریقۂ کار سمجھنا ضروری ہے، کیونکہ یہ واضح نہیں ہوتا۔ جب free memory کم ہو جاتی ہے تو kernel نئے pages دینے کے بجائے موجودہ pages reclaim کرتا ہے۔ reclaim کرنے کے لیے سب سے کم لاگت والے pages file-backed ہوتے ہیں، اور page cache ان تمام processes کے executable code کو محفوظ رکھتا ہے جو چل رہے ہوتے ہیں۔ اس لیے kernel sshd کے text pages کو خارج کر دیتا ہے، اور اس کے بعد چلنے والی اگلی instruction sshd ایک page fault پیدا کرتی ہے جسے وہ bytes دوبارہ storage سے پڑھنے پڑتے ہیں۔ ہر process چلنے کے بجائے disk کا انتظار کرنے لگتا ہے۔ وہی pages بار بار خارج ہو کر واپس آتے ہیں۔ اسے thrashing کہتے ہیں۔
VPS پر یہ صورت laptop کے مقابلے میں دو وجوہات سے زیادہ خراب ہوتی ہے۔ Storage اکثر network-attached یا shared ہوتی ہے، اس لیے ہر fault میں local NVMe device کے مقابلے میں زیادہ milliseconds لگتے ہیں۔ دوسری وجہ یہ ہے کہ kernel وقت نہیں بلکہ failure کو measure کرتا ہے: جب تک reclaim کسی page کو واپس حاصل کرتا رہتا ہے، چاہے بہت آہستہ، kernel سمجھتا ہے کہ progress ہو رہی ہے اور out of memory (OOM) killer کو نہیں چلاتا۔ کوئی box کسی process کو kill کیے بغیر کئی minutes تک اسی حالت میں رہ سکتا ہے۔
آپ اس عمل کو براہ راست monitor کر سکتے ہیں۔ Linux 4.20 اور اس کے بعد کے versions میں kernel pressure stall information (PSI) فراہم کرتا ہے:
cat /proc/pressure/memory
cat /proc/pressure/iosome avg10=63.72 avg60=41.02 avg300=12.33 total=13729481
full avg10=48.15 avg60=30.44 avg300=8.90 total=9114233full والی line اہم ہے۔ full avg10=48.15 کا مطلب ہے کہ گزشتہ 10 seconds کے دوران box پر ہر runnable task کا 48% وقت memory-related work کے انتظار میں stalled رہا، اس لیے کچھ بھی نہیں چل سکا۔ صحت مند server میں full کی قدر تقریباً zero ہوتی ہے۔ 10 سے زیادہ ہونے پر انسان کو system سست محسوس ہوتا ہے، جبکہ 40 یا اس سے زیادہ وہ حالت ہے جسے لوگ frozen کہتے ہیں۔
اسی لیے صرف limit مقرر کرنا کوئی ضمانت نہیں ہے۔ MemoryHigh= کے تحت چلنے والی unit کو kill کرنے کے بجائے throttle کیا جاتا ہے۔ وہ فعال رہتی ہے مگر سست ہو جاتی ہے، اور کوئی چیز اسے restart نہیں کرتی، کیونکہ systemd کے نقطۂ نظر سے وہ کبھی failed نہیں ہوئی۔ ایسی capped unit جسے swap استعمال کرنے کی اجازت ہو، وہ reads اور writes پیدا کرتی ہے جو اس unit کے account میں charge ہوتے ہیں، لیکن انہیں ایک ہی shared device پورا کرتا ہے۔ اس طرح box کی ہر دوسری service کے لیے /proc/pressure/io بڑھ سکتا ہے۔ Limits صرف یہ طے کرتی ہیں کہ shortage کی قیمت کون ادا کرے گا؛ وہ capacity پیدا نہیں کر سکتیں۔
اپنے VPS پر cgroup v2 کی تصدیق کریں
stat -fc %T /sys/fs/cgroupcgroup2fs متحد hierarchy ہے، اور ذیل کی تمام settings کے لیے یہی درکار ہے۔ tmpfs کا مطلب ہے کہ سسٹم نے پرانے v1 layout کے ساتھ boot کیا ہے، جہاں MemoryHigh= اور MemorySwapMax= موجود نہیں ہوتے اور ہر unit کے لیے OOM رویہ مختلف ہوتا ہے۔ Ubuntu 22.04 اور اس کے بعد کے ورژنز، اور Debian 11 اور اس کے بعد کے ورژنز، default طور پر v2 استعمال کرتے ہیں۔ پرانی image یا systemd.unified_cgroup_hierarchy=0 کے ساتھ boot کیا گیا kernel ایسا نہیں کرتا۔
cgroup v2 پر systemd ہر unit کے لیے memory accounting default طور پر فعال کرتا ہے، اس لیے اعداد پہلے سے دستیاب ہوتے ہیں:
systemd-cgtop -mیہ cgroups کو memory استعمال کے لحاظ سے ترتیب دے کر دکھاتا ہے۔ جب سرور اب بھی جواب دے رہا ہو تو یہ معلوم کرنے کا تیز ترین طریقہ ہے کہ "اس سسٹم کے وسائل کون استعمال کر رہا ہے"۔ اگر سرور نیا ہے تو نئے VPS کے پہلے دس منٹ میں account اور firewall کا کام اس مرحلے سے پہلے کیا جاتا ہے۔
MemoryHigh رفتار کو محدود کرتا ہے۔ MemoryMax ختم کر دیتا ہے۔
میموری کی دونوں settings کے درمیان فرق طے کرتا ہے کہ failure کس شکل میں ظاہر ہو گا۔
MemoryHigh=ایک نرم حد ہے۔ اس سے زیادہ ہونے پر kernel اس cgroup سے میموری زیادہ شدت سے reclaim کرتا ہے اور جان بوجھ کر اس کی allocations کو سست کر دیتا ہے۔ استعمال اس عدد سے آگے بھی جا سکتا ہے، اور کچھ بھی kill نہیں کیا جاتا۔MemoryMax=ایک سخت حد ہے۔ جب اس حد کے اندر allocation پوری نہ ہو سکے تو OOM killer اسی cgroup کے اندر چلتا ہے اور اس unit کے اپنے processes میں سے ایک process کو kill کر دیتا ہے۔
MemoryMax= کو ہر اس چیز پر set کرنے کی اصل وجہ یہی دوسرا حصہ ہے جس پر آپ کو مکمل اعتماد نہ ہو۔ کسی cap کے بغیر memory shortage پورے سرور کا مسئلہ بن جاتی ہے، اور global OOM killer اپنے victim کا انتخاب oom_score کے مطابق کرتا ہے، جس کا عموماً مطلب سب سے بڑا process ہوتا ہے۔ سب سے بڑا process عموماً آپ کا database ہوتا ہے، نہ کہ وہ script جس نے memory leak کیا ہو۔ cap کے ساتھ kill اسی unit کے اندر ہوتا ہے جس نے مسئلہ پیدا کیا۔
دونوں settings set کریں، اور MemoryHigh= کو MemoryMax= سے تقریباً 20 سے 30 فیصد کم رکھیں۔ یہ فرق warning zone بناتا ہے: آہستہ memory leak High کو عبور کرتا ہے اور service سست ہو جاتی ہے، جبکہ اچانک spike Max سے براہِ راست گزر کر process کو ختم کر دیتا ہے۔
Percentage values installed physical memory کے حساب سے پڑھی جاتی ہیں۔ اس لیے 4 GB plan پر MemoryMax=25% کا مطلب 1 GB ہے، اور plan resize کرنے کے بعد بھی یہ پوری مشین کی میموری کا ایک چوتھائی رہتا ہے۔ MemorySwapMax=0 اس unit کو swap سے مکمل طور پر باہر رکھتا ہے، جس سے طویل وقت تک سست رہنے کے بجائے تیزی سے اور واضح طور پر kill ہو جاتا ہے۔
کچھ services آپ کو ان کی memory ضرورت پہلے سے طے کرنے دیتی ہیں، بجائے اس کے کہ آپ اسے ناپیں۔ Ollama unit اپنے KV cache کا سائز آپ کے دیے گئے context window سے طے کرتی ہے، اس لیے کسی unit کے لیے ceiling منتخب کرنے سے پہلے دیکھیں کہ num_ctx بڑھانے سے RAM پر کیا اثر پڑتا ہے۔
کسی cap کے ساتھ restart policy بھی set کریں، ورنہ kill کے بعد service صرف stopped حالت میں رہ جائے گی۔
[Unit]
StartLimitIntervalSec=300
StartLimitBurst=5
[Service]
Restart=on-failure
RestartSec=5sStartLimit* کو [Unit] میں اور Restart= کو [Service] میں شامل کرنا چاہیے۔ کسی ایک کو غلط section میں رکھنے پر systemd اسے نظرانداز کر دیتا ہے۔ پانچ منٹ میں پانچ restarts کسی عارضی خرابی کے بجائے leak کی علامت ہیں، اس لیے اس کے بعد systemd مزید کوشش ترک کر کے unit کو failed حالت میں چھوڑ دیتا ہے۔ بعد میں یہی وہ state ہے جسے آپ تلاش کرنا چاہتے ہیں، نہ کہ ایسا crash loop جو مسئلے کو چھپا دے۔
CPUQuota سے CPU محدود کریں، یا CPUWeight کے ذریعے حصہ مقرر کریں
CPUQuota= ایک CPU پر دستیاب وقت کا فیصد استعمال کرتا ہے۔ CPUQuota=50% ایک core کا نصف ہے۔ CPUQuota=200% دو cores کے برابر ہے، اور unit اسے اپنی ضرورت کے مطابق زیادہ سے زیادہ اتنے threads میں تقسیم کر سکتی ہے۔ 2 vCPU پلان پر CPUQuota=200% پوری مشین کے برابر ہے۔
CPUWeight= زیادہ تر services کے لیے بہتر default ہے۔ یہ 1 سے 10000 تک نسبتی حصہ مقرر کرتا ہے، جبکہ kernel کا default 100 ہے۔ یہ صرف اس وقت مؤثر ہوتا ہے جب کوئی دوسرا کام بھی CPU کے لیے مقابلہ کر رہا ہو: load کے دوران CPUWeight=20 پر چلنے والا backup job، 100 پر چلنے والے web server کو ترجیح دے گا، اور جب مشین idle ہو تو دستیاب تمام CPU استعمال کرے گا۔ سخت quota، idle capacity کو ضائع کر دیتا ہے۔
CPU limit سے حاصل ہونے والے فائدے کے بارے میں حقیقت پسند رہیں۔ CPU-bound process عموماً Linux کو freeze نہیں کرتا، کیونکہ scheduler سب processes کو باری باری وقت دیتا رہتا ہے۔ مشین کو ناکارہ کرنے والی اصل وجہ اکثر memory ہوتی ہے۔ جب آپ کو قابلِ پیش گوئی حد درکار ہو تو CPUQuota= استعمال کریں، مثلاً کسی build یا ایسے agent کے لیے جو بصورتِ دیگر ایک گھنٹے تک پوری CPU استعمال کرتا رہے۔ اس قسم کے workload کے لیے sizing ایک الگ موضوع ہے، جس کی وضاحت coding agent VPS کے لیے کتنی RAM اور CPU درکار ہے میں کی گئی ہے۔
اگر CPU busy دکھائی دے، لیکن آپ کا کوئی process زیادہ کام نہ کر رہا ہو، تو وجہ hypervisor کی دوسری طرف ہو سکتی ہے۔ اسے noisy neighbour کی CPU steal time کہتے ہیں، اور آپ کی مقرر کردہ کوئی quota اسے تبدیل نہیں کر سکتی۔
TasksMax فورک لوپ کو روکتا ہے
TasksMax= ان processes اور threads کی تعداد ہے جنہیں کوئی unit رکھ سکتی ہے۔ Threads بھی شمار ہوتے ہیں، اس لیے Java یا Go service کو process list سے ظاہر ہونے والی تعداد کے مقابلے میں زیادہ گنجائش درکار ہوتی ہے۔ یہ ایسے script کے خلاف کم ترین لاگت والا تحفظ ہے جو loop میں fork کرتا ہے، کیونکہ fork اسی unit کے اندر ناکام ہو جاتا ہے اور پورا server process IDs ختم ہونے کی وجہ سے متاثر نہیں ہوتا۔
TasksMax=128جب کوئی unit اس حد تک پہنچتی ہے تو kernel cgroup کا نام درج کرتے ہوئے ایک log line لکھتا ہے:
cgroup: fork rejected by pids controller in /system.slice/myapp.serviceپروگرام عموماً خود fork: retry: Resource temporarily unavailable رپورٹ کرتا ہے۔ یہ جانچنے کے لیے کہ manager بطور default کیا لاگو کرتا ہے، systemctl show -p DefaultTasksMax استعمال کریں۔
systemd-run کے ساتھ یک وقتی کام محدود کریں
اس میں سے کوئی بھی کام کرنے کے لیے unit file ضروری نہیں۔ systemd-run ایک واحد command کے گرد عارضی unit بناتا ہے۔
sudo systemd-run --scope -p MemoryMax=1G -p MemorySwapMax=0 -p CPUQuota=50% -p TasksMax=64 ./import-data.sh--scope پہلے Running scope as unit: run-r7c1a....scope دکھانے کے بعد command کو آپ کے terminal میں چلاتا ہے۔ output آپ کی screen پر رہتا ہے، اور command ختم ہونے پر limits ختم ہو جاتی ہیں۔ systemd.resource-control کی کوئی بھی property، -p کے بعد کام کرتی ہے۔
طویل کام کے لیے --scope ہٹا دیں اور اسے نام دیں۔ اس کے بعد یہ background میں transient service کے طور پر چلتا ہے اور journal میں logs لکھتا ہے:
sudo systemd-run --unit=nightly-import -p MemoryMax=1G -p CPUWeight=20 ./import-data.sh
journalctl -u nightly-import -fجب آپ root نہ ہوں تو یہی options --user کے ساتھ بھی کام کرتے ہیں، تاہم آپ کے user manager کے پاس صرف وہی controllers ہوتے ہیں جو اسے delegate کیے گئے ہوں۔ اس لیے کوئی property وہاں مسترد ہو سکتی ہے۔ ایسا ہونے پر اسے sudo کے ساتھ چلائیں۔ جب کسی کام کو مستقل جگہ مل جائے تو settings بغیر تبدیلی کے حقیقی unit میں منتقل کر دیں: script کو systemd service اور timer کے طور پر چلانا۔
Swap کے سوال کا دیانت دار جواب
Swap ناکامی کو روکتا نہیں، بلکہ ناکامی کی صورت بدل دیتا ہے۔
Swap نہ ہو تو memory leak حد تک پہنچتی ہے اور چند سیکنڈ میں کوئی process ختم ہو جاتا ہے۔ outage نمایاں اور مختصر ہوتا ہے، اور بعد میں journal میں اس کی وجہ سمجھنا آسان ہوتا ہے۔ Swap موجود ہو تو kernel کم استعمال ہونے والے anonymous pages کو disk پر لکھ کر وقت حاصل کرتا ہے۔ اگر process کسی سطح پر مستحکم ہونے والا ہو تو swap آپ کو بچا لیتا ہے۔ لیکن اگر process بے قابو ہو تو swap پانچ سیکنڈ کی outage کو بیس منٹ کے stall میں بدل دیتا ہے۔ یہ stall زیادہ خراب ہے، کیونکہ ختم ہو جانے والا process پھر بھی آپ کو working shell دیتا ہے، جبکہ thrashing box ایسا نہیں کرتا۔
swapon --show
free -hچھوٹے VPS پر ایک قابلِ عمل درمیانی طریقہ یہ ہے کہ ایسے pages کے لیے ایک محدود swap file رکھیں جو ایک بار allocate ہونے کے بعد دوبارہ کبھی استعمال نہیں ہوتے، اور جن units کو آپ ختم ہونے دینے کے لیے تیار ہیں ان پر MemorySwapMax=0 مقرر کریں۔ اہم services اپنا swap برقرار رکھیں۔ غیر متوقع services جلد حد سے ٹکرائیں اور restart ہو جائیں۔
vm.swappiness کم کرنا کمزور طریقہ ہے، اور اس کی وجہ سمجھنا مفید ہے۔ یہ صرف page cache کو evict کرنے اور anonymous pages کو swap کرنے کے درمیان توازن بدلتا ہے، اور دونوں صورتوں میں بعد میں disk read درکار ہوتی ہے۔ اس سے صرف یہ بدلتا ہے کہ کون سے pages thrash کرتے ہیں، یہ نہیں کہ box thrash کرے گا یا نہیں۔
ابتدائی OOM daemon تعطل شروع ہونے سے پہلے process ختم کرتا ہے
Kernel reclaim کے مکمل طور پر ناکام ہونے کا انتظار کرتا ہے۔ چھوٹے VPS پر یہی انتظار وہ وقت ہوتا ہے جس میں machine دستیاب نہیں رہتی۔ دو userspace daemons خود memory monitor کر کے process جلد ختم کرتے ہیں۔
earlyoom available memory اور free swap کو monitor کرتا ہے۔ ان میں سے کوئی بھی threshold سے کم ہو جائے تو یہ سب سے زیادہ score والے process کو ختم کرتا ہے۔
sudo apt install earlyoom
systemctl status earlyoomDebian اور Ubuntu کا package install کے وقت service شروع کر دیتا ہے۔ اس کی options /etc/default/earlyoom میں موجود ہوتی ہیں:
EARLYOOM_ARGS="-m 5,2 -s 5,2 --avoid '^(sshd|systemd)$' --prefer '^(node|python3)$'"-m PERCENT available memory کی کم از کم مقدار مقرر کرتا ہے، جبکہ -s PERCENT free swap کی کم از کم مقدار مقرر کرتا ہے۔ دونوں کی default قدر 10 percent ہے۔ ہر pair میں دوسرا number SIGKILL point ہوتا ہے۔ پہلے value سے کم ہونے پر earlyoom SIGTERM بھیجتا ہے، پھر دوسرے value سے کم ہونے پر SIGKILL بھیجتا ہے۔ دوسرے value کی default قدر پہلے value کی نصف ہوتی ہے۔ تبدیلی لاگو کرنے کے لیے sudo systemctl restart earlyoom چلائیں۔ یہ دیکھنے کے لیے کہ کس process کو ختم کیا گیا اور وہ process کتنی memory استعمال کر رہا تھا، journalctl -u earlyoom پڑھیں۔
systemd-oomd دوسری option ہے۔ اس کا manual page اسے یوں بیان کرتا ہے: "ایک system service جو cgroups-v2 اور pressure stall information (PSI) استعمال کر کے kernel space میں OOM ہونے سے پہلے نگرانی اور اصلاحی کارروائی کرتی ہے"۔ یہ single processes کے بجائے پورے cgroups پر کارروائی کرتا ہے۔ اس لیے یہ کسی stray child کے بجائے ایک unit کو ختم کرتا ہے۔ Units ManagedOOMMemoryPressure=kill یا ManagedOOMSwap=kill کے ذریعے opt in کرتی ہیں، جبکہ thresholds /etc/systemd/oomd.conf میں موجود ہوتی ہیں۔
systemctl status systemd-oomd
oomctloomctl اس وقت monitor کیے جانے والے units دکھاتا ہے۔ Server image پر یہ اکثر کچھ بھی نہیں دکھاتا، کیونکہ setting ہر unit کے لیے opt-in ہوتی ہے۔ صرف ایک daemon منتخب کریں اور اسی پر قائم رہیں۔ دونوں چلانے سے victim منتخب کرنے کے لیے دو mechanisms ایک دوسرے سے race کرتے ہیں، اور کسی kill کی وجہ بعد میں معلوم کرنا زیادہ مشکل ہو جاتا ہے۔
کون سا unit ذمہ دار تھا؟
kernel سے شروع کریں، کیونکہ یہ اپنی جانب سے کیے گئے ہر process kill کو ریکارڈ کرتا ہے۔
journalctl -k --grep "Killed process" --since "2 hours ago"global OOM killer کی جانب سے کیا گیا kill اس طرح دکھائی دیتا ہے:
Out of memory: Killed process 4127 (node) total-vm:2731084kB, anon-rss:1874232kB, file-rss:0kB, shmem-rss:0kB, UID:1000 pgtables:4212kB oom_score_adj:0anon-rss اس memory کی مقدار ہے جو process کے ختم ہونے کے وقت RAM میں موجود تھی؛ یہاں یہ تقریباً 1.8 GB ہے۔ مربع brackets میں موجود نام کو احتیاط سے دیکھیں۔ یہ وہ process ہے جسے kernel نے victim کے طور پر منتخب کیا۔ kernel عموماً سب سے بڑے process کو منتخب کرتا ہے، لیکن shortage پیدا کرنے والا process ہمیشہ یہی نہیں ہوتا۔
cgroup limit کی وجہ سے ہونے والے kill کا prefix مختلف ہوتا ہے، اور اس کے اوپر دکھائی جانے والی report اس cgroup کا نام بتاتی ہے جو اپنی حد تک پہنچا:
Memory cgroup out of memory: Killed process 8811 (python3) total-vm:1044320kB, anon-rss:769112kB, file-rss:0kB, shmem-rss:0kB, UID:998 pgtables:1720kB oom_score_adj:0یہ prefix زیادہ تر تشخیص فراہم کرتا ہے۔ Memory cgroup out of memory کا مطلب ہے کہ ایک unit اپنی مقرر کردہ MemoryMax= تک پہنچا، جبکہ باقی system معمول کے مطابق تھا۔ سادہ Out of memory کا مطلب ہے کہ پوری machine کی memory ختم ہو گئی؛ اس لیے آپ کی caps یا تو موجود نہیں تھیں یا مجموعی طور پر بہت زیادہ تھیں۔
اس کے بعد systemd سے پوچھیں کہ اس نے کیا دیکھا:
systemctl status myapp.service
journalctl -u myapp.service -n 50myapp.service: A process of this unit has been killed by the OOM killer.
myapp.service: Main process exited, code=killed, status=9/KILL
myapp.service: Failed with result 'oom-kill'.systemctl status ایک ہی سطر میں یہی بات بتاتا ہے، یعنی Active: failed (Result: oom-kill)۔
cgroup counters تیسرا ذریعہ ہیں، اور واحد ایسا ذریعہ ہیں جو throttling ریکارڈ کرتے ہیں۔ throttling کبھی بھی log line پیدا نہیں کرتی:
cat /sys/fs/cgroup/system.slice/myapp.service/memory.events
cat /sys/fs/cgroup/system.slice/myapp.service/memory.peaklow 0
high 4213
max 118
oom 12
oom_kill 12high اس تعداد کو شمار کرتا ہے کہ unit کتنی بار MemoryHigh= سے تجاوز کر کے throttled ہوا۔ max بتاتا ہے کہ unit کتنی بار hard cap تک پہنچا، اور oom_kill ان processes کی تعداد بتاتا ہے جنہیں واقعی kill کیا گیا۔ oom_kill 0 کے ساتھ بڑا high اس پہلے بیان کیے گئے خاموش معاملے کی نشاندہی کرتا ہے: service چل رہی ہے، انتہائی سست ہو چکی ہے، اور اس نے کسی کو failure report نہیں کی۔ memory.peak (Linux 5.19 اور اس کے بعد کے ورژنز میں) وہ زیادہ سے زیادہ usage محفوظ رکھتا ہے جس تک cgroup پہنچی۔ MemoryMax= کی مقدار مقرر کرتے وقت یہی number استعمال کریں۔ جب unit restart ہوتا ہے تو دونوں files reset ہو جاتی ہیں، کیونکہ systemd cgroup دوبارہ بناتا ہے۔
ان سب کے لیے ایک بنیادی prerequisite ضروری ہے۔ اگر /var/log/journal موجود نہ ہو تو journal RAM میں رہتا ہے، اور box کی recovery کے لیے درکار reboot کے بعد ہر line ضائع ہو جاتی ہے۔
sudo mkdir -p /var/log/journal
sudo systemd-tmpfiles --create --prefix /var/log/journal
sudo systemctl restart systemd-journald
journalctl --list-bootsjournalctl --list-boots کا current boot سے زیادہ entries دکھانا اس بات کی علامت ہے کہ history اب محفوظ رہتی ہے۔ اس لیے journalctl -k -b -1 اس boot کے kernel messages دکھا سکتا ہے جو failure کے ساتھ ختم ہوا تھا۔
چھوٹے VPS کے لیے نقطۂ آغاز
2 GB پلان میں kernel اور page cache کے لیے 300 سے 400 MB مختص رکھیں۔ caps کا مجموعہ مکمل 2 GB تک نہ پہنچنے دیں، کیونکہ ہر unit ایک ہی وقت میں peak پر جا سکتی ہے۔ اہم service کو سب سے بڑا حصہ دیں، پھر اس کے گرد تمام غیر یقینی workloads پر cap لگائیں۔
[Service]
MemoryHigh=256M
MemoryMax=384M
MemorySwapMax=0
CPUWeight=20
TasksMax=64
Restart=on-failure
RestartSec=5sسرور تک رسائی برقرار رکھنے کے لیے ایک اضافی setting مفید ہے۔ OOMScoreAdjust=-500 کو ssh.service کے drop-in میں شامل کرنے سے global OOM killer کے لیے آپ کے SSH daemon کو victim منتخب کرنے کا امکان بہت کم ہو جاتا ہے۔ یہی فرق box کو درست کرنے اور اسے control panel سے reboot کرنے کے درمیان ہو سکتا ہے۔ اس سے صرف victim منتخب کرنے کا kernel کا فیصلہ تبدیل ہوتا ہے۔ اس سے stall کا دورانیہ کم نہیں ہوتا۔
Containers اپنے الگ cgroups میں چلتے ہیں۔ یہ cgroups آپ کی unit files کے بجائے container runtime بناتا ہے۔ اس لیے docker.service پر لگائی گئی limit کسی ایک container کی limit نہیں بنتی۔ MemoryMax= اور CPUQuota= کے فی container متبادل Docker Compose میں memory اور CPU limits مقرر کرنا میں بیان کیے گئے ہیں۔
FAQ
میری VPS runaway process کو ختم کرنے کے بجائے freeze کیوں ہو گئی؟
کیونکہ kernel پیش رفت کا اندازہ اس بات سے لگاتا ہے کہ memory reclaim سے pages واپس مل رہے ہیں یا نہیں، نہ کہ اس عمل میں کتنا وقت لگ رہا ہے۔ جب memory کم ہو تو یہ running programs کے executable pages سمیت page cache کو evict کرتا ہے، پھر اگلی instruction پر انہیں دوبارہ پڑھتا ہے۔ ہر چیز storage کا انتظار کرتی رہتی ہے اور کوئی allocation تکنیکی طور پر fail نہیں ہوتی، اس لیے OOM killer کبھی call نہیں ہوتا۔ مسئلہ جاری ہونے کے دوران /proc/pressure/memory چیک کریں: full avg10 کا 40 سے زیادہ ہونا ظاہر کرتا ہے کہ گزشتہ 10 seconds میں تقریباً کوئی task run نہیں کر سکا۔ earlyoom جیسا userspace daemon system اس حالت تک پہنچنے سے پہلے process ختم کر دیتا ہے۔
MemoryHigh اور MemoryMax میں کیا فرق ہے؟
MemoryHigh= ایک soft cap ہے جو throttling کرتا ہے۔ kernel unit سے pages aggressively reclaim کرتا ہے اور اس کی allocations سست کر دیتا ہے، لیکن usage اس number سے تجاوز کر سکتی ہے اور کوئی process ختم نہیں ہوتا۔ MemoryMax= ایک hard cap ہے: اگر اس cap کے اندر allocation پوری نہ ہو سکے تو اسی unit کے اپنے cgroup میں OOM killer activate ہوتا ہے۔ اس طرح مسئلہ پیدا کرنے والا process ختم ہوتا ہے، نہ کہ server پر موجود سب سے بڑا process۔ MemoryHigh= کو MemoryMax= سے کم رکھیں اور ان دونوں کے درمیان فرق کو warning zone سمجھیں۔
مجھے کیسے معلوم ہو گا کہ OOM killer نے کس service کو نشانہ بنایا؟
journalctl -k --grep "Killed process" --since "2 hours ago" چلائیں۔ Memory cgroup out of memory سے شروع ہونے والی line کا مطلب ہے کہ کسی unit نے اپنا MemoryMax= حاصل کر لیا، جبکہ سادہ Out of memory کا مطلب ہے کہ پوری machine کی memory ختم ہو گئی۔ پھر journalctl -u <unit> -n 50 چلائیں اور Failed with result 'oom-kill' تلاش کریں۔ اگر آپ کے server پر /var/log/journal موجود نہیں ہے تو journal RAM میں محفوظ تھا اور reboot کے ساتھ evidence ختم ہو گیا۔ اگلے incident سے پہلے یہ directory بنا لیں۔
کیا مجھے چھوٹی VPS میں swap شامل کرنی چاہیے؟
ایک چھوٹی swap file ان cold pages کے لیے مفید ہوتی ہے جو ایک بار allocate ہوتے ہیں اور پھر دوبارہ استعمال نہیں ہوتے۔ یہ runaway process میں مدد نہیں کرتی۔ یہ process کو ختم ہونے میں تاخیر دیتی ہے اور مختصر outage کو ایک طویل stall میں بدل دیتی ہے، جس کے دوران آپ login کر کے مسئلہ حل نہیں کر سکتے۔ swap کو محدود رکھیں اور ان units پر MemorySwapMax=0 مقرر کریں جنہیں ختم ہونے دینا قابل قبول ہے۔ اس طرح یہ units اپنی حد تک پہنچ کر جلد restart ہو جائیں گی، جبکہ اہم services اپنی swap برقرار رکھیں گی۔
کیا unit file لکھے بغیر کسی command کو محدود کیا جا سکتا ہے؟
ہاں۔ sudo systemd-run --scope -p MemoryMax=1G -p CPUQuota=50% ./script.sh آپ کے terminal میں command کو transient scope کے اندر ان limits کے ساتھ چلاتا ہے، اور command ختم ہوتے ہی یہ limits بھی ختم ہو جاتی ہیں۔ systemd.resource-control کی ہر property -p کے بعد دستیاب ہوتی ہے، اس لیے MemorySwapMax=، TasksMax= اور CPUWeight= بھی وہاں کام کرتے ہیں۔ background میں job چلانے کے لیے --scope ہٹائیں اور --unit=name شامل کریں، تاکہ اس کا output journal میں محفوظ ہو۔