সরাসরি পর্বে যান
পর্ব ১১ · ৮১-এর মধ্যে · মডিউল ২

Service চালানো — process, systemd, journalctl আর resource

Running Services — Processes, systemd, journalctl and Resources

Taught in Bangla, this lesson follows a server process from PID 1 and SIGTERM to graceful shutdown, shows why nohup is not a service, and writes a systemd unit file used with systemctl and journalctl. It works through restart policy and crash loop arithmetic, cgroup CPU and memory limits, sandboxing settings, load average, memory pressure, the OOM killer and a first investigation with the USE method.

এই পর্বে যা শিখবেন

  • Server-এ একটি process-এর জীবন, PID 1 আর SIGTERM থেকে graceful shutdown
  • নিজে একটি unit file লেখা এবং systemctl আর journalctl দিয়ে চালানো ও খোঁজা
  • Restart policy, crash loop আর start limit-এর হিসাব, সংখ্যা ধরে
  • cgroup দিয়ে CPU ও memory সীমা, আর sandboxing-এর কয়েকটি setting
  • Load average, memory pressure, OOM kill পড়া এবং USE method দিয়ে প্রথম তদন্ত

১ · Server-এ process-এর জীবন — PID 1, signal আর ভদ্রভাবে থামা

আগের পর্বগুলোতে আপনি Linux-এ ঘোরাফেরা, shell আর script শিখেছেন। এবার আসল কাজ: একটি কাল্পনিক অনলাইন বাজার অ্যাপের API, bazar-api, একটি server-এ দিনরাত চালানো। এর আগে এক লাইনে মনে করিয়ে দিই। Process হলো চলমান প্রোগ্রাম; ভেতরের তত্ত্ব আছে Operating Systems কোর্সের প্রসেস, fork, exec আর wait আর Signal পর্বে। এখানে শুধু operations-এর দিকটি দেখব।

Linux boot-এর শেষে kernel একটিমাত্র user-space process চালায়। তার নম্বর, মানে PID, হয় 1. আধুনিক বেশিরভাগ distribution-এ PID 1 হলো systemd. বাকি সব process এরই বংশধর। কোনো process-এর বাবা আগে মরে গেলে অনাথ সন্তানকেও PID 1 দত্তক নেয়, আর শেষে তার exit status তুলে নেয়। পুরো boot-এর গল্প আছে বুট প্রক্রিয়া পর্বে।

একটি service-এর জীবনে তিনটি মুহূর্ত গুরুত্বপূর্ণ: কে তাকে চালাল, কীভাবে সে মারা গেল, আর তারপর কী হলো। মৃত্যুর পথ দুটি: নিজে শেষ হওয়া (exit), অথবা signal পাওয়া। Server-এ দুটি signal প্রতিদিনের সঙ্গী।

রাত দশটায় একটি শপিং মলের দোকান বন্ধ হচ্ছে। ম্যানেজার বললেন, “নতুন ক্রেতা আর নয়। যাঁরা ভেতরে আছেন তাঁদের বিল শেষ করুন, ক্যাশ মিলিয়ে তালা দিন।” এটি SIGTERM. আর হঠাৎ মেইন সুইচ কেটে শাটার নামিয়ে দেওয়া হলো SIGKILL. দ্বিতীয়টিতে দোকান নিশ্চিতভাবে বন্ধ হয়, কিন্তু অর্ধেক লেখা রশিদ আর না মেলানো ক্যাশ পড়ে থাকে।

দিকSIGTERM (15)SIGKILL (9)
Process কি ধরতে পারে?হ্যাঁ, handler লেখা যায়, উপেক্ষাও করা যায়না, ধরা বা উপেক্ষা কোনোটিই যায় না
গুছিয়ে নেওয়ার সুযোগআছেনেই
Handler না থাকলে শেষ হওয়ার পর shell-এ status143 (128 + 15)137 (128 + 9)
কে সাধারণত পাঠায়systemctl stop, kill, Kubernetes, reboot-এর সময় systemdসময়সীমা শেষে systemd, OOM killer, হতাশ মানুষ

Graceful shutdown মানে SIGTERM পাওয়ার পর একটি নির্দিষ্ট ক্রম: নতুন কাজ নেওয়া বন্ধ, হাতের কাজ শেষ, buffer-এর data লেখা, database connection বন্ধ, তারপর exit 0. আর systemd বা অন্য manager অনন্তকাল অপেক্ষা করে না: সময়সীমা পেরোলে SIGKILL পাঠায়। systemd-তে এই সীমা TimeoutStopSec, ডিফল্ট সাধারণত ৯০ সেকেন্ড।

নিচের Bash block সেই আচরণটি ছোট আকারে দেখায়। পেছনে চলা worker-এর সারিতে পাঁচটি কাজ আছে। stop_unit ফাংশন systemd-র ভূমিকায়: আগে SIGTERM, সময় পেরোলে SIGKILL. কাজ ১০৩ চলার সময় signal আসে। দ্বিতীয় ঘটনায় একটি জেদি process SIGTERM উপেক্ষা করে।

tmp=$(mktemp -d)
worker() {                        # a fake service with 5 queued jobs
  local stop=0
  trap 'stop=1; echo "  worker: SIGTERM, no new job after this one"' TERM
  for job in 101 102 103 104 105; do
    [ "$stop" = 1 ] && break
    echo "  worker: start job $job"
    if [ "$job" = 103 ]; then     # the signal arrives during job 103
      touch "$tmp/busy"
      until [ -e "$tmp/sent" ]; do sleep 0.01; done
    fi
    echo "  worker: done  job $job"
  done
  echo "  worker: state saved, exit 0"; exit 0
}
stubborn() { trap '' TERM; touch "$tmp/busy"; while :; do sleep 0.01; done; }

stop_unit() {                     # $1 = job pid, $2 = grace in 0.01 s ticks
  until [ -e "$tmp/busy" ]; do sleep 0.01; done
  echo "manager: send SIGTERM"
  kill -TERM "$1"; touch "$tmp/sent"
  for ((i = 0; i < $2; i++)); do
    kill -0 "$1" 2>/dev/null || break
    sleep 0.01
  done
  if kill -0 "$1" 2>/dev/null; then
    echo "manager: grace over, send SIGKILL"; kill -KILL "$1"
  fi
  wait "$1" 2>/dev/null; echo "manager: exit status $?"
  rm -f "$tmp/busy" "$tmp/sent"
}
echo "== case 1: handles SIGTERM =="; worker &   stop_unit $! 500
echo "== case 2: ignores SIGTERM =="; stubborn & stop_unit $! 30
rm -rf "$tmp"

আউটপুট:

== case 1: handles SIGTERM ==
  worker: start job 101
  worker: done  job 101
  worker: start job 102
  worker: done  job 102
  worker: start job 103
manager: send SIGTERM
  worker: SIGTERM, no new job after this one
  worker: done  job 103
  worker: state saved, exit 0
manager: exit status 0
== case 2: ignores SIGTERM ==
manager: send SIGTERM
manager: grace over, send SIGKILL
manager: exit status 137

প্রথম ঘটনায় কাজ ১০৩ অর্ধেক অবস্থায় মরেনি, শেষ হয়েছে; ১০৪ আর ১০৫ সারিতেই রয়ে গেছে, পরের process নেবে। Status 0, মানে পরিষ্কার বিদায়। দ্বিতীয় ঘটনায় SIGTERM কোনো কাজে আসেনি, তাই সময় শেষে SIGKILL, আর status 137. Log-এ 137 দেখলেই ভাবুন: কেউ জোর করে মেরেছে। একই সংকেত memory সীমাতেও আসে, যা পরে দেখব। block-টি কোনো sleep-এর ভাগ্যে নির্ভর করে না, ফাইল দিয়ে দুই পক্ষ অপেক্ষা মেলায়, তাই আউটপুট প্রতিবার এক।

Container-এ একটি ফাঁদ আছে। Container-এর ভেতরে আপনার অ্যাপই প্রায়ই PID 1, আর kernel PID 1-কে এমন signal দেয় না যার handler সে লেখেনি। তাই handler ছাড়া অ্যাপ SIGTERM-এ থামে না, সময়সীমা শেষে SIGKILL খায়। বিস্তারিত পর্ব ৩৯ (Container চালানো) আর পর্ব ৪৩ (Pod).

২ · nohup বনাম service — কেন একজন ম্যানেজার দরকার

নতুন অনেকে SSH করে server-এ ঢুকে এভাবে অ্যাপ চালান। শব্দটি মনে রাখুন: nohup মানে “no hangup”. Terminal বন্ধ হলে process-কে SIGHUP পাঠানো হয়; nohup সেটি উপেক্ষা করতে বলে আর আউটপুট nohup.out-এ পাঠায়।

# works today, breaks on the first crash or reboot
nohup node server.js &

এটি চলে। রাত তিনটায় একটি unhandled exception হলো, process শেষ। কেউ জানে না। সকালে ক্রেতারা অভিযোগ করবেন। Cloud provider server restart করলেও একই দশা। এই ভঙ্গুরতাই service-এর সংজ্ঞা তৈরি করে: terminal ছাড়া পেছনে দীর্ঘ সময় চলা process-কে বলে daemon, আর তাকে একজন manager-এর হাতে তুলে দেওয়া হলো service হিসেবে চালানো।

nohup হলো দোকান খোলা রেখে কর্মচারীকে বলে চলে যাওয়া। তিনি অসুস্থ হলে দোকান বন্ধ, মালিক জানেন না। systemd হলো একজন সার্বক্ষণিক ম্যানেজার, যাঁর হাতে প্রতিটি কর্মচারীর চুক্তিপত্র আছে: কে কখন আসবেন, অসুস্থ হলে কতবার বদলি ডাকা হবে, কতটুকু রসদ পাবেন, খাতায় কী লিখবেন।

প্রশ্নnohup … &systemd service
Crash হলেশেষ, কেউ জানে নাRestart= অনুযায়ী আবার চলে
Reboot-এর পরফিরে আসে নাenable করা থাকলে নিজে চলে
Logএকটি বাড়তে থাকা ফাইলjournal-এ, সময় ও unit ধরে খোঁজা যায়
কোন userযিনি কমান্ড দিলেননির্দিষ্ট, কম ক্ষমতার user
Resource সীমানেইcgroup দিয়ে CPU, memory সীমা
থামানোPID খুঁজে হাতে killsystemctl stop, পুরো দলকে ক্রম মেনে

