SSD Nodes Learn 🎉 VPS $5.50/ماہ سے
تعلیمی Matt Connorتحریر: Matt Connor · اپ ڈیٹ شدہ 2026-08-13

systemd سے process کی memory اور CPU محدود کریں

MemoryHigh، MemoryMax، CPUQuota اور TasksMax سے systemd unit محدود کریں، پھر OOM kill اور stall کی وجہ جانیں، کیونکہ cap کے باوجود VPS منجمد ہو سکتا ہے۔

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 کی وہ سہولت ہے جسے systemd پہلے ہی سرور کی ہر service کا حساب رکھنے کے لیے استعمال کرتا ہے۔

sudo systemctl edit myapp.service

اس سے ایک drop-in file کھلتی ہے جس میں comments کی صورت میں ہدایات موجود ہوتی ہیں۔ ان ہدایات سے اوپر یہ شامل کریں:

[Service]
MemoryHigh=512M
MemoryMax=768M
MemorySwapMax=0
CPUQuota=80%
TasksMax=128
sudo systemctl daemon-reload
sudo systemctl restart myapp.service
systemctl show myapp.service -p MemoryHigh -p MemoryMax -p CPUQuotaPerSecUSec -p TasksMax

systemctl 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 کو کسی حد کے بغیر شروع کر دیتا ہے۔

اس guide کے باقی حصے میں بتایا گیا ہے کہ یہ اعداد کیسے منتخب کیے جائیں اور حدود مقرر کرنے کے بعد بھی کیا مسائل پیش آ سکتے ہیں۔

ایک بے قابو process VPS کو اس وقت بھی کیوں منجمد کر دیتا ہے جب وہ اسے مکمل طور پر نہیں بھرتا

جو process سخت memory cap تک پہنچتا ہے، وہ تقریباً ایک سیکنڈ میں ختم ہو جاتا ہے اور service دوبارہ شروع ہو جاتی ہے۔ یہ اچھی صورت ہے۔ خراب صورت وہ ہے جس میں کچھ بھی ختم نہیں ہوتا: box ping کا جواب دیتا ہے، SSH connection قبول کرتا ہے، لیکن shell prompt ظاہر نہیں ہوتا۔ مشین فعال اور مصروف ہوتی ہے، مگر اس میں ہونے والا کوئی کام مفید نہیں ہوتا۔

اس کا طریقۂ کار سمجھنا ضروری ہے، کیونکہ یہ واضح نہیں ہوتا۔ جب free memory کم ہو جاتی ہے تو kernel نئے pages دینے کے بجائے موجودہ pages واپس حاصل کرتا ہے۔ واپس حاصل کرنے کے لیے سب سے کم لاگت والے pages file-backed ہوتے ہیں، اور page cache ان تمام running programs کا 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 کی پیمائش کرتا ہے: جب تک reclaim کسی نہ کسی رفتار سے page واپس حاصل کرتا رہتا ہے، kernel سمجھتا ہے کہ پیش رفت ہو رہی ہے اور out of memory (OOM) killer کو فعال نہیں کرتا۔ کوئی box اس حالت میں کئی منٹ رہ سکتا ہے، اس سے پہلے کہ کوئی process ختم ہو۔

آپ اس صورت حال کو ہوتے ہوئے دیکھ سکتے ہیں۔ Linux 4.20 اور اس کے بعد کے versions میں kernel pressure stall information (PSI) فراہم کرتا ہے:

cat /proc/pressure/memory
cat /proc/pressure/io
some avg10=63.72 avg60=41.02 avg300=12.33 total=13729481
full avg10=48.15 avg60=30.44 avg300=8.90 total=9114233

full کی line اہم ہے۔ full avg10=48.15 کا مطلب ہے کہ گزشتہ دس seconds میں 48% وقت box کے تمام runnable tasks memory-related کام کے انتظار میں stalled رہے، اس لیے کچھ بھی نہیں چلا۔ صحت مند server میں full کی قدر تقریباً zero ہوتی ہے۔ 10 سے زیادہ قدر انسان کو system سست محسوس کراتی ہے، جبکہ 40 یا اس سے زیادہ وہ حالت ہے جسے لوگ frozen کہتے ہیں۔

