اجرای دائمی برنامه با systemd
شروع بعد از reboot، توقف درست و log قابل پیگیری
اجرای برنامه در یک ترمینال SSH با بستن session تمام میشود. systemd میتواند lifecycle برنامه را مدیریت کند، اما Restart همیشگی درمان crash نیست. یک unit خوب کاربر محدود، مسیرهای صریح، secret جدا، shutdown صحیح، log و health monitoring دارد.
پیشنیاز: برنامه باید در foreground و با کاربر محدود اجرا شود
فایل برنامه و runtime را در مسیر پایدار نصب و فرمانی را که در ترمینال با موفقیت اجرا میشود مشخص کنید. برنامه زیر systemd معمولاً نباید خودش daemonize یا fork پسزمینهای انجام دهد؛ process اصلی باید foreground بماند تا systemd PID و وضعیت واقعی را مدیریت کند. مسیر runtime را با command -v پیدا و در ExecStart بهصورت مطلق بنویسید.
یک حساب سرویس با حداقل دسترسی به فایلها، پورتها و منابع لازم در نظر بگیرید. برای گوشدادن روی پورتهای عادی نیازی به root نیست و reverse proxy میتواند TLS و پورت عمومی را مدیریت کند. برنامه را یکبار با همان User و environment هدف اجرا کنید؛ unit نباید پنهانکننده permission یا dependency ناقص باشد.
نمونه Unit پایه و قابل بازبینی
فایل را با نامی روشن مانند /etc/systemd/system/myapp.service بسازید و مسیرها و کاربر نمونه را با محیط خود تطبیق دهید:
[Unit]
Description=My application
After=network.target
[Service]
Type=simple
User=myapp
Group=myapp
WorkingDirectory=/opt/myapp
ExecStart=/usr/bin/node /opt/myapp/server.js
EnvironmentFile=-/etc/myapp/myapp.env
Restart=on-failure
RestartSec=5s
TimeoutStopSec=30s
[Install]
WantedBy=multi-user.target
علامت منفی پیش از EnvironmentFile یعنی نبودن فایل باعث failure واحد نمیشود؛ اگر environment برای اجرا اجباری است، آن را حذف کنید تا خطا آشکار باشد. secret را مستقیم داخل unit یا repository ننویسید. فایل environment باید فقط برای حسابهای لازم خواندنی باشد.
Unit را اعتبارسنجی، بارگذاری و فعال کنید
قبل از شروع، syntax و directiveهای ناشناخته را بررسی کنید. سپس manager را از فایل جدید آگاه و سرویس را هماکنون و برای bootهای بعد فعال کنید:
sudo systemd-analyze verify /etc/systemd/system/myapp.service
sudo systemctl daemon-reload
sudo systemctl enable --now myapp.service
sudo systemctl status myapp.service --no-pager
enable پیوندهای راهاندازی را طبق بخش Install میسازد و لزوماً بهتنهایی سرویس را start نمیکند؛ --now هر دو را انجام میدهد. پس از تغییر فایل unit، دوباره verify و daemon-reload لازم است. برای تغییر صرف فایل برنامه، reload manager لازم نیست؛ اما restart یا سازوکار deploy برنامه ممکن است لازم باشد.
Status و Journal را بهجای حدس بررسی کنید
خروجی stdout و stderr برنامه بهطور پیشفرض در journal ثبت میشود. برای مشاهده رخدادهای اخیر و دنبالکردن زنده:
sudo journalctl -u myapp.service -n 100 --no-pager
sudo journalctl -fu myapp.service
در status به exit code، signal، زمان restart و محدودشدن start توجه کنید. loop سریع crash ممکن است به start-limit-hit برسد؛ reset کردن وضعیت بدون رفع خطا فقط آن را تکرار میکند. log برنامه باید context کافی داشته باشد، ولی password، token و داده شخصی را ثبت نکند. retention journal را با ظرفیت دیسک هماهنگ کنید.
Restart و Shutdown را مطابق رفتار برنامه تنظیم کنید
Restart=on-failure برای process بلندمدتی که نباید پس از crash خاموش بماند معمولاً نقطه شروع مناسبی است؛ خروج عادی را دوباره اجرا نمیکند. always حتی خروج موفق را restart میکند و میتواند job یکباره یا خطای پیکربندی را وارد loop کند. RestartSec فاصله تلاشهاست، نه backoff کامل یا health check.
در stop، systemd به process اصلی signal پایان میدهد و تا TimeoutStopSec منتظر میماند. برنامه باید signal را بگیرد، پذیرش کار جدید را متوقف و اتصال/صف را با مهلت ببندد. اگر child process میسازید، مدل process و KillMode را طبق مستندات بررسی کنید؛ shell wrapper مبهم میتواند signal را به process واقعی نرساند.
Dependency و سختسازی را مرحلهای اضافه کنید
After=network.target فقط ترتیب را نسبت به پشته شبکه بیان میکند و آمادهبودن interface، DNS، API یا دیتابیس خارجی را تضمین نمیکند. اگر یک client واقعاً نمیتواند پیش از اعلام آمادگی شبکه شروع شود، Wants=network-online.target و After=network-online.target را با توجه به network manager و اثر آن بر زمان boot بررسی کنید؛ حتی در آن حالت برنامه باید timeout و retry کنترلشده داشته باشد. وابستگی محلی را با unit مناسب و رابطه Wants/Requires فقط در صورت نیاز مدل کنید؛ sleep ثابت راه مطمئنی برای readiness نیست.
directiveهایی مانند NoNewPrivileges=true، PrivateTmp=true، ProtectSystem=strict و ReadWritePaths= میتوانند سطح دسترسی را کم کنند، اما باید یکییکی در staging آزمایش شوند؛ سختسازی کور ممکن است نوشتن log، upload یا socket لازم را قطع کند. systemd-analyze security myapp.service سرنخ میدهد، نه گواهی امنیت کامل.
Deploy، health check و rollback را از service جدا نبینید
پیش از restart، نسخه تازه، dependency، migration و config را اعتبارسنجی کنید. systemctl reload فقط وقتی معنا دارد که برنامه و unit صریحاً ExecReload یا reload protocol را پشتیبانی کنند؛ در غیر این صورت restart کنترلشده لازم است. نسخه قبلی و روش بازگشت را تا پایان health check نگه دارید.
فعالبودن process به معنی سالمبودن سرویس نیست. endpoint سلامت، اتصال دیتابیس، queue lag، error rate و latency را بیرون از systemd مانیتور کنید. reboot آزمایشی در محیط مناسب نشان میدهد enable، dependency، mount، شبکه و secretها واقعاً پس از boot در دسترساند. unit و runbook را مانند کد version-control کنید، اما secret را خارج نگه دارید.
پرسشهای پرتکرار
چرا برنامه بعد از بستن SSH متوقف میشود؟
چون process به session ترمینال وابسته بوده است. آن را بهعنوان service foreground زیر systemd اجرا کنید و lifecycle را با systemctl مدیریت کنید.
Restart=always بهتر است یا on-failure؟
برای daemon معمولاً on-failure شروع محافظهکارانهتری است. always خروج موفق را نیز دوباره اجرا میکند و برای job یکباره یا خطای پایدار میتواند loop بسازد.
بعد از ویرایش service چرا تغییر اعمال نشد؟
پس از تغییر فایل unit باید systemd-analyze verify، سپس systemctl daemon-reload و در صورت نیاز restart انجام شود. تغییر فایل برنامه با daemon-reload حل نمیشود.
EnvironmentFile را کجا بگذارم؟
در مسیری خارج از repository و document root، با مالکیت و permission محدود. secretها را داخل متن unit یا source control قرار ندهید.
آیا سرویس را با root اجرا کنم تا خطای permission نداشته باشد؟
خیر. permission لازم را دقیق به کاربر سرویس بدهید. اجرای root دامنه خسارت vulnerability یا اشتباه برنامه را زیاد میکند.
چرا systemd سرویس را active نشان میدهد ولی سایت خراب است؟
active فقط زندهبودن process طبق مدل unit را نشان میدهد. health کاربردی، پورت، dependency، error rate و پاسخ HTTP باید جدا مانیتور شوند.
منابع رسمی و مطالعه بیشتر
برنامه دائمی به زیرساخت قابل پایش نیاز دارد
سیستمعامل و منابع VPS را با runtime، worker و بار واقعی برنامه تطبیق بده.