systemd যা সামলায় তার প্রতিটিকে বলে unit. Unit-এর ধরন ফাইলের শেষাংশে লেখা থাকে: .service (process), .timer (সময়সূচি, পর্ব ১০-এ দেখেছেন), .socket, .mount, আর .target. Target হলো কয়েকটি unit-এর দল; multi-user.target মোটামুটি বলে “server সাধারণ কাজের জন্য তৈরি”।

একটি unit যেকোনো সময় কোনো একটি অবস্থায় থাকে। নিচের ছবিটি মাথায় গেঁথে নিন, কারণ systemctl status-এর Active: লাইনে ঠিক এই শব্দগুলোই আসে।

একটি service unit-এর অবস্থাচক্র

Unit lifecycle: inactive, activating, active, deactivating, failed

Service unit-এর পাঁচটি অবস্থা আর তাদের মধ্যের পথ inactive activating active deactivating failed থেমে আছে চালু হচ্ছে চলছে থামছে ব্যর্থ start stop ready থামা শেষ crash reset-failed Restart =on-failure
ছবি ১১.১ — ডটেড পথটি auto-restart: RestartSec অপেক্ষার সময় status-এ activating (auto-restart) দেখায়। Activating অবস্থা থেকেও failed হতে পারে। আর enabled বা disabled এই চক্রের অংশ নয়; সেটি আলাদা প্রশ্ন, boot-এ চলবে কি না।

আগে Linux-এ service চলত SysV init-এর shell script দিয়ে, ক্রম ধরে একটির পর একটি। Ubuntu ২০০৬ সালে Upstart আনে। ২০১০ সালে Lennart Poettering আর Kay Sievers শুরু করেন systemd, যার ওপর Apple-এর launchd-এর প্রভাব ছিল বলে Poettering নিজেই লিখেছেন। RHEL 7 (২০১৪), Debian 8 আর Ubuntu 15.04 (২০১৫) একে ডিফল্ট করার পর এটি প্রধান ধারা হয়ে ওঠে। তবে Alpine-এর মতো কিছু distribution এখনো অন্য init ব্যবহার করে।

৩ · Unit file লেখা — service-এর চুক্তিপত্র

Unit file একটি ছোট লেখা ফাইল, রান্নাঘরের রেসিপি খাতার পাতার মতো: কী লাগবে, কোন ক্রমে, ব্যর্থ হলে কী। নিজের লেখা unit রাখা হয় /etc/systemd/system/-এ; package-এর দেওয়া unit থাকে সাধারণত /usr/lib/systemd/system/-এ, আর একই নাম হলে /etc-এরটি জেতে। নিচের নমুনাটি runner-এ চলবে না, কারণ runner-এ systemd নেই; এটি পড়ার জন্য।

# /etc/systemd/system/bazar-api.service
[Unit]
Description=Bazar API (fictional shop app)
Wants=network-online.target
After=network-online.target
StartLimitIntervalSec=300
StartLimitBurst=5

[Service]
Type=simple
User=bazar
Group=bazar
WorkingDirectory=/opt/bazar-api
Environment=PORT=8080 NODE_ENV=production
EnvironmentFile=/etc/bazar-api/env
ExecStart=/usr/bin/node /opt/bazar-api/server.js
Restart=on-failure
RestartSec=5
TimeoutStopSec=30

[Install]
WantedBy=multi-user.target
Settingকী করেযা ভুল হয়
ExecStartযে কমান্ড চলবে। পুরো path লিখুনএটি shell নয়, তাই &, > বা pipe কাজ করে না
Typeকখন “চালু হয়েছে” ধরা হবে। simple মানে process শুরু হলেই; notify মানে অ্যাপ নিজে জানালেনিজে background-এ যাওয়া অ্যাপ simple দিয়ে চালালে systemd তাকে মৃত ভাবে
User, Groupকোন পরিচয়ে চলবে। না দিলে system service চলে root হিসেবেঅকারণে root রাখা; এটি least privilege-এর উল্টো
Environment, EnvironmentFileEnvironment variable বসায়Unit file-এ password লেখা। ফাইলটি সবাই পড়তে পারে। Secret রাখুন কড়া অনুমতির আলাদা ফাইলে (পর্ব ৩৩)
Restart, RestartSecকখন আবার চলবে, কত পরে। ডিফল্ট Restart=no, RestartSec ১০০ মিলিসেকেন্ডRestart না লিখলে crash-এ service পড়েই থাকে
Afterশুধু ক্রম: ওটি চালু হলে তার পরে শুরু হবেভাবা যে এটি ওটিকে চালুও করবে
Wantsনির্ভরতা: ওটিকেও চালু করতে চাই, ব্যর্থ হলেও আমি চলবWants একা ক্রম ঠিক করে না, তাই সাধারণত After-সহ লেখা হয়
WantedBy[Install]-এর অংশ: enable করলে কোন target-এর সাথে boot-এ চলবেএটি না থাকলে enable কিছুই করে না

এবার দেখা যাক systemctl start দিলে ফাইল থেকে চলন্ত process পর্যন্ত আসলে কী কী ঘটে। “পরে” চাপুন।

  1. কমান্ডsystemctlPID 1-কে অনুরোধ
  2. Joblimit, orderনিয়ম যাচাই
  3. cgroupmemory.maxআলাদা ঘর
  4. fork, execUser=bazarনতুন process
  5. Typesimpleকখন “চালু”
  6. activerunningjournal-এ log
Terminalঅনুরোধ যায় systemd-র কাছে
$ sudo systemctl start bazar-api
# systemctl নিজে অ্যাপ চালায় না; সে PID 1-কে বলে
systemd-র মাথায়ফাইল নয়, memory-তে থাকা কপি
unit      : bazar-api.service  (already loaded)
limit     : 0 of 5 starts in window, allowed
Wants     : network-online.target
After     : wait until that target is done
job       : start, queued
cgroupservice-এর জন্য আলাদা ঘর
/sys/fs/cgroup/system.slice/bazar-api.service/
# সীমা লেখা থাকলে এখানে বসে (অংশ ৬ দেখুন)
memory.max   536870912
cpu.max      150000 100000
নতুন processexec-এর আগে পরিবেশ সাজানো
fork()                # systemd-র একটি কপি
set cgroup, sandbox, env, cwd
setuid(bazar)         # root থেকে নেমে আসা
exec /usr/bin/node /opt/bazar-api/server.js
Type=কখন “started” ধরা হবে
simple : process শুরু হলেই      # port খোলার আগেই!
exec   : exec সফল হলে
notify : অ্যাপ READY=1 পাঠালে   # সবচেয়ে সৎ
নমুনা আউটপুটversion-ভেদে আলাদা হতে পারে
$ systemctl status bazar-api
Active: active (running) since Fri 2026-10-30 14:02:11 +06
   Main PID: 2417 (node)
     CGroup: /system.slice/bazar-api.service
             └─2417 /usr/bin/node /opt/bazar-api/server.js
  1. অনুরোধ। systemctl শুধু একটি দূত। সে D-Bus-এর মতো পথে PID 1-কে বলে “এই unit চালান”। তাই অ্যাপটি আপনার shell-এর সন্তান হয় না, আর SSH কেটে গেলেও এর কিছু হয় না।
  2. Job আর যাচাই। systemd তার memory-তে লোড করা unit দেখে, ডিস্কের ফাইল নয়। এজন্যই ফাইল বদলে daemon-reload ছাড়া পুরোনো নিয়ম চলে। Start limit আর After-এর ক্রমও এখানে দেখা হয়; ৫-এর মধ্যে ০ বার হয়েছে, তাই অনুমতি মিলল।
  3. cgroup তৈরি। Service-এর নামে একটি cgroup হয়। এর পর থেকে অ্যাপের সব সন্তান process একই ঘরে থাকবে, তাই systemd কাউকে হারায় না। পাশের সংখ্যা দুটি অংশ ৬-এর MemoryMax=512M আর CPUQuota=150%-এর ফল।
  4. fork আর exec. systemd নিজের একটি কপি বানায়। কপিটি cgroup, sandbox, environment আর working directory ঠিক করে, bazar user হয়, তারপর exec করে node হয়ে যায়।
  5. Type সিদ্ধান্ত নেয়। Type=simple-এ process শুরু হলেই systemd বলে “চালু”, যদিও অ্যাপ হয়তো তখনো port খোলেনি। অন্য service যদি এর ওপর নির্ভর করে, তার জন্য notify বেশি সৎ, তবে অ্যাপকে সেটি পাঠাতে হয়।
  6. চলছে। অবস্থা active (running), PID 2417. অ্যাপের stdout আর stderr নিজে থেকেই journal-এ যাচ্ছে। লক্ষ করুন, “active” মানে process বেঁচে আছে, অ্যাপ সুস্থ এমন প্রমাণ নয়। সেজন্য আলাদা health check লাগে।

৪ · systemctl আর journalctl — চালানো আর খোঁজা

systemd-কে কমান্ড দেওয়ার টুল হলো systemctl. প্রতিদিনের কমান্ড অল্প কয়েকটি। সবচেয়ে বেশি গুলিয়ে যায় তিনটি জোড়া: start বনাম enable, restart বনাম reload, আর reload বনাম daemon-reload.

কমান্ডযা বদলায়যা বদলায় না
start, stopএখনকার অবস্থাboot-এর সিদ্ধান্ত
enable, disableboot-এর সিদ্ধান্ত (multi-user.target.wants/-এ symlink)এখনকার অবস্থা
enable --nowদুটোই—
restartprocess থামিয়ে আবার চালায়, নতুন PIDডিস্কের unit file পড়ে না
reloadঅ্যাপকে config আবার পড়তে বলে (ExecReload থাকলে), PID একইunit file পড়ে না
daemon-reloadsystemd unit file আবার পড়েচলন্ত service-এ কিছু হয় না
reset-failedfailed অবস্থা আর start limit-এর হিসাব মোছেমূল সমস্যা

তাই unit file বদলানোর পর ক্রমটি হলো: ফাইল সম্পাদনা, daemon-reload, restart, যাচাই। অন্যের unit বদলাতে মূল ফাইলে হাত না দিয়ে systemctl edit ব্যবহার করুন; এটি একটি drop-in override বানায়, যা package আপডেটে হারায় না।

sudo systemctl daemon-reload              # read the new file
sudo systemctl enable --now bazar-api     # boot + right now
systemctl status bazar-api                # read Loaded and Active
systemctl is-active bazar-api             # prints: active
systemctl is-enabled bazar-api            # prints: enabled
systemctl cat bazar-api                   # file plus drop-ins, as loaded