اسی لیے صرف limit مقرر کرنا ضمانت نہیں ہوتا۔ MemoryHigh= کے تحت چلنے والی unit کو ختم کرنے کے بجائے throttle کیا جاتا ہے، اس لیے وہ فعال مگر سست رہتی ہے۔ systemd کے نقطۂ نظر سے وہ کبھی failed نہیں ہوئی، اس لیے اسے restart بھی نہیں کیا جاتا۔ اگر capped unit کو swap استعمال کرنے کی اجازت ہو تو اس سے پیدا ہونے والی reads اور writes اسی unit کے ذمے شمار ہوتی ہیں، لیکن انہیں ایک shared device پورا کرتا ہے۔ اس کے نتیجے میں box کی ہر دوسری service کے لیے /proc/pressure/io کی قدر بڑھ سکتی ہے۔ Limits صرف یہ طے کرتی ہیں کہ قلت کی قیمت کون ادا کرے گا؛ وہ capacity پیدا نہیں کر سکتیں۔

اپنے VPS میں cgroup v2 کی موجودگی کی تصدیق کریں

stat -fc %T /sys/fs/cgroup

cgroup2fs متحدہ 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، v2 استعمال نہیں کرتا۔

cgroup v2 میں systemd ہر unit کے لیے memory accounting کو default طور پر فعال کر دیتا ہے، اس لیے اعداد پہلے ہی دستیاب ہوتے ہیں:

systemd-cgtop -m

یہ cgroups کو memory استعمال کے لحاظ سے ترتیب دے کر دکھاتا ہے۔ اس طرح سرور کے جواب دینے کے دوران یہ جاننے کا تیز ترین طریقہ ملتا ہے کہ "اس سرور کے وسائل کون استعمال کر رہا ہے"۔ اگر سرور نیا ہے تو نئے VPS پر پہلے دس منٹ میں account اور firewall کا کام اس مرحلے سے پہلے کیا جاتا ہے۔

MemoryHigh throttles۔ MemoryMax kills۔

ان دونوں memory settings کے درمیان فرق طے کرتا ہے کہ failure کی صورت حال کیسی ہوگی۔

  • MemoryHigh= ایک soft cap ہے۔ اس حد سے تجاوز پر kernel اس cgroup سے memory زیادہ شدت سے reclaim کرتا ہے اور جان بوجھ کر اس کی allocations سست کر دیتا ہے۔ Usage اس number سے آگے بھی جا سکتی ہے، اور کوئی process kill نہیں ہوتا۔
  • MemoryMax= ایک hard cap ہے۔ جب اس حد کے اندر allocation پوری نہ ہو سکے تو OOM killer اسی cgroup کے اندر چلتا ہے اور اس unit کے اپنے processes میں سے ایک کو kill کر دیتا ہے۔

دوسرا حصہ ہی اصل وجہ ہے کہ ایسی ہر چیز پر MemoryMax= set کرنا چاہیے جس پر آپ کو مکمل اعتماد نہ ہو۔ کسی cap کے بغیر memory shortage پورے host کا مسئلہ بن جاتی ہے، اور global OOM killer اپنا victim oom_score کے مطابق منتخب کرتا ہے، جس کا عموماً مطلب سب سے بڑا process ہوتا ہے۔ سب سے بڑا process عموماً آپ کا database ہوتا ہے، نہ کہ وہ script جس نے memory leak کی ہو۔ Cap کے ساتھ kill اسی unit کے اندر ہوتا ہے جس نے مسئلہ پیدا کیا ہو۔

دونوں settings set کریں، اور MemoryHigh= کو MemoryMax= سے تقریباً 20 سے 30 فیصد کم رکھیں۔ ان دونوں کے درمیان فرق warning zone ہے: slow leak High سے تجاوز کرتا ہے اور service سست ہو جاتی ہے، جبکہ sudden spike براہ راست Max سے آگے نکل کر service کو kill کر دیتی ہے۔

Percentage values installed physical memory کے حساب سے پڑھی جاتی ہیں۔ اس لیے 4 GB plan پر MemoryMax=25%، 1 GB بنتا ہے، اور plan resize کرنے کے بعد بھی host کی ایک چوتھائی memory ہی رہے گا۔ MemorySwapMax=0 اس unit کو swap سے مکمل طور پر باہر رکھتا ہے، جس سے طویل crawl کے بجائے تیز اور واضح kill ہوتا ہے۔

