systemd service Type: simple, forking, notify এর পার্থক্য
আপনার systemd সার্ভিস active দেখালেও প্রসেসটি বন্ধ হয়ে যাচ্ছে? simple, forking, notify এবং oneshot এর সঠিক ব্যবহার জানুন এবং মেইন PID ট্র্যাক করার সঠিক পদ্ধতিটি শিখুন।
কেন systemd একটি ইউনিটকে active হিসেবে দেখায় যখন প্রসেসটি মারা গেছে
একটি systemd service ইউনিট ততক্ষণ active থাকে যতক্ষণ পর্যন্ত systemd-এর নির্ধারিত মেইন প্রসেসটি সচল থাকে, এবং [Service] সেকশনের Type= নির্ধারণ করে সেই প্রসেসটি কোনটি। ভুল মান নির্বাচন করলে systemd একটি শেল র্যাপার বা স্বল্পস্থায়ী প্যারেন্ট প্রসেসকে পর্যবেক্ষণ করতে থাকে, অথচ আপনার কাঙ্ক্ষিত ডেমোনটি একই ইউনিটের ভেতরেই মারা যায়। ইউনিটটি আপনাকে সেই প্রসেস সম্পর্কেই তথ্য দিচ্ছে যা তাকে পর্যবেক্ষণ করতে বলা হয়েছে।
এখানে restart policy পরিবর্তন করে কোনো লাভ হবে না। Restart= তখনই কাজ করে যখন মেইন প্রসেসটি বন্ধ হয়, তাই যতক্ষণ মেইন PID (process identifier) এমন কোনো কিছুর অধীনে থাকে যা এখনো চলছে, ততক্ষণ Restart=always কার্যকর হয় না। প্রথমে Type= ঠিক করুন। মেইন প্রসেসটি প্রকৃতপক্ষে বন্ধ হওয়ার পর systemd কী করবে তা একটি আলাদা সিদ্ধান্ত, যা Restart= এবং RestartSec=-এর নির্দেশিকা-তে আলোচনা করা হয়েছে।
Type= আসলে কী নির্ধারণ করে
প্রতিটি Type= মান একই সাথে দুটি প্রশ্নের উত্তর দেয়। systemd কখন একটি ইউনিটকে 'started' বা চালু হিসেবে গণ্য করবে এবং কোনটি মূল প্রসেস হবে।
প্রথম উত্তরটি অর্ডারিং বা ক্রম নিয়ন্ত্রণ করে। যে ইউনিট After=-এ আপনার ইউনিটের নাম উল্লেখ করে, সেটি systemd আপনার ইউনিটকে চালু ঘোষণা করা পর্যন্ত অপেক্ষা করে। একটি Type= যদি খুব দ্রুত "started" রিপোর্ট করে, তবে আপনার সার্ভিস প্রস্তুত হওয়ার আগেই নির্ভরশীল ইউনিটগুলো চলতে শুরু করতে পারে।
দ্বিতীয় উত্তরটি সুপারভিশন বা তদারকি নিয়ন্ত্রণ করে। systemd একটি ইউনিটের মাধ্যমে চালু হওয়া প্রতিটি প্রসেসকে একটি cgroup (control group)-এ রাখে। এটি কার্নেলের একটি ফিচার যা প্রসেসগুলোকে একত্রিত করে, যাতে সেগুলোকে একসাথে সীমাবদ্ধ করা বা বন্ধ করা যায়। এই cgroup-এর মাধ্যমেই systemctl stop ক্লিনআপ বা পরিষ্কারের কাজ করে: KillMode= ডিফল্টভাবে control-group থাকে, তাই একটি ইউনিট বন্ধ করলে এর ভেতরের প্রতিটি প্রসেসে সিগন্যাল পাঠানো হয়। মূল PID বা মেইন প্রসেস আইডি বিষয়টি আরও সুনির্দিষ্ট। এটি সেই একক প্রসেস যার সমাপ্তি পুরো ইউনিটটিকে বন্ধ করে দেয় এবং যার এক্সিট স্ট্যাটাস ইউনিটের ফলাফল হিসেবে গণ্য হয়। cgroup-কে মূল PID হিসেবে ভুলভাবে বিবেচনা করাই বিভ্রান্তির মূল কারণ।
Type=simple রিপোর্ট করে যে বাইনারি চলার আগেই সার্ভিস শুরু হয়েছে
Type=simple হলো ডিফল্ট মোড যখন ExecStart= সেট করা থাকে এবং Type= বা BusName= এর কোনোটিই উপস্থিত থাকে না। systemd প্রসেসটি তৈরি করে, ইউনিটটিকে সাথে সাথে শুরু হয়েছে বলে ধরে নেয় এবং সেই প্রসেসটিকে মেইন PID হিসেবে গণ্য করে। সার্ভিস বাইনারি কার্যকর হওয়ার আগেই পরবর্তী ইউনিটগুলো কাজ শুরু করে দেয়।
এই শেষ বিষয়টি একটি সাধারণ বিভ্রান্তির কারণ ব্যাখ্যা করে। ExecStart= পাথে কোনো টাইপো থাকলেও একটি স্টার্ট জব সফলভাবে সম্পন্ন হয়, এবং বাইনারি কার্যকর করতে ব্যর্থ হলে কিছুক্ষণ পরেই ত্রুটি দেখা দেয়। systemd এই ক্ষেত্রটিকে 203 এক্সিট কোড দিয়ে রেকর্ড করে, যাকে তাদের নিজস্ব টেবিলে EXEC হিসেবে চিহ্নিত করা হয়েছে এবং এটি সার্ভিস বাইনারি কার্যকর করতে ব্যর্থ হওয়াকে নির্দেশ করে। সুতরাং, systemctl start কোনো ত্রুটি ছাড়া সম্পন্ন হওয়া মানেই এই নয় যে আপনার বাইনারিটি বিদ্যমান।
যেসব প্রোগ্রাম ফোরগ্রাউন্ডে থাকে এবং কখনোই ব্যাকগ্রাউন্ডে যায় না, সেগুলোর জন্য simple ব্যবহার করুন। আধুনিক বেশিরভাগ ডেমন এবং আপনার নিজের লেখা প্রায় যেকোনো প্রোগ্রামের জন্য এটি প্রযোজ্য।
Type=exec প্রোগ্রামটি প্রকৃতপক্ষে চালু হওয়া পর্যন্ত অপেক্ষা করে
Type=exec হলো simple-এর একটি উন্নত সংস্করণ। systemd তখনই ইউনিটটিকে চালু হিসেবে গণ্য করে যখন fork এবং binary-এর execution উভয়ই সফল হয়। কোনো binary অনুপস্থিত থাকলে বা কোনো User= সমাধান করা না গেলে, সেটি এখন সফল হওয়ার রিপোর্ট না দিয়ে সরাসরি start job-কে ব্যর্থ করে দেয়, যা আগে নীরবে ব্যর্থ হতো।
Type=exec প্রথম systemd 240-এ যুক্ত করা হয়, তাই বর্তমানে প্রচলিত প্রতিটি সার্ভার ডিস্ট্রিবিউশনে এটি রয়েছে। আগস্ট 2026 অনুযায়ী, Ubuntu 24.04-এ systemd 255 এবং Debian 13-এ systemd 257 ব্যবহৃত হচ্ছে। আপনার ভার্সনটি systemctl --version দিয়ে যাচাই করুন।
এর ফলে শুরুতে একটি অতিরিক্ত সিঙ্ক্রোনাইজেশন ধাপের প্রয়োজন হয়। তবে এর বিনিময়ে systemctl start থেকে একটি সঠিক exit status পাওয়া যায়। foreground প্রোগ্রামের ক্ষেত্রে simple-এর পরিবর্তে exec ব্যবহার করা শ্রেয়।
Type=forking এবং যেভাবে main PID হারিয়ে যায়
Type=forking systemd-কে জানায় যে ExecStart=-এ থাকা প্রসেসটি একটি child প্রসেস তৈরি করবে এবং তারপর ইচ্ছাকৃতভাবে বন্ধ হয়ে যাবে। systemd সেই প্রথম প্রসেসটির বন্ধ হওয়ার জন্য অপেক্ষা করে এবং তারপর ইউনিটটিকে started হিসেবে গণ্য করে। যে child প্রসেসটি রয়ে যায়, সেটিই হলো daemon।
এখানে মূল সমস্যা হলো পরিচয় শনাক্ত করা। systemd যে প্রসেসটি চালু করেছিল সেটি আর নেই, তাই systemd-কে খুঁজে বের করতে হয় যে টিকে থাকা প্রসেসগুলোর মধ্যে কোনটি মূল প্রসেস। PIDFile=-এ সেই ফাইলটির পাথ সেট করুন যেখানে daemon তার PID লেখে, সাধারণত এটি /run-এর অধীনে থাকে। systemd সেই ফাইল থেকে PID পড়ে নেয়। systemd এটিও যাচাই করে যে, ফাইলে থাকা PID-টি এই সার্ভিসেরই কোনো প্রসেসের কি না। ফলে, পুরনো কোনো ফাইলে অন্য কোনো প্রসেসের PID থাকলেও systemd তা গ্রহণ করে না।
PIDFile= ছাড়া GuessMainPID= কার্যকর হয় এবং এর ডিফল্ট মান হলো yes। এই অনুমান কেবল তখনই নির্ভরযোগ্য যখন সার্ভিসটি একটি মাত্র প্রসেসে স্থির থাকে। ম্যানুয়ালটিতে সীমাবদ্ধতা স্পষ্টভাবে বলা আছে: যদি daemon-এ একাধিক প্রসেস থাকে, তবে এই অনুমান ভুল হতে পারে এবং ব্যর্থতা শনাক্তকরণ (failure detection) কাজ করা বন্ধ করে দেয়। এমনও হতে পারে যে ইউনিটের main PID 0 হয়ে গেছে, যার অর্থ systemd-এর তত্ত্বাবধান করার মতো কিছুই নেই।
অধিকাংশ daemon যা fork করে, সেগুলোর foreground-এ থাকার জন্য একটি নির্দিষ্ট switch থাকে। Type=exec-এর সাথে সেই switch-টি ব্যবহার করুন এবং PIDFile= লাইনটি মুছে ফেলুন। যত কম জটিলতা থাকবে, PID হারিয়ে যাওয়ার সম্ভাবনা তত কম হবে।
Type=oneshot কাজের জন্য যা শেষ হয়ে যায়
Type=oneshot আশা করে যে প্রসেসটি চলবে এবং তারপর বন্ধ হয়ে যাবে। systemd ইউনিটটিকে তখনই শুরু হয়েছে বলে গণ্য করে যখন এটি বন্ধ হয়, যা oneshot-কে এমন যেকোনো কাজের জন্য উপযুক্ত করে তোলে যার জন্য অন্য কোনো ইউনিটের অপেক্ষা করা প্রয়োজন। যখন কোনো ইউনিটে Type= বা ExecStart= নির্দিষ্ট করা থাকে না, তখন এটিই ডিফল্ট হিসেবে কাজ করে।
oneshot-এর ক্ষেত্রে দুটি আচরণ বিশেষভাবে প্রযোজ্য। এটিই একমাত্র টাইপ যা একাধিক ExecStart= লাইন গ্রহণ করে এবং সেই লাইনগুলো ক্রমানুসারে কার্যকর হয়। এর স্টার্ট টাইমআউট ডিফল্টভাবে নিষ্ক্রিয় থাকে, তাই কোনো oneshot যদি আটকে যায় তবে সেটি অনির্দিষ্টকাল অপেক্ষা করবে, যদি না আপনি নিজে TimeoutStartSec= সেট করেন।
প্রসেসটি বন্ধ হওয়ার পর, ইউনিটটি inactive অবস্থায় ফিরে আসে। RemainAfterExit=yes এটিকে কোনো প্রসেস চলমান না থাকা সত্ত্বেও active অবস্থায় রাখে। এই পৃষ্ঠার শুরুতে উল্লিখিত সমস্যার এটিই হলো পরিকল্পিত সংস্করণ, এবং যখন ইউনিটের কাজ কোনো কিছু চলমান রাখা নয় বরং কোনো অবস্থা (state) রেখে যাওয়া হয়, তখন এটি সঠিক পদ্ধতি: যেমন ফায়ারওয়াল রুলসেট লোড করা বা কন্টেইনার স্ট্যাক চালু করা। এটি reboot-এর পর ফিরে আসা একটি Docker Compose stack-এর পেছনের প্যাটার্ন, যেখানে ইউনিটটি compose কমান্ড চালায়, বন্ধ হয়ে যায় এবং সক্রিয় থাকে কারণ এটি যে কন্টেইনারগুলো শুরু করেছিল সেগুলো ইউনিটের চেয়েও দীর্ঘস্থায়ী হয়। একটি oneshot ইউনিট হলো সেটি যা কোনো শিডিউল ট্রিগার করে, যা cron-এর পরিবর্তে systemd টাইমার ব্যবহার করে কোনো জব চালানো-এর অন্য অর্ধেক।
Type=notify এর মাধ্যমে সার্ভিস নিজেই জানায় যে সেটি প্রস্তুত
Type=notify সিদ্ধান্ত নেওয়ার দায়িত্ব সার্ভিসটির ওপর ছেড়ে দেয়। সার্ভিসটি যতক্ষণ না NOTIFY_SOCKET এনভায়রনমেন্ট ভেরিয়েবলে প্রাপ্ত Unix socket-এর মাধ্যমে READY=1 বার্তা পাঠাচ্ছে, ততক্ষণ systemd স্টার্ট জবটি চালু রাখে। এর C ইন্টারফেস হলো sd_notify(3), যা অনেক সার্ভার ইতিমধ্যেই সমর্থন করে।
"সার্ভিসটি কি চালু হয়েছে?" - এই প্রশ্নের এটিই সঠিক উত্তর। simple এবং exec রিপোর্ট করে যে সার্ভিসটি চালু হয়েছে, কিন্তু তখনো সার্ভিসটি তার কনফিগারেশন পড়া বা লিসেনিং সকেট খোলার কাজ শেষ নাও করতে পারে। ফলে কোনো ডিপেন্ডেন্ট ইউনিট খুব দ্রুত চালু হয়ে প্রথম কানেকশন তৈরিতে ব্যর্থ হতে পারে। notify রিপোর্ট করে যে সার্ভিসটি ঠিক সেই মুহূর্তে চালু হয়েছে যখন সার্ভিসটি নিজে জানায় যে সেটি প্রস্তুত।
systemd শুধুমাত্র মেইন প্রসেস থেকে আসা বার্তা গ্রহণ করে, যার অর্থ হলো NotifyAccess=main, এবং Type=notify এটিই নির্দেশ করে। যদি বার্তাটি কোনো চাইল্ড বা হেল্পার প্রসেস থেকে আসে, তবে NotifyAccess=all সেট করুন। একটি শেল স্ক্রিপ্ট systemd-notify --ready কল করতে পারে, কিন্তু এটি একটি আলাদা স্বল্পস্থায়ী প্রসেস হিসেবে চলে, তাই এর জন্য NotifyAccess=all প্রয়োজন হয়। এছাড়া, যে সেন্ডার ইতিমধ্যে এক্সিট করেছে, তার পাঠানো বার্তা systemd শনাক্ত করতে ব্যর্থ হতে পারে। যে সার্ভিস সরাসরি এই প্রোটোকল ব্যবহার করে, সেটি অনেক বেশি নির্ভরযোগ্য।
দুটি সম্পর্কিত সেটিংস জেনে রাখা ভালো। Type=notify-reload, যা systemd 253 থেকে পাওয়া যাচ্ছে, রিলোডের ক্ষেত্রেও একই হ্যান্ডশেক প্রক্রিয়া প্রসারিত করে। ফলে systemctl reload তখনই রিটার্ন করে যখন সার্ভিসটি রিলোড শেষ হওয়ার রিপোর্ট দেয়, সিগন্যাল পাঠানোর পরপরই নয়। WatchdogSec= একটি নোটিফাইং সার্ভিসকে নির্দিষ্ট বিরতিতে কিপ-অ্যালাইভ বার্তা পাঠাতে বলে, এবং কোনো ডেডলাইন মিস হলে systemd সেটিকে ব্যর্থতা হিসেবে গণ্য করে।
Type=dbus এবং Type=idle
Type=dbus ততক্ষণ অপেক্ষা করে যতক্ষণ না সার্ভিসটি D-Bus-এ একটি নাম গ্রহণ করে, যা হলো এমন একটি মেসেজ বাস যা সিস্টেম এবং ডেস্কটপ সার্ভিসগুলো একে অপরের সাথে যোগাযোগের জন্য ব্যবহার করে। এর জন্য BusName= প্রয়োজন, এবং BusName= সেট করা মাত্রই এটি ডিফল্ট হিসেবে কার্যকর হয়। এটি শুধুমাত্র সেই সার্ভিসগুলোর জন্য ব্যবহার করুন যা প্রকৃতপক্ষে একটি বাস নাম রেজিস্টার করে।
Type=idle অনেকটা simple-এর মতো কাজ করে, কিন্তু এটি প্রোগ্রাম চালানোকে ততক্ষণ বিলম্বিত করে যতক্ষণ না কিউতে থাকা জবগুলো সম্পন্ন হয়, যার সর্বোচ্চ সময়সীমা পাঁচ সেকেন্ড। এটি এই উদ্দেশ্যে রাখা হয়েছে যাতে বুট হওয়ার সময় কনসোল আউটপুট স্ট্যাটাস মেসেজের সাথে মিশে না যায়। এটি কোনো অর্ডারিং টুল নয় এবং সাধারণ কোনো সার্ভিসে এটি ব্যবহার করা উচিত নয়।
কেন একটি র্যাপার স্ক্রিপ্ট systemd-কে ভুল PID-তে আটকে রাখে
এখানে সেই কাঠামোটি দেওয়া হলো যা মূল সমস্যাটি তৈরি করে।
[Service]
Type=simple
ExecStart=/opt/app/run.sh#!/bin/bash
export APP_ENV=production
/opt/app/bin/server --config /etc/app.yaml &
/opt/app/bin/exporter --port 9101systemd শেলটিকে মূল PID হিসেবে রেকর্ড করে। যখন exporter ফোরগ্রাউন্ডে চলে, তখন শেলটি জীবিত থাকে। যদি server মারা যায়, শেলটি তা বুঝতে পারে না, তাই মূল PID জীবিত থাকে, ইউনিটটি এখনও active অবস্থায় থাকে এবং Restart=-এর কাজ করার মতো কিছু থাকে না। উভয় প্রসেসই পুরো সময় ইউনিটের cgroup-এ অবস্থান করে, তাই systemctl stop এখনও সঠিকভাবে ক্লিনআপ করতে পারে। এখানে সুপারভিশন ভেঙে গেছে, ক্লিনআপ নয়।
সমাধানটি নির্ভর করে ইউনিটে আসলে কতগুলো দীর্ঘস্থায়ী প্রসেস চলছে তার ওপর।
যদি একটি মাত্র প্রসেস থাকে, তবে শেলটিকে সেটি দিয়ে প্রতিস্থাপন করুন।
#!/bin/bash
export APP_ENV=production
exec /opt/app/bin/server --config /etc/app.yamlexec শেলটিকে নির্দিষ্ট প্রোগ্রাম দিয়ে প্রতিস্থাপন করে এবং একই PID বজায় রাখে, তাই systemd যে PID রেকর্ড করেছিল তা এখন ডেমনের অধীনে থাকে। আরও ভালো হয় যদি র্যাপারটি সরিয়ে ফেলেন। Environment= এবং EnvironmentFile= ভেরিয়েবলগুলো বহন করে এবং ExecStartPre= সেটআপ ধাপটি সম্পন্ন করে, ফলে systemd সরাসরি ডেমোনটি চালু করতে পারে এবং গঠনের মাধ্যমেই এর PID জানতে পারে।
যদি দুটি প্রসেস থাকে, তবে কোনো একক PID ইউনিটটিকে প্রতিনিধিত্ব করে না। সেগুলোকে দুটি ইউনিটে ভাগ করুন এবং After= ও Wants= দিয়ে সেগুলোর ক্রম নির্ধারণ করুন। প্রতি প্রসেসে একটি ইউনিট হলো সেই বিন্যাস যা systemd ভালোভাবে সুপারভাইজ করতে পারে এবং এটিই একমাত্র উপায় যার মাধ্যমে প্রতিটি প্রসেস নিজস্ব রিস্টার্ট আচরণ পায়।
ExitType=cgroup পরিবর্তনের কারণ
ExitType= systemd 250 সংস্করণে যুক্ত করা হয়েছে। এর ডিফল্ট মান হলো main: যখন মূল প্রসেসটি বন্ধ হয়ে যায়, তখন ইউনিটটিকেও বন্ধ হিসেবে গণ্য করা হয়। ExitType=cgroup ব্যবহার করলে, যতক্ষণ পর্যন্ত cgroup-এর ভেতরে কোনো প্রসেস সচল থাকে, ততক্ষণ ইউনিটটিকে সচল হিসেবে গণ্য করা হয়।
[Service]
Type=simple
ExitType=cgroup
ExecStart=/opt/app/launcherএটি একটি নির্দিষ্ট সমস্যার সমাধান করে। যদি কোনো লঞ্চার প্রোগ্রাম মূল কাজটি শুরু করে নিজে বন্ধ হয়ে যায়, তবে ExitType=main-এর অধীনে systemd ইউনিটটিকে বন্ধ মনে করে এবং বাকি প্রসেসগুলোকে কিল (kill) করে দেয়। ExitType=cgroup ব্যবহার করলে ইউনিটটি পুরো গ্রুপটিকে অনুসরণ করে।
এটি কী সমাধান করে না, সে বিষয়ে স্পষ্ট ধারণা থাকা প্রয়োজন। ExitType=cgroup ইউনিটটিকে ততক্ষণ সচল রাখে যতক্ষণ অন্তত একটি প্রসেস জীবিত থাকে, তাই একটি ইউনিটে দুটি ডেমোন (daemon) থাকলে একটি মারা গেলেও ইউনিটটি সচল থাকে। এটি লঞ্চারের সমস্যাটি সমাধান করে। কিন্তু এটি একটি ইউনিটকে একাধিক স্বাধীন প্রসেসের সুপারভাইজারে পরিণত করে না। ExitType=-কে Type=oneshot-এর সাথে ব্যবহার করা যায় না।
রিসোর্স অ্যাকাউন্টিং cgroup-এর মাধ্যমেই হয়, তাই MemoryMax= এবং CPUQuota=-এর মতো সীমাবদ্ধতাগুলো ইউনিট দ্বারা তৈরি প্রতিটি প্রসেসের ওপর প্রযোজ্য হয়, মূল PID সম্পর্কে Type= যা-ই বলুক না কেন। এই বিষয়টি সম্পর্কে বিস্তারিত জানতে দেখুন capping a service's memory and CPU with systemd।
How to find the process systemd is actually watching
Work through this on the unit you are debugging, in order. Read what systemd loaded, then read what it tracks, then compare that with the process table.
systemctl cat app.service
systemctl show -p Type,ExitType,GuessMainPID,PIDFile,MainPID,RemainAfterExit app.servicesystemctl cat prints the unit file together with every drop-in that applies to it, so you are reading what systemd loaded rather than the file you remember editing. systemctl show prints the effective values, including the defaults you never wrote down. Note the value of MainPID before moving on.
systemd-cgls --unit=app.service
ps -o pid,ppid,stat,etime,args -p "$(systemctl show -p MainPID --value app.service)"systemd-cgls lists every process in the unit's cgroup. The ps line describes the single process systemd supervises. Read the two together. A MainPID of 0 means systemd has no process to watch. A MainPID that resolves to a shell while the cgroup also holds your daemon is the wrapper case above. A cgroup with more processes than you expected means a launcher or a forking daemon is involved.
systemctl status app.service
journalctl -u app.service -bsystemctl status prints the state line and the cgroup tree together, so it often answers both questions at once. journalctl -u limited to this boot with -b shows the start and stop events systemd recorded for the unit, with the exit codes it saw. If the daemon writes to its own log file instead of the journal, read that file as well, because systemd can only record what reached it.
When you change Type=, reload and restart.
systemd-analyze verify /etc/systemd/system/app.service
sudo systemctl daemon-reload
sudo systemctl restart app.servicesystemd-analyze verify parses the file and reports settings it cannot accept. daemon-reload makes systemd re-read unit files from disk. A changed Type= does not apply to an already running unit, so the restart is required, not optional.
Then test the change. Take the PID of the process you actually care about from systemd-cgls and kill it. Run systemctl is-active app.service straight after. If Type= is right, the unit leaves the active state. If it stays active, systemd is still watching something else.
কোন systemd service Type= ব্যবহার করা উচিত
- যে প্রোগ্রামটি ফোরগ্রাউন্ডে চালু থাকে:
Type=exec। - যে প্রোগ্রামটি readiness notification সমর্থন করে:
Type=notify, এবং যদি এটি রিলোড নিশ্চিত করতে পারে তবেnotify-reload। - যে ডেমোনটি ব্যাকগ্রাউন্ডে চলে যেতে চায়:
Type=forkingএর সাথেPIDFile=, অথবা এর ফোরগ্রাউন্ড সুইচসহType=exec। - যে স্ক্রিপ্টটি কাজ শেষ করে বেরিয়ে যায়:
Type=oneshot, এবং যদি উদ্দেশ্য হয় স্টেট বজায় রাখা তবেRemainAfterExit=yes। - যে লঞ্চারটি বেরিয়ে যায় কিন্তু এর চাইল্ড প্রসেসগুলো চলতে থাকে:
Type=simpleএর সাথেExitType=cgroup।
কোনো থার্ড-পার্টি ডেমোনের জন্য কোনটি প্রয়োজন তা নিশ্চিত না হলে, প্রথমে এর প্যাকেজড ইউনিট ফাইলটি পড়ুন। ডিস্ট্রিবিউশন থেকে আসা কোনো ইউনিটে systemctl cat চালালে আপস্ট্রিম থেকে নির্ধারিত Type= দেখা যায়, এবং সেই পছন্দটি আপনার চেয়েও বেশি মানুষের দ্বারা পরীক্ষিত।
FAQ
প্রসেস মারা যাওয়ার পরেও আমার systemd unit কেন active থাকে?
কারণ systemd যে প্রসেসটিকে মূল প্রসেস হিসেবে গণ্য করে, সেটি তখনও সচল থাকে। systemd প্রতিটি ইউনিটের cgroup-এর সব প্রসেস না দেখে, Type= অনুযায়ী প্রতিটি সার্ভিসের জন্য একটি নির্দিষ্ট PID পর্যবেক্ষণ করে। Type=simple দিয়ে শুরু করা কোনো wrapper script সাধারণত এর কারণ হয়: শেলটিই মূল PID হিসেবে থাকে, তাই শেল ব্যাকগ্রাউন্ডে যে ডেমোন চালু করে সেটি বন্ধ হয়ে গেলেও ইউনিটটি active থেকে যায়। systemctl show -p MainPID app.service চালান, তারপর systemd-cgls --unit=app.service দিয়ে ইউনিটের cgroup তালিকা দেখুন এবং দুটির মধ্যে তুলনা করুন।
Type=simple এবং Type=exec এর মধ্যে পার্থক্য কী?
Type=simple ইউনিটটিকে তখনই চালু হয়েছে বলে ধরে নেয় যখন systemd প্রসেসটি তৈরি করে, বাইনারিটি এক্সিকিউট হওয়ার আগেই। তাই ExecStart=-এ ভুল পাথ থাকলে সেটি সফলভাবে চালু হয়েছে বলে দেখায় এবং পরে ব্যর্থ হয়। Type=exec এক্সিকিউশন সফল হওয়া পর্যন্ত অপেক্ষা করে, ফলে ব্যর্থতা সরাসরি স্টার্ট জব থেকেই রিপোর্ট করা হয়। উভয়ই একই প্রসেসকে মূল PID হিসেবে গণ্য করে। Type=exec ব্যবহারের জন্য systemd 240 বা তার পরবর্তী সংস্করণ প্রয়োজন।
Type=forking এর সাথে কি আমার এখনও PIDFile= প্রয়োজন?
হ্যাঁ, যখনই ডেমোন কোনো ফাইল লেখে। এটি ছাড়া systemd GuessMainPID=-এ ফিরে যায়, যা একটি অনুমান মাত্র এবং শুধুমাত্র সেই সব সার্ভিসের জন্য নির্ভরযোগ্য যা একটি মাত্র প্রসেসে স্থির হয়। যখন এই অনুমান ভুল হয় বা অসম্ভব হয়, তখন সেই ইউনিটের জন্য ব্যর্থতা শনাক্তকরণ এবং স্বয়ংক্রিয়ভাবে রিস্টার্ট করার প্রক্রিয়া কাজ করা বন্ধ করে দেয়। PIDFile=-কে সেই সঠিক পাথে নির্দেশ করুন যেখানে ডেমোন ফাইলটি লেখে, সাধারণত এটি /run এর অধীনে থাকে।
আমার কখন RemainAfterExit=yes ব্যবহার করা উচিত?
যখন ইউনিটের উদ্দেশ্য কোনো প্রসেস চালু রাখা নয়, বরং সিস্টেমের অবস্থা পরিবর্তন করা হয়। একটি Type=oneshot ইউনিট যা ফায়ারওয়াল রুল লোড করে বা কন্টেইনার স্ট্যাক চালু করে, কাজ শেষ হওয়ার সাথে সাথেই বন্ধ হয়ে যায়। RemainAfterExit=yes ছাড়া ইউনিটটি inactive হয়ে যায়, যার ফলে systemctl stop-এর কাছে থামানোর মতো কিছু থাকে না এবং ExecStop= ক্লিনআপ চালানোর কোনো উপায় থাকে না। এটি ব্যবহার করলে কোনো প্রসেস ছাড়াই ইউনিটটি active থাকে, যা এখানে উদ্দেশ্যপ্রণোদিত।
Type= পরিবর্তন করলে কি daemon-reload প্রয়োজন?
হ্যাঁ, এবং সেই সাথে ইউনিটটি রিস্টার্ট করাও প্রয়োজন। systemctl daemon-reload ডিস্কে থাকা ইউনিট ফাইলগুলোকে systemd-কে পুনরায় পড়তে বাধ্য করে, কিন্তু একটি চলমান ইনস্ট্যান্স তার শুরুর Type= বজায় রাখে। পরীক্ষার আগে sudo systemctl daemon-reload এবং তারপর sudo systemctl restart app.service চালান, অন্যথায় আপনি পুরনো সুপারভিশন আচরণই পর্যবেক্ষণ করতে থাকবেন।