status-এর এই দুটি লাইন আগে পড়ুন। Loaded বলে boot-এর সিদ্ধান্ত (enabled), Active বলে এই মুহূর্তের অবস্থা। নিচেরটি নমুনা আউটপুট, version-ভেদে আলাদা হতে পারে।

● bazar-api.service - Bazar API (fictional shop app)
     Loaded: loaded (/etc/systemd/system/bazar-api.service; enabled; preset: enabled)
     Active: active (running) since Fri 2026-10-30 14:02:11 +06; 3h 12min ago
   Main PID: 2417 (node)
      Tasks: 11 (limit: 512)
     Memory: 186.4M (high: 450.0M max: 512.0M available: 263.6M)
        CPU: 2min 41.302s
     CGroup: /system.slice/bazar-api.service
             └─2417 /usr/bin/node /opt/bazar-api/server.js

journal থেকে ঠিক লাইনটি বের করা

Service-এর stdout আর stderr journald-এ জমা হয়। journalctl হলো সেই খাতা পড়ার যন্ত্র। পর্ব ৯-এর grep-এর আগে এর নিজের ছাঁকনি ব্যবহার করুন; উত্তর দ্রুত আসে।

journalctl -u bazar-api -n 50 --no-pager      # last 50 lines of one unit
journalctl -u bazar-api -f                    # follow live
journalctl -u bazar-api --since "2026-10-30 14:00" --until "2026-10-30 14:30"
journalctl -u bazar-api -b                    # this boot only
journalctl -b -1 -p err                       # previous boot, err and worse
journalctl -k -g "out of memory"              # kernel log, search text
journalctl -u bazar-api -o json-pretty -n 1   # every field of one entry
journalctl --disk-usage                       # how much the journal holds

দুটি ফাঁদ। এক, -p err journal-এর priority দেখে, লেখার শব্দ নয়। অ্যাপ console.log("ERROR ...") লিখলে journal সেটিকে সাধারণত info ধরে, তাই -p err-এ আসে না; তখন -g ERROR লাগবে। দুই, -b -1 কাজ করতে journal ডিস্কে থাকা চাই। কিছু distribution-এ ডিফল্টে /run-এ থাকে এবং reboot-এ হারায়; /var/log/journal আছে কি না দেখে নিন।

৫ · Restart policy আর crash loop — কখন থামতে হবে

Restart policy বলে process থামলে কী হবে। Restart=-এর প্রধান মান:

মানকখন আবার চলেকোথায় মানায়
no (ডিফল্ট)কখনো নয়একবারের কাজ
on-failurenon-zero exit, অপরিষ্কার signal, timeout, watchdogবেশিরভাগ API, worker
on-abnormalsignal, timeout, watchdog; non-zero exit-এ নয়যে অ্যাপ ইচ্ছা করে non-zero দিয়ে থামে
alwaysযেভাবেই থামুক (হাতে systemctl stop ছাড়া)যা কখনোই থামার কথা নয়, যেমন agent

এখানে “পরিষ্কার” মানে exit 0, অথবা SIGTERM, SIGHUP, SIGINT, SIGPIPE দিয়ে শেষ হওয়া। কিন্তু কোড যদি প্রতিবার ভেঙে পড়ে? ভুল config, ভাঙা database, ফুরিয়ে যাওয়া secret — এসবে restart কিছুই ঠিক করে না। এই চক্রের নাম crash loop.

গাড়ি স্টার্ট না নিলে চালক বারবার চাবি ঘোরান। দু-তিনবারে সাড়া দিলে ভালো। কিন্তু দশবার ঘোরালে ব্যাটারি শেষ, গাড়ি তবু চলে না। ভালো চালক একটা সীমায় থেমে মিস্ত্রি ডাকেন। Start limit ঠিক সেই সীমা।

নিয়মটি হলো: StartLimitIntervalSec সময়ের মধ্যে StartLimitBurst-এর বেশি start হবে না। ডিফল্ট সাধারণত ১০ সেকেন্ডে ৫ বার। দুটিই [Unit] section-এ বসে, এবং নিয়মটি হাতের systemctl start-কেও গোনে। এবার সবচেয়ে কাজের হিসাবটি। ধরুন process চালু হওয়ার u সেকেন্ড পরে ভেঙে পড়ে, আর r হলো RestartSec. তাহলে এক চক্র u + r. সীমা আটকাতে পারবে কেবল তখনই, যখন:

burst × (uptime + RestartSec) < StartLimitIntervalSec
না হলে window ফুরিয়ে গণনা শূন্য হয়, crash loop চলতেই থাকে

নিচের simulator চারটি ঘটনা এক ঘণ্টার কাল্পনিক ঘড়িতে চালায়। সব সময় মিলিসেকেন্ডে, কোনো আসল ঘড়ি বা random নেই।

// systemd Restart= with a start limit, on a simulated clock (milliseconds).
// Fixed window, like systemd's rate limiter: the count resets once the
// window has passed. A crash happens "uptimeMs" after every start.
function run(c) {
  let t = 0, begin = -1, num = 0, starts = 0, crashes = 0, last = 0;
  while (t < c.horizonMs) {
    if (begin < 0 || t > begin + c.intervalMs) { begin = t; num = 0; }
    if (num >= c.burst) return [starts, crashes, 'failed (limit hit)', t];
    num++; starts++; last = t;
    if (crashes >= c.crashLimit) return [starts, crashes, 'active (healthy)', t];
    t += c.uptimeMs; crashes++;
    t += c.restartMs;
  }
  return [starts, crashes, 'still looping', last];
}
const hour = 3600000;
const cases = [ // name, restart, window, burst, uptime, crashes until fixed
  ['A default   100ms 10s/5', 100, 10000, 5, 200, 1e9],
  ['B slow      5s    10s/5', 5000, 10000, 5, 1000, 1e9],
  ['C tuned     5s    300s/5', 5000, 300000, 5, 1000, 1e9],
  ['D transient 5s    300s/5', 5000, 300000, 5, 1000, 2],
];
console.log('case                      starts crashes end state          at');
for (const [label, restartMs, intervalMs, burst, uptimeMs, crashLimit] of cases) {
  const [s, cr, end, at] = run({ restartMs, intervalMs, burst, uptimeMs, crashLimit, horizonMs: hour });
  console.log(label.padEnd(25), String(s).padStart(6), String(cr).padStart(7),
    ' ' + end.padEnd(18), (at / 1000).toFixed(1) + 's');
}
// The limit can stop a loop only if burst full cycles fit inside the window.
for (const [label, r, i, b, u] of cases.slice(0, 3)) {
  const need = (b * (r + u)) / 1000;
  console.log(label[0], 'needs', need + 's', need < i / 1000 ? '< window: stops' : '>= window: never stops');
}

আউটপুট:

case                      starts crashes end state          at
A default   100ms 10s/5        5       5  failed (limit hit) 1.5s
B slow      5s    10s/5      600     600  still looping      3594.0s
C tuned     5s    300s/5       5       5  failed (limit hit) 30.0s
D transient 5s    300s/5       3       2  active (healthy)   12.0s
A needs 1.5s < window: stops
B needs 30s >= window: never stops
C needs 30s < window: stops

A: ডিফল্টে এক চক্র ০.৩ সেকেন্ড, ৫ বার পরে ১.৫ সেকেন্ডে ঠেকে যায়। সীমা কাজ করল, তবে এত দ্রুত যে ছোট database বিভ্রাটেও service হাল ছেড়ে দেয়। B সবচেয়ে বিপজ্জনক: কেউ ভালো ভেবে RestartSec=5 দিলেন, কিন্তু window বদলালেন না। ১০ সেকেন্ডে দুটির বেশি start হয়ই না, তাই এক ঘণ্টায় ৬০০ বার crash, অথচ status-এ কখনো failed আসে না। C: ৫ × ৬ = ৩০ সেকেন্ড, ৩০০-এর অনেক কম, তাই ৩০ সেকেন্ডে থামে। D: দুবার ভেঙে তৃতীয়বার সুস্থ, মানে ক্ষণস্থায়ী সমস্যা; restart ঠিক এই ঘটনার জন্যই। C আর D একই setting, তাই একটি ভালো policy দুটোই সামলায়।

এবার ছোট সংখ্যায় একটি crash loop ধাপে ধাপে দেখুন। ধরুন RestartSec=5, StartLimitIntervalSec=60, StartLimitBurst=3, আর ভুল database password-এর কারণে অ্যাপ প্রতিবার ১ সেকেন্ড পরে exit 1 দেয়।

simulated ঘড়িতে unit-এর অবস্থা
সময়
Active
window-এ start
  1. t = ০. প্রথম start, window শুরু। গণনা ৩-এর মধ্যে ১। Status দেখলে সব ঠিক মনে হবে, কারণ Type=simple-এ শুরু হওয়াই “active”.
  2. t = ১. Password ভুল, exit 1. Restart=on-failure-এর নিয়মে আবার চলবে, তবে ৫ সেকেন্ড পরে। এই অপেক্ষার সময় অবস্থা activating (auto-restart).
  3. t = ৬. দ্বিতীয় start, গণনা ২। Database ভাঙা থাকলে এখানে সুস্থ হওয়ার সুযোগ ছিল।
  4. t = ৭. আবার exit 1, কারণ password ভুলই আছে। Restart এই ভুল ঠিক করতে পারে না।
  5. t = ১২. তৃতীয় start. গণনা ৩, সীমায় পৌঁছে গেল, তবে এই start অনুমোদিত।
  6. t = ১৩. তৃতীয় crash. systemd পরের চেষ্টা ৫ সেকেন্ড পরের জন্য ঠিক করল।
  7. t = ১৮. ১৮ সেকেন্ড ৬০-এর ভেতরে, আর গণনা ৩ = burst, তাই চতুর্থ start নিষিদ্ধ। journal-এ আসে Start request repeated too quickly, অবস্থা failed. এখন এটি একটি স্পষ্ট alert-যোগ্য অবস্থা। হিসাব মিলছে: ৩ × ৬ = ১৮ < ৬০.
  8. t = ৯০০. On-call প্রকৌশলী journal পড়ে password ঠিক করলেন। reset-failed দিয়ে হিসাব মুছে আবার start; গণনা নতুন করে ১। পুরো ঘটনার একটি লাইন runbook-এ যাবে।

৬ · Resource সীমা আর sandboxing — একজনের ভুলে সবাই যেন না ডোবে