Cap کے ساتھ restart policy بھی set کریں، ورنہ kill کے بعد آپ کے پاس صرف stopped service رہ جائے گی۔

[Unit]
StartLimitIntervalSec=300
StartLimitBurst=5

[Service]
Restart=on-failure
RestartSec=5s

StartLimit* کو [Unit] میں اور Restart= کو [Service] میں رکھنا چاہیے۔ کسی ایک کو غلط section میں رکھنے پر systemd اسے ignore کر دیتا ہے۔ پانچ منٹ میں پانچ restarts کسی عارضی blip کے بجائے leak کی علامت ہیں، اس لیے اس کے بعد systemd کوشش ترک کر کے unit کو failed حالت میں چھوڑ دیتا ہے۔ بعد میں یہی وہ state ہے جسے آپ تلاش کرنا چاہتے ہیں، نہ کہ ایسا crash loop جو مسئلہ چھپا دے۔

CPUQuota کے ذریعے CPU محدود کریں، یا CPUWeight کے ذریعے CPU وقت کا حصہ مقرر کریں

CPUQuota= ایک CPU پر دستیاب وقت کے فیصد کے برابر وسائل استعمال کرتا ہے۔ CPUQuota=50% ایک core کے نصف کے برابر ہے۔ CPUQuota=200% دو cores کے برابر ہے، جنہیں unit اپنی ضرورت کے مطابق متعدد threads میں تقسیم کر سکتی ہے۔ 2 vCPU plan پر CPUQuota=200% پوری machine کے برابر ہے۔

زیادہ تر services کے لیے CPUWeight= بہتر default ہے۔ یہ 1 سے 10000 تک نسبتی حصہ مقرر کرتا ہے، جبکہ kernel کا default 100 ہے۔ اس کا اثر صرف اس وقت ہوتا ہے جب کوئی دوسرا کام بھی CPU کے لیے مقابلہ کر رہا ہو: load کے دوران CPUWeight=20 پر چلنے والا backup job، 100 پر چلنے والے web server کو ترجیح دے گا، لیکن machine idle ہونے پر دستیاب پوری capacity استعمال کر سکے گا۔ سخت quota اس idle capacity کو ضائع کر دیتا ہے۔

CPU limit سے حاصل ہونے والے فائدے کے بارے میں حقیقت پسند رہیں۔ CPU-bound process عموماً Linux کو freeze نہیں کرتا، کیونکہ scheduler سب processes کو وقت دیتا رہتا ہے۔ Machine کو بند کرنے والا اصل مسئلہ memory ہوتا ہے۔ جب آپ کو قابل پیش گوئی زیادہ سے زیادہ حد چاہیے تو CPUQuota= استعمال کریں، مثلاً کسی build یا ایسے agent کے لیے جو بصورت دیگر ایک گھنٹے تک مسلسل مکمل رفتار سے چلتا رہے۔ ایسے workload کے لیے sizing ایک الگ موضوع ہے، جس کی وضاحت coding agent VPS کے لیے کتنی RAM اور CPU درکار ہے میں کی گئی ہے۔

اگر CPU busy دکھائی دے، جبکہ آپ کے processes زیادہ کام نہ کر رہے ہوں، تو وجہ hypervisor کے دوسرے حصے میں ہو سکتی ہے۔ اسے شور کرنے والے پڑوسی سے CPU steal time کہتے ہیں، اور آپ کی مقرر کردہ کوئی quota اسے تبدیل نہیں کر سکتی۔

TasksMax ایک fork loop کو روکتا ہے

TasksMax= ان processes اور threads کی تعداد ہے جو ایک unit کے پاس ہو سکتی ہے۔ Threads بھی شمار ہوتے ہیں، اس لیے Java یا Go service کو process list سے ظاہر ہونے والی تعداد کے مقابلے میں زیادہ گنجائش درکار ہوتی ہے۔ یہ کسی ایسے script کے خلاف سب سے کم لاگت والا تحفظ ہے جو مسلسل loop میں fork کرتا ہے، کیونکہ fork اسی unit کے اندر ناکام ہوتا ہے اور پورا server process IDs ختم ہونے کی وجہ سے متاثر نہیں ہوتا۔

TasksMax=128

جب کوئی unit اس limit تک پہنچتی ہے تو kernel ایک ایسی line log کرتا ہے جس میں cgroup کا نام شامل ہوتا ہے:

