اگر یک سرور لینوکسی را مدیریت میکنید، تقریباً هر کاری که با سرویسها انجام میدهید از مسیر systemd میگذرد: بالا آوردن Nginx، ریاستارت کردن MySQL پس از تغییر تنظیمات، بررسی اینکه چرا PHP-FPM پس از ریبوت اجرا نشده است یا محدود کردن حافظه یک برنامه پرمصرف. با این حال بسیاری از مدیران سرور فقط سه دستور start، stop و restart را میشناسند و از ظرفیت واقعی systemctl استفاده نمیکنند. در این راهنما ساختار unit ها، دستورات روزمره، روش درست ویرایش سرویسها، محدودسازی منابع و عیبیابی سرویسهای خراب را با مثالهای عملی مرور میکنیم.
systemd چیست و چرا جایگزین SysV init شد؟
systemd نخستین پروسهای است که کرنل لینوکس پس از بوت اجرا میکند (PID 1) و مسئول راهاندازی، نظارت و توقف همه سرویسهای دیگر است. در SysV init قدیمی، هر سرویس یک اسکریپت شل طولانی در /etc/init.d داشت که بهصورت ترتیبی اجرا میشد و هیچ نظارت واقعی بر پروسهها وجود نداشت. systemd این اسکریپتها را با فایلهای پیکربندی اعلانی (declarative) جایگزین کرد، سرویسها را موازی بالا میآورد و با cgroup ها دقیقاً میداند هر سرویس کدام پروسهها را ساخته است. Ubuntu 24.04، Debian 12، AlmaLinux و Rocky Linux همگی بهطور پیشفرض از systemd استفاده میکنند.
Unit چیست؟
در systemd هر چیزی که مدیریت میشود یک unit نام دارد و با یک فایل متنی توصیف میشود. پسوند فایل نوع آن را مشخص میکند: nginx.service یک سرویس، sshd.socket یک سوکت گوشدهنده، backup.timer یک زمانبند و multi-user.target گروهی از unit ها است. فایلهای بستهها در /usr/lib/systemd/system قرار میگیرند و فایلهایی که مدیر سرور میسازد در /etc/systemd/system؛ مسیر دوم همواره بر مسیر اول اولویت دارد.
دستورات روزمره systemctl برای کنترل سرویسها
ابزار خط فرمان systemctl رابط اصلی شما با systemd است. جدول زیر دستورات پرکاربرد را نشان میدهد؛ در همه آنها میتوانید پسوند .service را حذف کنید.
| دستور | کاربرد |
|---|---|
| systemctl start nginx | اجرای فوری سرویس بدون تأثیر بر بوت بعدی |
| systemctl stop nginx | توقف سرویس و همه پروسههای فرزند آن |
| systemctl restart nginx | توقف کامل و اجرای مجدد؛ اتصالهای فعال قطع میشوند |
| systemctl reload nginx | بارگذاری مجدد پیکربندی بدون قطع اتصالها (اگر سرویس پشتیبانی کند) |
| systemctl enable --now nginx | فعالسازی در بوت و اجرای همزمان |
| systemctl status nginx | وضعیت، PID، مصرف حافظه و آخرین خطوط لاگ |
تفاوت enable و start
رایجترین اشتباه مدیران تازهکار این است که سرویسی را با start اجرا میکنند و پس از ریبوت متوجه میشوند بالا نیامده است. دستور start فقط وضعیت فعلی را تغییر میدهد، در حالی که enable یک symlink در پوشه wants/ هدف مربوطه میسازد تا سرویس در بوتهای بعدی هم اجرا شود. برای سرویسهای حیاتی همواره از enable --now استفاده کنید. دستور mask نیز با لینک کردن unit به /dev/null، اجرای آن را حتی بهعنوان وابستگی سرویس دیگر غیرممکن میکند.
خواندن درست خروجی status
در خروجی systemctl status، عبارت Loaded نشان میدهد فایل unit از کجا خوانده شده و آیا enabled است؛ Active وضعیت فعلی (active، failed یا activating) را میگوید و CGroup فهرست همه پروسههای سرویس است. ده خط پایانی آخرین پیامهای journald هستند که معمولاً اولین سرنخ خطا را میدهند.
ساختار فایل unit و معنای هر بخش
یک فایل service سه بخش اصلی دارد: [Unit] برای توضیحات و وابستگیها، [Service] برای نحوه اجرا و [Install] برای رفتار هنگام enable. نمونه زیر یک سرویس ساده Node.js است که با کاربر غیر root اجرا میشود و در صورت کرش دوباره بالا میآید:
[Unit]
Description=Sample Node.js app
After=network-online.target
Wants=network-online.target
[Service]
Type=simple
User=app
WorkingDirectory=/srv/app
ExecStart=/usr/bin/node /srv/app/server.js
Restart=on-failure
RestartSec=5
[Install]
WantedBy=multi-user.target
وابستگیها: After، Requires و Wants
دستور After فقط ترتیب را مشخص میکند و به معنای وابستگی نیست؛ اگر میخواهید سرویس شما بدون دیتابیس اصلاً اجرا نشود، باید Requires=mysql.service را هم اضافه کنید. Wants نسخه نرمتر Requires است: تلاش میکند وابستگی را بالا بیاورد اما در صورت شکست، سرویس شما را متوقف نمیکند. برای اغلب اپلیکیشنهای وب ترکیب After و Wants امنتر است، چون یک خطای موقت باعث از کار افتادن زنجیرهای سرویسها نمیشود.
انتخاب Type مناسب
مقدار Type به systemd میگوید چه زمانی سرویس را «آماده» تلقی کند. Type=simple (پیشفرض) بلافاصله پس از اجرای ExecStart سرویس را فعال میداند؛ Type=forking برای دیمنهای قدیمی است که خودشان به پسزمینه میروند؛ Type=oneshot برای اسکریپتهای یکباره؛ و Type=notify برای برنامههایی مانند PostgreSQL است که با sd_notify آمادگی خود را اعلام میکنند. انتخاب اشتباه Type رایجترین دلیل وضعیت «activating» بیپایان یا خطای timeout است.
ویرایش امن سرویسها با drop-in و override
هرگز فایلهای unit داخل /usr/lib/systemd/system را مستقیماً ویرایش نکنید؛ با اولین بهروزرسانی بسته، تغییرات شما بازنویسی میشود. روش درست استفاده از drop-in است: دستور systemctl edit nginx یک فایل override.conf در /etc/systemd/system/nginx.service.d/ میسازد و فقط گزینههایی را که در آن مینویسید روی unit اصلی اعمال میکند. برای جایگزینی کل فایل از systemctl edit --full استفاده کنید.
systemctl edit nginx
# override.conf
[Service]
LimitNOFILE=65535
Restart=always
RestartSec=3
فراموش نکنید: daemon-reload
پس از هر تغییر دستی در فایلهای unit باید systemctl daemon-reload را اجرا کنید تا systemd پیکربندی جدید را بخواند؛ systemctl edit این کار را خودکار انجام میدهد اما ویرایش با nano یا vim خیر. اگر این مرحله فراموش شود، systemd هشدار «unit file changed on disk» میدهد و با تنظیمات قدیمی کار میکند. برای تغییر ExecStart در drop-in، ابتدا آن را با یک خط خالی (ExecStart=) ریست کنید، وگرنه مقدار دوم به جای جایگزینی اضافه میشود.
محدودسازی منابع و ایزولهسازی سرویسها
یکی از مزیتهای کمتر استفادهشده systemd این است که به لطف cgroup ها میتواند مصرف منابع هر سرویس را بدون ابزار جانبی محدود کند. در سرورهای مجازی که چند سرویس بر سر حافظه محدود رقابت میکنند، میتوانید مانع شوید که یک پروسه بکاپ یا worker صف، حافظه را تا حدی مصرف کند که OOM Killer دیتابیس را بکشد.
محدودیت CPU و حافظه
[Service]
MemoryMax=1G
MemoryHigh=800M
CPUQuota=150%
TasksMax=512
IOWeight=50
MemoryHigh آستانه نرمی است که پس از آن کرنل شروع به فشار آوردن بر سرویس میکند و MemoryMax سقف سختی است که عبور از آن به کشته شدن پروسه میانجامد. CPUQuota=150% یعنی حداکثر یکونیم هسته. برای مشاهده مصرف لحظهای همه سرویسها به تفکیک cgroup، دستور systemd-cgtop را اجرا کنید.
گزینههای امنیتی (sandboxing)
با چند خط میتوانید دسترسی یک سرویس به سیستم را به شدت محدود کنید: ProtectSystem=strict کل فایلسیستم را فقطخواندنی میکند و با ReadWritePaths= فقط مسیرهای لازم را باز میکنید؛ PrivateTmp=yes پوشه /tmp جداگانه میدهد؛ و NoNewPrivileges=yes مانع افزایش سطح دسترسی از طریق setuid میشود. دستور systemd-analyze security nginx به هر سرویس نمرهای از 0 تا 10 میدهد و میگوید کدام گزینهها تنظیم نشدهاند. این گزینهها را ابتدا در محیط تست بررسی کنید.
عیبیابی سرویسهایی که بالا نمیآیند
وقتی سرویسی در وضعیت failed قرار میگیرد، یک روند ثابت را دنبال کنید:
- با systemctl --failed فهرست همه سرویسهای خراب را ببینید.
- systemctl status nginx -l --no-pager را اجرا کنید و به کد خروج و پیام آخر دقت کنید.
- لاگ کامل را با journalctl -u nginx -b -n 100 بخوانید؛ خطاهای پیکربندی معمولاً اینجا بهوضوح نوشته شدهاند.
- پیکربندی برنامه را با ابزار خودش تست کنید (nginx -t یا php-fpm -t).
- پس از رفع مشکل با systemctl reset-failed وضعیت را پاک و سرویس را دوباره اجرا کنید.
کند بودن بوت و زنجیره وابستگیها
اگر سرور پس از ریبوت دیر در دسترس قرار میگیرد، systemd-analyze blame زمان راهاندازی هر unit را به ترتیب نزولی نشان میدهد و systemd-analyze critical-chain زنجیره بحرانی وابستگیها را ترسیم میکند. در سرورهای مجازی معمولاً انتظار برای شبکه (systemd-networkd-wait-online) یا cloud-init بیشترین زمان را میگیرد.
ریاستارت خودکار با Restart=
با Restart=on-failure سرویس فقط هنگام خروج با خطا دوباره اجرا میشود و با Restart=always حتی پس از توقف عادی. دو گزینه StartLimitIntervalSec و StartLimitBurst تعیین میکنند چند بار در چه بازهای این تلاش تکرار شود (پیشفرض پنج تلاش در 10s). اگر سرویسهای یک سرور مجازی پس از هر قطعی شبکه نیاز به ریاستارت دستی دارند، این تنظیمات نخستین جایی است که باید بازبینی شود.
جمعبندی
systemd چیزی فراتر از یک جایگزین برای اسکریپتهای init است: با unit های اعلانی، drop-in های امن، محدودیت منابع مبتنی بر cgroup و لاگ یکپارچه در journald، ابزاری کامل برای مدیریت چرخه عمر سرویسها فراهم میکند. اگر عادت کنید سرویسها را با enable --now فعال کنید، تغییرات را فقط با systemctl edit اعمال کنید و هنگام خطا ابتدا journalctl -u را بخوانید، بخش بزرگی از مشکلات روزمره سرور در چند دقیقه حل میشود. برای سرورهایی که وقت مدیریت آنها را ندارید، سرویس مدیریت سرور میتواند این کارها را بر عهده بگیرد.
سوالات متداول
تفاوت systemctl restart و systemctl reload چیست؟
restart سرویس را کاملاً متوقف و دوباره اجرا میکند و اتصالهای فعال قطع میشوند. reload فقط پیکربندی را دوباره بارگذاری میکند و پروسه اصلی زنده میماند؛ برای Nginx و PHP-FPM این گزینه بدون قطعی است. اگر سرویسی ExecReload نداشته باشد، از reload-or-restart استفاده کنید.
چرا سرویس بعد از ریبوت اجرا نمیشود؟
در اغلب موارد سرویس فقط start شده و enable نشده است. با systemctl is-enabled nginx بررسی کنید و در صورت نیاز systemctl enable nginx را اجرا کنید. دلیل دیگر میتواند وابستگی به شبکه باشد که باید با After=network-online.target در فایل unit تعریف شود.
فایلهای unit سفارشی را کجا بسازیم؟
فایلهای خودتان را همواره در /etc/systemd/system قرار دهید، نه در /usr/lib/systemd/system. مسیر /etc بر مسیر بستهها اولویت دارد و با بهروزرسانی سیستم بازنویسی نمیشود. پس از ساخت فایل، systemctl daemon-reload را فراموش نکنید.
چگونه حافظه مصرفی یک سرویس را محدود کنیم؟
با systemctl edit نامسرویس یک drop-in بسازید و در بخش [Service] مقدار MemoryMax را تنظیم کنید، مثلاً MemoryMax=512M. systemd با cgroup این محدودیت را اعمال میکند و در صورت عبور، پروسه را میکشد. برای فشار نرم پیش از رسیدن به سقف، MemoryHigh را کمی پایینتر تنظیم کنید.