একটি server-এ কয়েকটি service থাকে। একটির memory leak যেন পুরো machine না ডোবায়। মেসের কথা ভাবুন: এক সাব-মিটার ছাড়া একজনের হিটার সবার বিল বাড়ায়। সাব-মিটার বসালে প্রত্যেকের হিসাব ও সীমা আলাদা। সেই সাব-মিটার হলো cgroup. Linux এই সুবিধা দেয়, আর systemd প্রতিটি service-কে নিজের cgroup-এ রাখে; ভেতরের তত্ত্ব আছে Linux-এর scheduler পর্বে। Docker আর Kubernetes-ও একই kernel সুবিধা ব্যবহার করে (পর্ব ৩৯, ৪৮)।

# sudo systemctl edit bazar-api   (saved as a drop-in override)
[Service]
MemoryHigh=450M
MemoryMax=512M
CPUQuota=150%
TasksMax=512

NoNewPrivileges=yes
ProtectSystem=strict
ProtectHome=yes
PrivateTmp=yes
ReadWritePaths=/var/lib/bazar-api
Settingমানে
MemoryMaxকঠিন সীমা। এখানে পৌঁছালে kernel আগে memory ফেরানোর চেষ্টা করে, না পারলে এই cgroup-এর ভেতরেই OOM kill
MemoryHighনরম সীমা। Kill করে না, process-কে ধীর করে আর memory ফিরিয়ে নেয়। এটি একটি আগাম সতর্ক রেখা
CPUQuotaএকটি core-এর শতাংশ। 150% মানে প্রতি ১০০ মিলিসেকেন্ডে সর্বোচ্চ ১৫০ মিলিসেকেন্ড CPU সময়
TasksMaxprocess ও thread সংখ্যার সীমা, যাতে নিয়ন্ত্রণহীন fork না চলে
NoNewPrivilegessetuid প্রোগ্রাম চালিয়েও এই process আর বেশি ক্ষমতা পাবে না
ProtectSystem=strictপুরো filesystem এই service-এর চোখে read-only; শুধু ReadWritePaths লেখা যাবে
ProtectHome/home, /root তার কাছে অদৃশ্য
PrivateTmpনিজের আলাদা /tmp; অন্য service-এর ফাইল দেখে না

হিসাবের দুটি ফাঁদ। এক, CPUQuota=50% চার core-এর machine-এর অর্ধেক নয়, একটি core-এর অর্ধেক; মানে পুরো machine-এর ১২.৫%. চার core-এর পুরোটা হলো 400%. দুই, systemd-র M হলো 1024 ভিত্তিক: 512M = 512 × 1024 × 1024 = 536,870,912 byte, যা এই পর্বের stepper-এর memory.max-এর সাথে মেলে।

Sandboxing বাগ ঠিক করে না, ক্ষতির blast radius ছোট করে। অ্যাপ দখল হলেও সে সিস্টেম ফাইল বদলাতে পারে না, আর /tmp দিয়ে অন্য service-এর গোপন ফাইল দেখতে পারে না। ভেতরে এগুলো namespace ও অন্যান্য kernel সুবিধা; তত্ত্ব আছে Sandboxing পর্বে। systemd-analyze security bazar-api একটি service-এর উন্মুক্ততার মোটামুটি নম্বর দেয়, যা হিসেবে ভালো তবে নিরাপত্তার প্রমাণ নয়। নতুন setting একটি একটি করে যোগ করুন, কারণ ProtectSystem=strict অ্যাপের লেখার পথ বন্ধ করে ভেঙে দিতে পারে।

৭ · Load average, memory pressure আর OOM killer

এবার পুরো machine-এর স্বাস্থ্য। uptime বা top-এর প্রথম লাইনে তিনটি সংখ্যা থাকে: load average ১, ৫ আর ১৫ মিনিটের। এটি CPU-র শতাংশ নয়। এটি বলে গড়ে কতটি task চলছে বা চলার অপেক্ষায় আছে।

ব্যাংকে ৪টি কাউন্টার। Load ৪ মানে গড়ে প্রতিটি কাউন্টারে একজন, কোনো লাইন নেই। Load ৮ মানে ৪ জন সেবা পাচ্ছেন, আরও ৪ জন দাঁড়িয়ে। আর একই load ৮ যদি ১৬ কাউন্টারের শাখায় হয়, তাহলে অর্ধেক কাউন্টার ফাঁকা। তাই load একা অর্থহীন; সবসময় core সংখ্যা দিয়ে ভাগ করুন।

Linux-এ একটি বিশেষ কথা আছে: এই গণনায় CPU-র অপেক্ষায় থাকা task-এর সাথে D state-এর task-ও পড়ে, মানে যারা সাধারণত disk I/O-র জন্য আটকে আছে। তাই load উঁচু আর CPU প্রায় ফাঁকা হলে সমস্যা CPU নয়, খুব সম্ভবত disk. Kernel প্রতি ৫ সেকেন্ডে একটি সূচকীয় গড় হিসাব করে: load = load × e + n × (1 − e), যেখানে এক মিনিটের জন্য e = exp(−5/60). নিচের simulator ৪ core-এর machine-এ এক মিনিটের spike দেখায়, আর তারপর একই load ৮.০ চার রকম machine-এ মাপে।

// Linux load average: every 5 s, load = load*e + n*(1-e), with
// e = exp(-5/60), exp(-5/300), exp(-5/900) for the 1, 5, 15 minute figures.
// n = tasks runnable or in uninterruptible sleep. Machine has 4 cores.
const cores = 4;
const decay = [60, 300, 900].map(w => Math.exp(-5 / w));
let load = [2, 2, 2];                             // steady n = 2 before spike
const nAt = s => (s >= 60 && s < 120 ? 16 : 2);   // 1-minute spike of 16 tasks
const verdict = l => l / cores < 0.7 ? 'ok' : l / cores <= 1 ? 'busy' : 'QUEUE';

console.log('min  n   load1  load5 load15  load1/core  trend    verdict');
for (let s = 0; s <= 600; s += 5) {
  if (s % 60 === 0 && (s <= 240 || s === 600)) {
    const [a, b, c] = load;
    console.log(String(s / 60).padStart(3), String(nAt(s)).padStart(2),
      a.toFixed(2).padStart(7), b.toFixed(2).padStart(6), c.toFixed(2).padStart(6),
      (a / cores).toFixed(2).padStart(11),
      (a > c + 0.5 ? 'rising' : a < c - 0.5 ? 'falling' : 'steady').padEnd(8), verdict(a));
  }
  const n = nAt(s);
  load = load.map((l, i) => l * decay[i] + n * (1 - decay[i]));
}

console.log('\nsame load 8.0 on different machines');
for (const c of [2, 4, 8, 16]) {
  const r = 8 / c;
  console.log(String(c).padStart(3) + ' cores  per core ' + r.toFixed(2).padStart(4) + '  ' +
    (r > 1 ? (8 - c) + ' tasks waiting on average' : r === 1 ? 'exactly full' : (c - 8) + ' cores free on average'));
}

আউটপুট:

min  n   load1  load5 load15  load1/core  trend    verdict
  0  2    2.00   2.00   2.00        0.50 steady   ok
  1 16    2.00   2.00   2.00        0.50 steady   ok
  2  2   10.85   4.54   2.90        2.71 rising   QUEUE
  3  2    5.26   4.08   2.84        1.31 rising   QUEUE
  4  2    3.20   3.70   2.79        0.80 steady   busy
 10  2    2.00   2.51   2.53        0.50 falling  ok

same load 8.0 on different machines
  2 cores  per core 4.00  6 tasks waiting on average
  4 cores  per core 2.00  4 tasks waiting on average
  8 cores  per core 1.00  exactly full
 16 cores  per core 0.50  8 cores free on average

মিনিট ২-এ spike-এর আসল মান ১৬, কিন্তু load1 মাত্র ১০.৮৫: 2 + 14 × (1 − e−1) = 10.85. গড় পিছিয়ে থাকে। তাই তিনটি সংখ্যার ঢাল পড়ুন: load1 > load15 মানে চাপ বাড়ছে, উল্টো হলে কমছে। মিনিট ১০-এ load1 স্বাভাবিক, কিন্তু load15 তখনো ২.৫৩, অর্থাৎ ঘটনার ছাপ রয়ে গেছে। নিচের অংশ ব্যাংকের উদাহরণটিই সংখ্যায়: load ৮.০ দুই core-এ ভয়ংকর, ১৬ core-এ আরামদায়ক।

Memory: free কম মানেই বিপদ নয়

Linux ফাঁকা RAM ফেলে রাখে না; disk-এর data রেখে দেয় page cache-এ। দরকার হলে এই জায়গা ছেড়ে দেয়। তাই দেখার সংখ্যা available, free নয়। আসল চাপ শুরু হয় যখন kernel-কে কষ্ট করে memory ফেরাতে হয়: swap-এ ঘন ঘন লেখা-পড়া, ধীর হয়ে যাওয়া। এই memory pressure সরাসরি মাপে PSI (/proc/pressure/memory, Linux 4.20 থেকে)। আরও তত্ত্ব আছে Linux-এ মেমরি পর্বে।

শেষ পর্যন্ত জায়গা না মিললে OOM killer নামে। Linux সাধারণত চাওয়া memory-র প্রতিশ্রুতি আগে দিয়ে দেয়, এই overcommit-এর দাম এখানেই শোধ হয়। Kernel একটি process বেছে SIGKILL পাঠায়, সাধারণত যার oom_score বেশি, মানে সাধারণত যে বেশি memory ধরে আছে। OOMScoreAdjust= দিয়ে এই পছন্দ প্রভাবিত করা যায়। দুটি ধরন আলাদা করুন: MemoryMax-এর কারণে হলে শুধু সেই cgroup-এর ভেতরে kill, journal-এ Memory cgroup out of memory; পুরো machine ফুরালে Out of memory: Killed process, আর শিকার যে কেউ হতে পারে। নিচের ঘটনাটি প্রথম ধরনের।