cgroup: fork rejected by pids controller in /system.slice/myapp.service

خود program عموماً fork: retry: Resource temporarily unavailable report کرتا ہے۔ systemctl show -p DefaultTasksMax کے ذریعے چیک کریں کہ manager default طور پر کیا لاگو کرتا ہے۔

systemd-run کے ساتھ ایک وقتی job محدود کریں

اس کے لیے 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 کے بعد کام کرتی ہے۔

طویل job کے لیے --scope ہٹا دیں اور اسے ایک نام دیں۔ اس کے بعد یہ عارضی service کے طور پر background میں چلتی ہے اور journal میں log لکھتی ہے:

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 وہاں reject ہو سکتی ہے۔ ایسا ہونے پر اسے sudo کے ساتھ چلائیں۔ جب کسی job کو مستقل جگہ مل جائے تو 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 رہتی ہے، جبکہ مسلسل disk thrashing کے دوران ایسا نہیں ہوتا۔

swapon --show
free -h

چھوٹے VPS پر ایک قابلِ عمل درمیانی حل یہ ہے کہ ایک محدود swap file رکھیں، تاکہ وہ pages اس میں منتقل ہو سکیں جو ایک بار allocate ہوتے ہیں اور پھر دوبارہ استعمال نہیں ہوتے۔ جن units کو ضرورت پڑنے پر ختم ہونے دینا قابلِ قبول ہو، ان پر MemorySwapMax=0 set کریں۔ اہم services اپنی swap برقرار رکھیں۔ غیر متوقع services جلد حد تک پہنچیں اور restart ہو جائیں۔

vm.swappiness کی قدر کم کرنا ایک کمزور طریقہ ہے، اور اس کی وجہ سمجھنا مفید ہے۔ یہ صرف page cache کو evict کرنے اور anonymous pages کو swap کرنے کے درمیان توازن بدلتا ہے، اور دونوں صورتوں میں بعد میں disk read درکار ہوتی ہے۔ یہ صرف اس بات کو بدلتا ہے کہ کون سے pages thrash کریں گے، یہ نہیں بدلتا کہ system thrash کرے گا یا نہیں۔

ابتدائی OOM daemon، تعطل شروع ہونے سے پہلے process ختم کرتا ہے

Kernel کے reclaim کے مکمل طور پر ناکام ہونے تک انتظار کرنے سے مسئلہ پیدا ہوتا ہے۔ چھوٹے VPS پر یہی انتظار وہ وقت ہوتا ہے جس میں آپ machine سے کنکشن کھو دیتے ہیں۔ دو userspace daemons خود memory کو monitor کر کے یہ وقفہ کم کرتے ہیں اور process کو پہلے ختم کر دیتے ہیں۔

earlyoom دستیاب memory اور free swap کو monitor کرتا ہے۔ ان میں سے کوئی بھی value threshold سے کم ہونے پر یہ سب سے زیادہ score والے process کو ختم کر دیتا ہے۔

sudo apt install earlyoom
systemctl status earlyoom

Debian اور Ubuntu کا package install کے وقت service شروع کر دیتا ہے۔ اس کے options /etc/default/earlyoom میں موجود ہیں:

EARLYOOM_ARGS="-m 5,2 -s 5,2 --avoid '^(sshd|systemd)$' --prefer '^(node|python3)$'"

-m PERCENT دستیاب memory کی کم از کم مقدار اور -s PERCENT free swap کی کم از کم مقدار مقرر کرتا ہے۔ دونوں کی default value 10 percent ہے۔ ہر pair میں دوسرا number SIGKILL point ہوتا ہے۔ پہلے value سے کم ہونے پر earlyoom SIGTERM بھیجتا ہے، پھر دوسرے value سے کم ہونے پر SIGKILL بھیجتا ہے۔ دوسرے value کی default setting پہلے value کی نصف ہوتی ہے۔ تبدیلی sudo systemctl restart earlyoom سے نافذ کریں، اور journalctl -u earlyoom پڑھ کر دیکھیں کہ اس نے کون سا process ختم کیا اور اس process کے پاس کتنی memory تھی۔

