نوشتن یک سرویس systemd برای برنامهی خودتان
زمانی برنامهها را با nohup ./app & اجرا میکردم و هر بار که سرور راهاندازی دوباره میشد، یادم میرفت دوباره بالا بیاورمشان. systemd این مشکل را برای همیشه حل میکند و نوشتن یک unit ساده کمتر از پنج دقیقه وقت میگیرد.
یک unit کامل
# /etc/systemd/system/myapp.service
[Unit]
Description=My application
After=network-online.target
Wants=network-online.target
[Service]
Type=simple
User=myapp
WorkingDirectory=/opt/myapp
EnvironmentFile=/etc/myapp/env
ExecStart=/opt/myapp/bin/server --port 3000
Restart=on-failure
RestartSec=3
[Install]
WantedBy=multi-user.target
systemctl daemon-reload
systemctl enable --now myapp
systemctl status myapp
enable یعنی بعد از هر بوت اجرا شود و --now یعنی همین حالا هم شروع کند. بعد از هر تغییر در فایل unit باید daemon-reload بزنید، وگرنه systemd همان نسخهی قبلی را در حافظه دارد.
چند نکته که ارزش دانستن دارد
- برنامه را با کاربر
rootاجرا نکنید. یک کاربر سیستمی بسازید:useradd --system --no-create-home myapp. Restart=on-failureفقط وقتی برنامه با کد خطا بیرون بیاید دوباره راهش میاندازد.Restart=alwaysحتی بعد از خروج سالم هم این کار را میکند.- متغیرهای محیطی و رمزها را در
EnvironmentFileبگذارید و دسترسی فایل را600کنید. آنها را در خود unit ننویسید؛systemctl catبه همه نشانشان میدهد. - مسیر
ExecStartباید مطلق باشد. systemd پوسته نیست وPATHشما را نمیشناسد.
محدود کردن دسترسی
systemd میتواند برنامه را در یک محیط محدود اجرا کند. این چند خط را به بخش [Service] اضافه کنید:
NoNewPrivileges=true
PrivateTmp=true
ProtectSystem=strict
ProtectHome=true
ReadWritePaths=/var/lib/myapp
با ProtectSystem=strict کل سیستمفایل فقطخواندنی میشود، مگر مسیرهایی که در ReadWritePaths آوردهاید. اگر برنامهای نفوذپذیر باشد، مهاجم جای زیادی برای نوشتن ندارد. دستور systemd-analyze security myapp امتیازی از وضعیت فعلی میدهد و پیشنهادهای بیشتری هم دارد.
لاگها
هرچه برنامه روی خروجی استاندارد بنویسد به journal میرود:
journalctl -u myapp -f
journalctl -u myapp --since "1 hour ago"