bazar-api · MemoryMax=512M (নমুনা মান)
Memory
Active
আপনার কাজ
14:02সব স্বাভাবিক
Memory: 186.4M (max: 512.0M)
16:38ধীরে ধীরে leak
Memory: 498.0M (max: 512.0M)
# kernel page cache ছেড়ে দিচ্ছে, অ্যাপ ধীর হচ্ছে
16:40:52সীমায় পৌঁছাল
limit : 512 MiB = 524288 kB
used  : anon-rss 519844 kB + file-rss 1204 kB + kernel
reclaim failed, OOM killer in this cgroup
16:40:52SIGKILL
handler চলার সুযোগ নেই
চলমান request ভেঙে গেল
exit status 137, মানে 128 + 9
Restart=on-failure: 5 সেকেন্ড পরে আবার
নমুনা আউটপুটversion-ভেদে আলাদা হতে পারে
$ systemctl status bazar-api
Active: activating (auto-restart) (Result: oom-kill)
Process: 2417 ExecStart=... (code=killed, signal=KILL)
নমুনা journal, ছাঁটাversion-ভেদে আলাদা হতে পারে
$ journalctl -k -g "out of memory" --since "16:30"
kernel: Memory cgroup out of memory: Killed
  process 2417 (node) total-vm:1834212kB,
  anon-rss:519844kB, file-rss:1204kB, UID:998

$ journalctl -u bazar-api --since "16:30"
systemd[1]: bazar-api.service: A process of this
  unit has been killed by the OOM killer.
systemd[1]: bazar-api.service: Failed with result
  'oom-kill'.
সিদ্ধান্তদুটি সম্ভাবনা
ধীরে ধীরে সিঁড়ির মতো বাড়ে, নামে না → leak: কোড ঠিক
ট্রাফিকের সাথে বাড়ে, পরে নামে  → সীমা ছোট: load test
                                  করে MemoryMax বাড়ান
  1. দুপুর ২টা। Service চালু, memory ১৮৬ MiB, সীমা ৫১২ MiB. কোনো সমস্যা নেই। কিন্তু একটি ছোট leak চুপচাপ জমছে।
  2. ২ ঘণ্টা ৩৬ মিনিট পরে। Memory ৪৯৮ MiB. Kernel এই cgroup-এর page cache ফেরত নিয়ে জায়গা করছে, ফলে অ্যাপ ধীর হচ্ছে। এখনো কেউ alert পাননি।
  3. সীমায় পৌঁছাল। ৫১২ MiB = ৫,২৪,২৮৮ kB. Anon memory একাই প্রায় ৫,১৯,৮৪৪ kB, যা ফেরত নেওয়ার মতো নয়। Reclaim ব্যর্থ, তাই এই cgroup-এর ভেতরেই OOM killer নামল। পাশের service-গুলোর গায়ে হাত পড়েনি।
  4. SIGKILL. এখানে handler নেই, graceful shutdown নেই। চলমান request ভেঙে গেল। Status 137. Restart=on-failure ৫ সেকেন্ড পরের জন্য নতুন start ঠিক করল, আর alert গেল আপনার কাছে।
  5. প্রথম প্রশ্ন: কীভাবে মরল? systemctl status-এ Result: oom-kill আর signal=KILL. কেউ হাতে kill করেনি, অ্যাপ নিজে থেকেও থামেনি।
  6. দ্বিতীয় প্রশ্ন: কোন ধরনের OOM? Service এর মধ্যে আবার চলছে। Kernel-এর log বলে Memory cgroup out of memory, তাই সীমা ছুঁয়েছে, machine ফুরায়নি। anon-rss মিলছে আগের হিসাবের সাথে, আর systemd-র লাইন একই ঘটনা নিশ্চিত করছে।
  7. সিদ্ধান্ত। সুস্থ হওয়াটা ভুল ভরসা দেয়, কারণ leak থাকলে ঘটনাটি আবার হবে। Memory graph দেখুন: না নামা সিঁড়ি মানে leak, তাই কোড ঠিক করুন; ট্রাফিকের সাথে ওঠানামা মানে সীমা ছোট, তাই মেপে বাড়ান। অন্ধভাবে সীমা তুলে দিলে পরের বার পুরো machine ডুবতে পারে।

৮ · top, free, vmstat, iostat, df, du আর USE method

সমস্যার সময় অনুমান নয়, ক্রম দরকার। ২০১২ সালে Brendan Gregg ACM Queue-তে যে পদ্ধতি লেখেন, তার নাম USE method: প্রতিটি resource-এর জন্য তিনটি প্রশ্ন। Utilization: কত সময় ব্যস্ত? Saturation: কত কাজ সারিতে? Errors: ভুল হচ্ছে কি? Utilization একা ঠকায়, কারণ ৯০% ব্যস্ত কাউন্টারে লাইন নাও থাকতে পারে; লাইন দেখায় saturation. Metric-এর দিক থেকে USE আবার আসবে পর্ব ৬৩-তে, আর গভীর performance তদন্ত আছে পারফরম্যান্স আর observability পর্বে।

ResourceUtilizationSaturationErrors
CPUtop: us + syload ÷ core, vmstat-এর rjournal-এ MCE ধরনের বার্তা
Memoryfree: availablevmstat-এর si, so; PSIOOM kill, journalctl -k
Diskiostat -xz: %utilaqu-sz, await, vmstat-এর bjournalctl -k-এ I/O error
জায়গাdf -h, df -i৯০% পেরোনোNo space left on device
Networkপর্ব ১৩-র টুলdrop, retransmitinterface error

নিচের সবগুলো নমুনা আউটপুট, শেখার জন্য বানানো এবং version-ভেদে column আলাদা হতে পারে। একই ঘটনার ছবি: ২ vCPU, ৮ GB-র একটি server ধীর হয়ে গেছে।

$ top -bn1 | head -3                     # sample
top - 14:32:07 up 12 days,  3:11,  1 user,  load average: 6.42, 5.10, 3.87
Tasks: 142 total,   3 running, 139 sleeping,   0 stopped,   0 zombie
%Cpu(s): 38.2 us,  7.1 sy,  0.0 ni, 12.4 id, 41.0 wa,  0.0 hi,  1.3 si,  0.0 st

$ free -m                                # sample
               total        used        free      shared  buff/cache   available
Mem:            7820        7050         180          64         590         420
Swap:           2047        1460         587

$ vmstat 1 3                             # sample; line 1 is average since boot
procs -----------memory---------- ---swap-- -----io---- -system-- ------cpu-----
 r  b   swpd   free   buff  cache   si   so    bi    bo   in   cs us sy id wa st
 1  1 1458000 188000  12000 580000    3    5    40    60  300  600 12  3 83  2  0
 2  6 1460200 181000  12000 578000  820  640  9120  1530 2210 4310 38  7 12 41  0
 3  5 1461100 180000  12000 577000  790  710  8840  1660 2180 4250 37  8 13 40  0

$ iostat -xz 1                           # sample; some columns removed
Device     r/s    w/s   r_await  w_await  aqu-sz  %util
vda      412.0   88.0     18.40    41.20    9.80   98.7

$ df -h                                  # sample
Filesystem      Size  Used Avail Use% Mounted on
/dev/vda1        40G   37G  1.1G  97% /
/dev/vdb1       100G   61G   34G  65% /data

$ du -xsh /var/log/* | sort -h | tail -2   # sample
1.2G    /var/log/journal
19G     /var/log/bazar-api
টুলকীভাবে পড়বেনএই নমুনায়
topus আপনার কোড, sy kernel, id ফাঁকা, wa disk-এর অপেক্ষা, st hypervisor নিয়ে গেছে। Shared VM-এ st বেশি হলে পাশের কারও চাপ (পর্ব ১৫)wa ৪১%: CPU-র হাতে কাজ নেই, task-গুলো disk-এর জন্য আটকে আছে
freeavailable দেখুন; free আর buff/cache মিলিয়ে ভুল সিদ্ধান্ত নেবেন না৪২০ MiB, মাত্র ৫.৪%
vmstatপ্রথম লাইন boot থেকে গড়, বাদ দিন। r সারিতে task, b আটকে থাকা task, si/so swap-এ আসা-যাওয়াso ৬৪০: এখনই swap চলছে
iostatawait প্রতি I/O-তে সময়, aqu-sz গড় সারি। %util SSD ও cloud disk-এ ঠকাতে পারে, কারণ এরা একসাথে অনেক কাজ করেসারি ৯.৮, %util ৯৮.৭
dfUse% সাধারণ ব্যবহারকারীর জন্য জায়গার হিসাব; root-এর জন্য জায়গা সংরক্ষিত থাকে, তাই Size ≠ Used + Avail. df -i দেখায় inode/ ৯৭%
duকোন ফোল্ডার খাচ্ছে। df ভরা অথচ du কম হলে মুছে ফেলা ফাইল কোনো process এখনো ধরে আছে; সেই service restart করলে জায়গা ফেরেapp log ১৯ GB

এবার এই সব নমুনার মূল অংশ একটি heredoc-এ রেখে ছোট একটি triage script চালাই। এটি সত্যিকারের সিস্টেমের কোনো কিছু পড়ে না, শুধু নিজের ভেতরের লেখা।

cat > snapshot.txt <<'EOF'
top - 14:32:07 up 12 days,  3:11,  1 user,  load average: 6.42, 5.10, 3.87
%Cpu(s): 38.2 us,  7.1 sy,  0.0 ni, 12.4 id, 41.0 wa,  0.0 hi,  1.3 si,  0.0 st
               total        used        free      shared  buff/cache   available
Mem:            7820        7050         180          64         590         420
Swap:           2047        1460         587
Filesystem      Size  Used Avail Use% Mounted on
/dev/vda1        40G   37G  1.1G  97% /
/dev/vdb1       100G   61G   34G  65% /data
tmpfs           3.9G     0  3.9G   0% /dev/shm
EOF
cores=2; warns=0
alert() { echo "WARN  $1"; warns=$((warns + 1)); }
over()  { awk -v a="$1" -v b="$2" 'BEGIN { exit !(a > b) }'; }

load1=$(sed -n 's/.*load average: \([0-9.]*\),.*/\1/p' snapshot.txt)
per=$(awk -v l="$load1" -v c="$cores" 'BEGIN { printf "%.2f", l / c }')
echo "load1=$load1 cores=$cores per-core=$per"
over "$per" 1 && alert "tasks are queueing"

wa=$(awk -F'[ ,]+' '/^%Cpu/ { print $10 }' snapshot.txt)
echo "iowait=$wa%"
over "$wa" 20 && alert "much time lost waiting for disk"

read -r avail pct < <(awk '/^Mem:/ { printf "%d %.1f\n", $7, 100 * $7 / $2 }' snapshot.txt)
echo "available=$avail MiB ($pct%)"
over 10 "$pct" && alert "less than 10% memory available"