systemd-oomd دوسرا option ہے۔ اس کے manual page میں اسے یوں بیان کیا گیا ہے: "ایک system service جو cgroups-v2 اور pressure stall information (PSI) استعمال کر کے kernel space میں OOM واقع ہونے سے پہلے monitor کرتی اور اصلاحی کارروائی کرتی ہے"۔ یہ single processes کے بجائے پورے cgroups پر کارروائی کرتا ہے، اس لیے یہ کسی stray child کے بجائے ایک unit کو ختم کرتا ہے۔ Units ManagedOOMMemoryPressure=kill یا ManagedOOMSwap=kill کے ذریعے opt in کرتی ہیں، اور thresholds /etc/systemd/oomd.conf میں موجود ہیں۔

systemctl status systemd-oomd
oomctl

oomctl اس وقت monitor کیے جانے والے objects دکھاتا ہے۔ Server image پر یہ اکثر کچھ بھی نہیں دکھاتا، کیونکہ setting ہر unit کے لیے opt-in ہوتی ہے۔ صرف ایک daemon منتخب کریں اور اسی تک محدود رہیں۔ دونوں چلانے سے victim منتخب کرنے کے لیے دو mechanisms میں race پیدا ہوتی ہے، اور کسی kill کی وجہ معلوم کرنا زیادہ مشکل ہو جاتا ہے۔

کون سا unit ذمہ دار تھا؟

Kernel سے شروع کریں، کیونکہ یہ اپنے کیے گئے ہر kill کا record درج کرتا ہے۔

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:0

anon-rss وہ memory ہے جو process کے ختم ہونے کے وقت RAM میں موجود تھی؛ یہاں یہ تقریباً 1.8 GB ہے۔ مربع brackets میں موجود نام کو احتیاط سے پڑھیں۔ یہ وہ victim ہے جسے kernel نے منتخب کیا، اور kernel عموماً سب سے بڑا process منتخب کرتا ہے۔ یہ ہمیشہ وہ process نہیں ہوتا جس کی وجہ سے memory shortage پیدا ہوئی۔

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 50
myapp.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 record کرتا ہے۔ Throttling کبھی بھی log line پیدا نہیں کرتی:

cat /sys/fs/cgroup/system.slice/myapp.service/memory.events
cat /sys/fs/cgroup/system.slice/myapp.service/memory.peak
low 0
high 4213
max 118
oom 12
oom_kill 12

high یہ گنتا ہے کہ unit کتنی بار MemoryHigh= سے تجاوز کر کے throttled ہوئی۔ max یہ گنتا ہے کہ unit کتنی بار hard cap تک پہنچی، جبکہ oom_kill ان processes کی تعداد بتاتا ہے جنہیں واقعی kill کیا گیا۔ high کی بڑی قدر اور oom_kill 0 خاموش صورتِ حال کی نشاندہی کرتے ہیں: service چل رہی ہے، اس کی رفتار بہت کم ہو گئی ہے، اور اس نے کسی کو failure report نہیں کیا۔ memory.peak (Linux 5.19 اور اس کے بعد کے versions میں) وہ زیادہ سے زیادہ usage محفوظ رکھتا ہے جس تک cgroup پہنچی۔ اسی number کی بنیاد پر MemoryMax= کا size مقرر کریں۔ Unit restart ہونے پر دونوں files reset ہو جاتی ہیں، کیونکہ systemd cgroup دوبارہ بناتا ہے۔

ان سب کے لیے ایک prerequisite ضروری ہے۔ اگر /var/log/journal موجود نہ ہو تو journal RAM میں رہتا ہے، اور اس reboot کے بعد ہر line ختم ہو جاتی ہے جس کی مدد سے آپ system کو recover کرنا چاہتے تھے۔

sudo mkdir -p /var/log/journal
sudo systemd-tmpfiles --create --prefix /var/log/journal
sudo systemctl restart systemd-journald
journalctl --list-boots

journalctl --list-boots کا current boot سے زیادہ ہونا بتاتا ہے کہ history اب محفوظ رہتی ہے، اس لیے journalctl -k -b -1 آپ کو اس boot کے kernel messages دکھا سکتا ہے جو crash ہو گیا تھا۔

چھوٹے VPS کے لیے نقطۂ آغاز

2 GB پلان پر kernel اور page cache کے لیے 300 سے 400 MB مختص رکھیں، اور limits کا مجموعہ مکمل 2 GB تک نہ پہنچنے دیں، کیونکہ ہر unit بیک وقت peak پر جا سکتی ہے۔ اہم سروس کو سب سے بڑا حصہ دیں، پھر اس کے گرد موجود غیر یقینی workloads پر limits مقرر کریں۔