read -r used swp < <(awk '/^Swap:/ { print $3, int(100 * $3 / $2) }' snapshot.txt)
echo "swap used=$used MiB ($swp%)"
over "$swp" 50 && alert "more than half of swap used"

while read -r mnt use; do alert "disk $mnt at $use%"; done < <(
  awk '$5 ~ /^[0-9]+%$/ && $5 + 0 >= 90 { print $6, $5 + 0 }' snapshot.txt)
echo "summary: $warns warnings"

আউটপুট:

load1=6.42 cores=2 per-core=3.21
WARN  tasks are queueing
iowait=41.0%
WARN  much time lost waiting for disk
available=420 MiB (5.4%)
WARN  less than 10% memory available
swap used=1460 MiB (71%)
WARN  more than half of swap used
WARN  disk / at 97%
summary: 5 warnings

পাঁচটি সতর্কতা, কিন্তু পাঁচটি আলাদা সমস্যা নয়। গল্পটি এক সুতোয় বাঁধুন: memory প্রায় শেষ, তাই kernel swap করছে; swap হলো disk-এ যাওয়া, তাই wa ৪১% আর task-গুলো D state-এ; সেই task-গুলোই প্রতি core-এ load ৩.২১-এ তুলেছে। CPU আসল সমস্যা নয়, তাই core বাড়ালে তেমন লাভ নেই। আগে memory-র মালিক খুঁজুন। Disk ৯৭% আলাদা, স্বাধীন আগুন: du বলে দোষী app log, তাই log rotation লাগবে। Script শুধু সতর্ক করে; সিদ্ধান্ত মানুষের। এই সীমাগুলো উদাহরণ মাত্র, প্রতিটি service-এর স্বাভাবিক মান নিজের মতো মেপে নিতে হয়।

আরও গভীরে: Linux-এর load-এ D state ঢোকে ১৯৯৩ সালের একটি পরিবর্তনে, আর এর ইতিহাস Brendan Gregg ২০১৭ সালের “Linux Load Averages: Solving the Mystery” লেখায় খুঁড়ে বের করেন। তাঁর ২০১৫ সালের “Linux Performance Analysis in 60,000 Milliseconds” লেখার প্রথম-মিনিটের কমান্ড তালিকা অনেক দলের runbook-এর বীজ। systemd-র v254 থেকে RestartSteps= আর RestartMaxDelaySec= দিয়ে restart-এর অপেক্ষা ধাপে ধাপে বাড়ানো যায়, যা exponential backoff-এর ধারণা। আর systemd-oomd PSI দেখে kernel-এর OOM-এর আগেই ব্যবস্থা নিতে পারে, distribution-ভেদে চালু থাকে বা থাকে না।

পরীক্ষা ও ইন্টারভিউতে:

  • systemctl start আর enable-এর পার্থক্য? Start এখন চালায়; enable boot-এ চলার symlink বানায়।
  • Exit status 137 মানে? 128 + 9, SIGKILL; প্রায়ই OOM kill বা stop-এর সময়সীমা শেষ।
  • ৪ core, load 8 — ভালো না খারাপ? প্রতি core-এ ২, সারি জমছে; তবে Linux-এ D state-ও গোনে, তাই wa দেখে CPU না disk ঠিক করুন।
  • free প্রায় শূন্য, সমস্যা? একা নয়। available আর swap activity দেখুন; page cache ফেরত পাওয়া যায়।
  • Disk ভরা, du-তে কিছু নেই? মুছে ফেলা কিন্তু খোলা ফাইল; lsof +L1 দিয়ে খুঁজে সেই process restart করুন।

৯ · AI-এর যুগে service চালানো

AI কয়েক সেকেন্ডে unit file লিখে দেয়, আর ফাইলটি দেখতে ঠিকঠাকই লাগে। কিন্তু ফাইলটি production-এ root হিসেবে চলবে। তাই এখানে “চলছে” যথেষ্ট প্রমাণ নয়। AI সাধারণত যে ভুলগুলো করে:

AI যা দেয়কেন ক্ষতিকরযাচাই
User=root বা User-ই নেইঅ্যাপ দখল হলে পুরো serversystemd-analyze security, least privilege
Restart=always + RestartSec=0অদৃশ্য crash loop, log ভরে, CPU খায়উপরের সূত্রে হিসাব করুন
Unit file-এ passwordsecret ফাঁস, git-এ ওঠার ঝুঁকিকড়া অনুমতির আলাদা ফাইল (পর্ব ৩৩)
বানানো setting, যেমন MaxMemory=systemd অচেনা setting উপেক্ষা করে, সীমা বসেই নাsystemd-analyze verify, man systemd.resource-control
“Disk ভরা? rm -rf /var/log/*”তদন্তের প্রমাণ শেষ, খোলা ফাইলে জায়গাও ফেরে নাআগে du, তারপর rotation বা --vacuum-time
“Service আটকে? kill -9”graceful shutdown বাদ, data ভাঙার ঝুঁকিআগে systemctl stop আর journal
“Memory কম? drop_caches”কারণ ঠিক হয় না, cache হারিয়ে disk আরও ব্যস্তUSE ধরে memory-র মালিক খোঁজা

AI-কে শুধু “একটি service file লিখে দিন” না বলে পরিবেশ, সীমা আর যাচাই একসাথে চান। Log পেস্ট করার আগে hostname, IP, token মুছে নিন।

আমার একটি Node.js API আছে। Ubuntu LTS-এ systemd দিয়ে চালাব। পথ /opt/bazar-api/server.js, user হবে bazar, port 8080. আমার জন্য একটি unit file লেখো, যাতে: ১. Secret unit file-এ থাকবে না, কড়া অনুমতির EnvironmentFile-এ থাকবে। ২. Restart=on-failure, আর RestartSec ও StartLimit এমন হবে যাতে burst × চক্রের সময় window-র চেয়ে ছোট হয়। হিসাবটি দেখাও। ৩. MemoryMax, CPUQuota আর NoNewPrivileges, ProtectSystem, PrivateTmp দাও। প্রতিটি setting systemd-র কোন man page-এ আছে বলো। কোনো setting নিয়ে নিশ্চিত না হলে স্পষ্ট বলো, বানিয়ে লিখো না। শেষে systemd-analyze verify আর পরীক্ষা করার ধাপ লেখো।

AI-এর দেওয়া unit বা কমান্ড হাতে পেলে

ভাবনার প্রশ্ন

প্রতিটি প্রশ্ন নিয়ে নিজে কিছুক্ষণ ভাবুন, তারপর “→ উত্তর” চাপুন।

প্র ০১Shell script দিয়েই তো দশকের পর দশক service চলছিল। তাহলে systemd নিয়ে এত বিতর্ক কেন, আর এটি কেন জিতল?

SysV init-এর দুনিয়ায় প্রতিটি service-এর একটি shell script থাকত। ক্রম ঠিক হতো ফাইলের নামের সংখ্যায়, আর সব একে একে চলত, তাই boot ছিল ধীর। প্রতিটি script নিজেই PID ফাইল লিখত, আর process সন্তান তৈরি করে পালালে তাকে আর খুঁজে পাওয়া যেত না। Restart বা resource সীমা নিজে থেকে ছিল না।

২০১০ সালে systemd তিনটি বড় চিন্তা আনে। প্রথমত, script-এর বদলে ঘোষণামূলক ফাইল: কী চাই লিখুন, কীভাবে তা systemd জানে। দ্বিতীয়ত, cgroup দিয়ে process ট্র্যাক করা, তাই fork করে পালানো সম্ভব নয়। তৃতীয়ত, নির্ভরতা ধরে একসাথে অনেক service চালানো।

বিতর্ক হয় পরিসর নিয়ে। সমালোচকদের মত ছিল, systemd শুধু init নয়, logging, login, network, DNS-এর দিকেও ছড়িয়ে গেছে, যা Unix-এর “একটি কাজ ভালোভাবে” দর্শনের উল্টো, আর PID 1 বড় হলে ঝুঁকিও বড়। ২০১৪ সালে Debian-এর technical committee-তে এ নিয়ে তিক্ত তর্ক ও ভোট হয়, এবং systemd ডিফল্ট হয়; কেউ কেউ এর জেরে Devuan নামে আলাদা distribution শুরু করেন। সমর্থকদের যুক্তি ছিল, আলাদা আলাদা অংশ একসাথে ভালোভাবে জুড়ে না থাকলে distribution-গুলোকে সেই আঠা নিজেরা লিখতে হয়।

এটি জিতেছে মূলত ব্যবহারিক কারণে: distribution-গুলোর জন্য একই unit ফাইল সব জায়গায় চলে, আর operator-রা একই কমান্ড পান। তবে এটিকে নিখুঁত বলা ঠিক নয়। Container-এ অনেকেই systemd ছাড়াই চলেন, আর Alpine-এর মতো distribution অন্য পথ বেছে নিয়েছে।

মূল উপলব্ধি: প্রকৌশলের বড় লড়াইগুলো প্রায়ই প্রযুক্তির নয়, মূল্যবোধের: ছোট স্বাধীন অংশ, না একসাথে জোড়া ব্যবস্থা। উভয় পক্ষের যুক্তি বুঝলে আপনি শুধু কমান্ড নয়, নকশার দাম বোঝেন।

প্র ০২৪ core-এর server-এ load ২০, অথচ top-এ CPU ৭০% ফাঁকা। এটি কীভাবে সম্ভব, আর Linux এমন “অদ্ভুত” গণনা করে কেন?

ব্যাখ্যাটি Linux-এর সংজ্ঞায়। অনেক Unix-এ load শুধু CPU-র সারি গোনে। Linux গোনে চলার যোগ্য task আর uninterruptible sleep-এ, মানে D state-এ থাকা task, দুটোই। তাই ২০টি task যদি একটি ধীর disk বা আটকে যাওয়া network storage-এর উত্তরের অপেক্ষায় থাকে, load ২০ হবে, অথচ CPU-তে তাদের কোনো কাজই নেই। CPU তখন ফাঁকা বা wa দেখায়।

কেন এমন? Brendan Gregg ২০১৭ সালে পুরোনো mailing list খুঁড়ে ১৯৯৩ সালের একটি patch খুঁজে পান, যার বার্তায় বলা ছিল: শুধু CPU গুনলে machine swap করে ধীর হওয়ার সময় load কমে যেতে দেখা যায়, যা বিভ্রান্তিকর। Swap-এ আটকে থাকা task তো কাজের চাহিদাই, শুধু CPU চায় না। তাই সংখ্যাটি হলো “system-এর চাহিদা”, শুধু “CPU-র চাহিদা” নয়।

এর একটি পরোক্ষ ফল আছে। Kernel-এর কিছু lock-এর অপেক্ষাও D state-এ পড়ে, তাই disk নীরব থাকলেও load উঠতে পারে। Network filesystem আটকে গেলে এমন task signal-এও সাড়া দেয় না, এমনকি kill -9-এও সঙ্গে সঙ্গে যায় না, কারণ signal তখনই কাজ করে যখন task kernel থেকে ফেরে।

তদন্তের ক্রম তাই এমন: আগে wa আর vmstat-এর b দেখুন, তারপর iostat -xz-এ সারি, আর দরকারে কোন task D state-এ তা খুঁজুন। আধুনিক সিস্টেমে PSI আলাদা করে CPU, memory আর I/O-র চাপ জানায়, যা একটি মিশ্র সংখ্যার চেয়ে অনেক পরিষ্কার। তাই auto-scaling-এর সিদ্ধান্ত load দিয়ে নিলে সাবধান: disk সমস্যায় নতুন CPU কেনা অর্থহীন।

মূল উপলব্ধি: প্রতিটি metric-এর একটি সংজ্ঞা আর একটি ইতিহাস আছে। সংখ্যাটি কী গোনে তা না জেনে সিদ্ধান্ত নিলে ভুল জায়গায় টাকা ঢালবেন।

প্র ০৩একটি process-কে মেরে ফেলাই কি kernel-এর সেরা সমাধান? OOM killer বন্ধ করে দিলে কী হয়?

OOM killer জন্মেছে overcommit থেকে। fork() করলে সন্তান বাবার memory “পায়”, কিন্তু আসল কপি হয় কেবল লিখলে। অ্যাপ বড় জায়গা চায়, ব্যবহার করে সামান্য। সব প্রতিশ্রুতি RAM-এ মেলাতে গেলে সিস্টেম অনেক অপচয় করত। তাই Linux ডিফল্টে বিমান কোম্পানির মতো কিছুটা বেশি টিকিট বিক্রি করে। সবাই একসাথে এলে কাউকে নামাতে হয়।

২০০৪ সালের দিকে kernel আলোচনায় Andries Brouwer-এর একটি বিখ্যাত উপমা ছিল: বিমান কোম্পানি জ্বালানি বাঁচাতে কম তেল নিল, আর তেল ফুরালে যাত্রী বেছে জানালা দিয়ে ফেলে দিতে লাগল। কৌতুকটির মর্ম হলো, kernel ঠিক কাকে মারবে তা নিখুঁতভাবে জানা অসম্ভব।

বিকল্প আছে। vm.overcommit_memory=2 দিলে kernel হিসাব রেখে অতিরিক্ত চাওয়া অস্বীকার করে; তখন malloc ব্যর্থ হয় এবং বড় fork()-ও ব্যর্থ হতে পারে। কিছু database দল এটি বিবেচনা করে, কিন্তু বেশিরভাগ অ্যাপ malloc-এর ব্যর্থতা ভালোভাবে সামলায় না, তাই crash তো থামেই না, আসে আরও অদ্ভুত জায়গায়। oom_score_adj-কে −1000 দিলে একটি process সুরক্ষিত থাকে, কিন্তু সব জরুরি process এমন করলে শিকার হবে অন্য কেউ, অথবা machine জমে যাবে।

বাস্তব সমস্যা হলো kernel-এর OOM অনেক দেরিতে আসে: তার আগে machine মিনিটের পর মিনিট swap আর reclaim-এ প্রায় অচল হতে পারে। এজন্য Facebook oomd বানায় ও প্রকাশ করে, যা PSI দেখে আগেই ব্যবস্থা নেয়; তার ধারণা থেকে এসেছে systemd-oomd. বড় সিস্টেমে আসল প্রতিরক্ষা হলো প্রতিটি service-এ cgroup সীমা, যাতে ক্ষতি এক ঘরে থাকে, আর memory বাড়ার trend-এ আগেই alert.

মূল উপলব্ধি: OOM kill কোনো বাগ নয়, ব্যবস্থা ডুবে যাওয়ার আগের শেষ উপায়। লক্ষ্য তাকে বন্ধ করা নয়, এমনভাবে নকশা করা যাতে তাকে কম ডাকতে হয় আর ডাকলেও ক্ষতি ছোট থাকে।

প্র ০৪Restart=always দিলেই কি reliability বাড়ে? “Restart করে দিন” সংস্কৃতির ঝুঁকি কী?

Restart সত্যিই শক্তিশালী হাতিয়ার। Telecom সিস্টেমের জন্য তৈরি Erlang-এর জগতে Joe Armstrong-এর দল “let it crash” দর্শন জনপ্রিয় করেন: ভুল পরিস্থিতি সব আগে থেকে ধরার চেষ্টার বদলে process মরতে দিন, আর একটি supervisor পরিষ্কার অবস্থায় আবার চালাক। অনেক বাগ বিরল সময়ের কারণে ঘটে, তাই নতুন শুরুতে এড়িয়ে যায়।

লক্ষণীয় হলো Erlang/OTP-র supervisor-এও একটি restart intensity আছে: নির্দিষ্ট সময়ে বেশি restart হলে supervisor নিজে হাল ছেড়ে উপরে জানায়। এটি systemd-র StartLimitBurst-এর হুবহু ধারণা। Kubernetes-ও CrashLoopBackOff-এ অপেক্ষা ক্রমশ বাড়ায়। সবার একই শিক্ষা: সীমাহীন restart ভালো নয়।

ঝুঁকি কয়েকটি। এক, লুকিয়ে যাওয়া: প্রতি ঘণ্টায় OOM হলে service দিব্যি “চলছে” দেখায়, অথচ প্রতিবার চলমান request হারায়, আর দল leak-টি জানতেই পারে না। দুই, ক্ষতি বাড়ানো: database ধীর হলে সবাই একসাথে restart করে, প্রত্যেকে নতুন connection খোলে, cache ঠান্ডা থাকে, আর চাপ আরও বাড়ে। এটি thundering herd-এর এক রূপ; প্রতিকার jitter আর backoff. তিন, data ভাঙা: অর্ধেক লেখা অবস্থায় বারবার মরলে state খারাপ হতে পারে। চার, log আর disk ভরে যাওয়া।

তাই ভালো অভ্যাস হলো restart-এর সংখ্যাকেই একটি metric বানানো। প্রতিটি অপ্রত্যাশিত restart একটি ছোট ঘটনা, যা journal-এ কারণসহ দেখা উচিত। একবার restart ঠিক আছে; সপ্তাহে একশোবার হলে সেটি একটি খোলা বাগ। Resilience pattern পর্বে এ ধরনের নকশা দেখা যাবে।

মূল উপলব্ধি: Restart হলো ব্যথানাশক, রোগের ওষুধ নয়। এটি সময় কেনে, কিন্তু সেই সময়ে কারণ খোঁজা আপনারই কাজ।

প্র ০৫সবাই যখন Kubernetes-এ যাচ্ছে, তখন systemd আর এসব টুল শেখার দরকার কী? Graceful shutdown-এর হিসাবই বা সেখানে কেমন?

Kubernetes server-কে উধাও করে না, শুধু আড়াল করে। প্রতিটি node একটি Linux machine, আর সেখানে kubelet ও container runtime সাধারণত systemd service হিসেবেই চলে। Node খারাপ হলে প্রথম কমান্ড প্রায়ই journalctl -u kubelet. Kubernetes-এর resource limit নিচে সেই একই cgroup, আর OOMKilled মানে সেই একই kernel OOM, exit code 137. শেখা ধারণাগুলো নাম বদলে ফিরে আসে: Restart= হয় restartPolicy, StartLimit হয় CrashLoopBackOff, TimeoutStopSec হয় terminationGracePeriodSeconds, লেখার সময়ে যার ডিফল্ট ৩০ সেকেন্ড।

Graceful shutdown-এ একটি সূক্ষ্মতা আছে, যা অনেকে ধরেন না। ধরুন একটি API load balancer-এর পেছনে আছে। SIGTERM পেলেই সে বন্ধ করলে load balancer কয়েক সেকেন্ড ধরে তখনো নতুন request পাঠায়, কারণ খবরটি ছড়াতে সময় লাগে। ফল: deployment-এর সময় ছোট ছোট 502. সঠিক ক্রম হলো: আগে load balancer থেকে নিজেকে সরানো, মানে health check ব্যর্থ বলা বা deregister, কিছুক্ষণ অপেক্ষা, তারপর হাতের request শেষ, তারপর বের হওয়া। Connection draining নামে এটি পর্ব ১৮-তে আসবে।

আর সময়সীমা হিসাব করে রাখতে হয়। সবচেয়ে ধীর request ২০ সেকেন্ড নিলে TimeoutStopSec বা grace period তার চেয়ে বেশি হতেই হবে। না হলে SIGKILL ঠিক সেই কাজটি নষ্ট করবে যা বাঁচানোর চেষ্টা করছিলেন। Deployment ও release পর্বে এর নকশার দিকটি পাবেন।

আরও একটি বাস্তব কথা: সব কিছু Kubernetes-এ চলে না। ছোট প্রতিষ্ঠানের একটি VM, database server, bastion, monitoring agent, build runner — এগুলোর অনেকই সরাসরি systemd-তে চলে। আর আপনি যে আলোচনাই করুন, ইন্টারভিউ আর on-call-এ এই মৌলিক টুলগুলো দিয়েই সত্য যাচাই হয়।

মূল উপলব্ধি: বিমূর্ততার প্রতিটি স্তর নিচের স্তরকে আড়াল করে, মুছে দেয় না। যখন ওপরের স্তর ভেঙে পড়ে, যিনি নিচের স্তর বোঝেন তিনিই ঘর ঠিক করতে পারেন।

অনুশীলন

নিজের কম্পিউটারে Linux না থাকলেও সবগুলো কাগজে করা যায়। Cloud-এর server-এ পরীক্ষা করলে খরচ হতে পারে; শেষে server মুছে ফেলুন।

  1. হিসাব করুন: (ক) অ্যাপ start-এর ১ সেকেন্ড পরে ভেঙে পড়ে। RestartSec=10, StartLimitIntervalSec=60, StartLimitBurst=5. কখন থামবে? (খ) RestartSec=12 করলে কী হবে? (গ) ৮ vCPU-র VM-এ load1 = ১২.০ হলে প্রতি core-এ কত? (ঘ) CPUQuota=250% মানে পুরো VM-এর কত শতাংশ, আর cpu.max-এ কী লেখা হবে?

    (ক) চক্র = ১ + ১০ = ১১ সেকেন্ড। Start হয় ০, ১১, ২২, ৩৩, ৪৪ সেকেন্ডে, মোট ৫টি। ষষ্ঠ চেষ্টা ৫৫ সেকেন্ডে, যা ৬০-এর ভেতরে, তাই নিষিদ্ধ; unit failed. সূত্র: ৫ × ১১ = ৫৫ < ৬০.

    (খ) চক্র ১৩, তাই ৫ × ১৩ = ৬৫, যা ৬০-এর চেয়ে বড়। Start হয় ০, ১৩, ২৬, ৩৯, ৫২ সেকেন্ডে। ষষ্ঠ চেষ্টা t = ৬৫-এ আসে, তখন window শেষ, গণনা নতুন করে শুরু। Crash loop কখনো থামবে না। দুই সেকেন্ডের পার্থক্যে আচরণ উল্টে গেল; তাই window যথেষ্ট বড় রাখুন, যেমন ৩০০।

    (গ) ১২ ÷ ৮ = ১.৫. গড়ে ৪টি task অপেক্ষায় (ধরে নিচ্ছি সব CPU চায়; wa দেখে নিশ্চিত হোন)।

    (ঘ) ২.৫ core ÷ ৮ = ৩১.২৫%. cpu.max-এ 250000 100000: প্রতি ১,০০,০০০ মাইক্রোসেকেন্ডে ২,৫০,০০০ মাইক্রোসেকেন্ড।

  2. ফলাফল অনুমান করুন: bazar-api থেমে আছে এবং disabled. পরপর চলল: (১) systemctl enable bazar-api (২) unit file-এ RestartSec ৫ থেকে ৩০ করা (৩) systemctl start bazar-api (৪) systemctl daemon-reload (৫) systemctl stop bazar-api (৬) server reboot. প্রতিটি ধাপের পর চলছে কি না, boot-এ চলবে কি না, আর শেষে কোন RestartSec কার্যকর?

    (১) থেমে আছে, enabled: enable এখন চালায় না। (২) কিছুই বদলায়নি, systemd পুরোনো ৫-ই মনে রেখেছে। (৩) চলছে, কিন্তু ৫ সেকেন্ডের নিয়মে; নতুন systemd version status-এ ফাইল বদলের সতর্কবার্তাও দেখাতে পারে। (৪) systemd ৩০ জেনে গেল, process-এর গায়ে হাত পড়েনি, এখনো চলছে। (৫) থেমে গেল, তবে enabled রইল: stop boot-এর সিদ্ধান্ত বদলায় না। (৬) Boot-এ নিজে থেকে চলবে, কারণ enabled, আর RestartSec=30 কার্যকর।

  3. সমালোচনা করুন: একজন AI থেকে এই unit নিলেন। অন্তত পাঁচটি সমস্যা বের করে ঠিক করুন: [Service]-এ User=root, Environment=DB_PASS=example-secret, ExecStart=nohup node /opt/app/server.js &, Restart=always, RestartSec=0; আর কোনো [Install] section নেই।
    • root: নিজস্ব কম ক্ষমতার user বানিয়ে User=app.
    • Secret: ফাইলটি প্রায়ই সবার পড়ার যোগ্য, আর systemctl show-এও দেখা যায়। 0600 অনুমতির EnvironmentFile-এ সরান, এবং এই মানটি আগে ফাঁস হয়ে থাকলে বদলান।
    • ExecStart: nohup আর & এখানে অর্থহীন; এটি shell নয়। পুরো path দিন: /usr/bin/node /opt/app/server.js.
    • Restart: on-failure আর RestartSec=5; সাথে StartLimitIntervalSec=300, StartLimitBurst=5, যা ৫ × (uptime + ৫) < ৩০০ রাখে, যদি crash ৫৫ সেকেন্ডের কমে হয়।
    • [Install] নেই: WantedBy=multi-user.target ছাড়া enable কাজ করে না, reboot-এ service ফিরবে না।
    • বাড়তি: MemoryMax, NoNewPrivileges, ProtectSystem=strict, PrivateTmp যোগ করুন; After আর Wants দিয়ে network-এর অপেক্ষা লিখুন।
  4. Triage: একটি কাল্পনিক ৪ core-এর server ধীর। নমুনা মান: load ৯.১, ৭.৪, ৩.২; top-এ us ৮, sy ৪, id ২৬, wa ৬২; free-এ available ৬২%; vmstat-এ r ১, b ৯, si ০, so ০; iostat-এ aqu-sz ৩২, r_await ৪৮০ ms; df-এ ৪১%. USE ধরে বলুন সমস্যা কোথায়, কোথায় নয়, আর পরের দুটি কমান্ড কী হবে।

    CPU: us + sy মাত্র ১২%, r ১, তাই সমস্যা নয়, যদিও load ÷ core = ২.২৮. Memory: available ৬২%, swap activity শূন্য, তাই নয়। Disk জায়গা: ৪১%, নয়। Disk I/O: wa ৬২, b ৯, সারি ৩২, await ৪৮০ ms: saturation স্পষ্ট। Load বাড়ছে, কারণ load1 > load15.

    পরের ধাপ: journalctl -k -p err দিয়ে disk error খোঁজা, আর iotop বা pidstat -d 1 দিয়ে কে পড়ছে তা বের করা। হতে পারে index ছাড়া একটি ভারী query, backup job, বা cloud disk-এর IOPS সীমা। Core বাড়ালে কোনো লাভ হবে না।

নিজেকে যাচাই করুন

ভুল হলে কারণসহ ব্যাখ্যা পাবেন, আবার চেষ্টাও করতে পারবেন।

ক্রমএকটি নতুন systemd service চালু করার ধাপগুলো সঠিক ক্রমে সাজান (▲▼ চেপে সরান)

  1. /etc/systemd/system/-এ unit file লেখা
  2. systemd-analyze verify দিয়ে ফাইল পরীক্ষা
  3. systemctl daemon-reload দিয়ে systemd-কে ফাইল পড়ানো
  4. systemctl enable --now দিয়ে boot-এ ও এখনই চালানো
  5. systemctl status আর journalctl -u দিয়ে যাচাই

systemctl stop দিলে একটি সাধারণ service-এর ক্ষেত্রে ডিফল্টে কী ঘটে?

  • সরাসরি SIGKILL পাঠানো হয়
  • প্রথমে SIGTERM, সময়সীমা পেরোলে SIGKILL
  • SIGHUP পাঠিয়ে অনন্তকাল অপেক্ষা করা হয়
  • Unit file মুছে দেওয়া হয়

একটি service বর্তমানে থেমে আছে এবং disabled. systemctl enable bazar-api চালানোর পর অবস্থা কী?

  • চলছে, এবং boot-এ চলবে
  • থেমে আছে, এবং boot-এ চলবে না
  • থেমে আছে, কিন্তু boot-এ চলবে
  • failed অবস্থায় চলে যায়

একটি অ্যাপ start-এর ১ সেকেন্ড পরে সবসময় ভেঙে পড়ে। Unit-এ Restart=on-failure, RestartSec=5, StartLimitIntervalSec=10, StartLimitBurst=5. কী ঘটবে?

  • ৩০ সেকেন্ডে unit failed হয়ে থামবে
  • একবার ভেঙেই থেমে যাবে
  • কিছুক্ষণ পরে নিজে থেকে সুস্থ হবে
  • সীমা কখনো ছোঁবে না, crash loop চলতেই থাকবে

৪ core-এর Linux server-এ load average ৮.০, কিন্তু top-এ us ৫% আর wa ৭০%. সবচেয়ে সম্ভাব্য ব্যাখ্যা কোনটি?

  • Task-গুলো I/O-র অপেক্ষায় আটকে আছে; load-এ তারাও গোনা হয়
  • CPU কম, core বাড়ানো দরকার
  • Load ৮.০ মানে CPU ৮০০% ব্যবহার হচ্ছে
  • Server ফাঁকা, এই সংখ্যা উপেক্ষা করা যায়

৪ core-এর machine-এ একটি service-এ CPUQuota=50% বসানো হলো। Service সর্বোচ্চ কতটা CPU সময় পাবে?

  • দুটি core-এর সমান সময়
  • একটি core-এর অর্ধেক সময়
  • একটি নির্দিষ্ট core-এর পুরোটা
  • অন্য service না চাইলে সীমা ছাড়াই যত খুশি

journal-এ Memory cgroup out of memory: Killed process আর unit-এর Result: oom-kill দেখা গেল। এ থেকে কী বোঝা যায়?

  • Disk-এর জায়গা ফুরিয়ে গেছে
  • একজন operator হাতে kill -9 দিয়েছেন
  • Service তার cgroup-এর memory সীমায় পৌঁছেছিল, kernel তার ভেতরের process মেরেছে
  • Service SIGTERM পেয়ে গুছিয়ে বের হয়েছে

শব্দকোষ

লেখার মধ্যে ডটেড দাগ দেওয়া শব্দে মাউস রাখুন বা চাপুন — অর্থ দেখাবে। এই পর্বের সব নতুন শব্দ এখানে একসাথে:

মনে রাখুন

  1. SIGTERM ধরে গুছিয়ে থামুন; SIGKILL ধরা যায় না, status 137.
  2. Service মানে manager-এর হাতে দেওয়া process: unit file হলো চুক্তি, start এখন, enable boot-এ, ফাইল বদলালে daemon-reload.
  3. Crash loop থামে কেবল যখন burst × (uptime + RestartSec) < window; restart-এর সংখ্যাকেও alert করুন।
  4. cgroup ক্ষতি এক ঘরে রাখে: CPUQuota এক core-এর শতাংশ, MemoryMax পেরোলে OOM kill; sandbox setting ক্ষতির পরিধি ছোট করে।
  5. Load ÷ core, available, wa, journal; অনুমান নয়, USE method ধরে এগোন।

পরের পর্বে: Service চলছে, কিন্তু server তো দূরে। পর্ব ১২-তে SSH-র key, config, tunnel আর bastion দিয়ে নিরাপদে সেখানে পৌঁছানো দেখব।

আরও পড়ুন · ABCL TECH-এ আপনার পরবর্তী পদক্ষেপ

এই পর্বটি শেয়ার করুন:
এই পর্বে ভুল পেয়েছেন? জানান