[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 منتخب کرنے کا امکان بہت کم ہو جاتا ہے۔ یہی فرق server کو درست کرنے اور control panel سے reboot کرنے کے درمیان ہو سکتا ہے۔ یہ صرف kernel کے victim منتخب کرنے کے عمل کو تبدیل کرتا ہے۔ اس سے stall کا دورانیہ کم نہیں ہوتا۔

Containers اپنے الگ cgroups میں چلتے ہیں۔ یہ cgroups آپ کی unit files کے بجائے container runtime بناتا ہے۔ اس لیے docker.service پر مقرر کردہ limit کسی ایک container پر لاگو نہیں ہوتی۔ MemoryMax= اور CPUQuota= کے per-container متبادل Docker Compose میں memory اور CPU limits مقرر کرنا میں بیان کیے گئے ہیں۔

FAQ

میرا VPS بے قابو process کو ختم کرنے کے بجائے freeze کیوں ہو گیا؟

کیونکہ kernel پیش رفت کا فیصلہ اس بنیاد پر کرتا ہے کہ memory reclaim سے pages واپس مل رہے ہیں یا نہیں، نہ کہ اس عمل میں کتنا وقت لگ رہا ہے۔ memory کم ہونے پر یہ running programs کے executable pages سمیت page cache کو خارج کرتا ہے، پھر اگلی instruction پر انہیں دوبارہ پڑھتا ہے۔ ہر چیز storage پر انتظار کرتی رہتی ہے اور کوئی allocation باضابطہ طور پر ناکام نہیں ہوتی، اس لیے OOM killer کبھی نہیں چلتا۔ یہ صورت حال جاری ہو تو /proc/pressure/memory چیک کریں: full avg10 کا 40 سے زیادہ ہونا بتاتا ہے کہ گزشتہ دس seconds میں تقریباً کوئی task چل ہی نہیں سکا۔ earlyoom جیسا userspace daemon server کے اس حالت تک پہنچنے سے پہلے process ختم کر دیتا ہے۔

MemoryHigh اور MemoryMax میں کیا فرق ہے؟

MemoryHigh= ایک soft cap ہے جو process کو throttle کرتا ہے۔ kernel unit سے memory سختی سے reclaim کرتا ہے اور اس کی allocations سست کر دیتا ہے، لیکن usage اس number سے بڑھ سکتی ہے اور کچھ بھی ختم نہیں کیا جاتا۔ MemoryMax= ایک hard cap ہے: اگر اس حد کے اندر allocation پوری نہ ہو سکے تو اسی unit کے اپنے cgroup میں OOM killer چلتا ہے۔ یوں مسئلہ پیدا کرنے والا 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 ہوتے ہیں اور پھر دوبارہ استعمال نہیں ہوتے۔ یہ بے قابو process کے لیے مددگار نہیں ہے۔ اس سے process ختم ہونے میں تاخیر ہوتی ہے اور مختصر outage کے بجائے طویل stall پیدا ہوتا ہے، جس کے دوران آپ login کر کے مسئلہ درست نہیں کر سکتے۔ swap کو محدود رکھیں، اور جن units کو ختم ہونے دینا قابل قبول ہو ان پر MemorySwapMax=0 مقرر کریں۔ اس طرح وہ اپنی ceiling تک پہنچ کر تیزی سے restart ہو جائیں گی، جبکہ اہم services اپنی swap برقرار رکھیں گی۔

کیا unit file لکھے بغیر کسی command کو محدود کیا جا سکتا ہے؟

ہاں۔ sudo systemd-run --scope -p MemoryMax=1G -p CPUQuota=50% ./script.sh آپ کے terminal میں command کو ان limits کے ساتھ transient scope کے اندر چلاتا ہے، اور command ختم ہوتے ہی یہ limits ختم ہو جاتی ہیں۔ systemd.resource-control کی ہر property -p کے بعد دستیاب ہوتی ہے، اس لیے MemorySwapMax=، TasksMax= اور CPUWeight= بھی وہاں کام کرتے ہیں۔ --scope ہٹا کر --unit=name شامل کریں تاکہ job background میں چلے اور اس کا output journal میں محفوظ ہو۔