🖥️ 自訂 systemd 服務
撰寫 .service 單元讓應用成為系統服務
學習撰寫 systemd service unit 檔案,將自己的應用程式設定為系統服務,實現開機自動啟動、崩潰自動重啟(Restart=on-failure)與資源限制。
Service 檔案結構
systemd service unit 檔案放在 /etc/systemd/system/,由三個主要區塊組成:
[Unit] # 服務的描述與依賴關係
[Service] # 實際的執行設定
[Install] # 安裝設定(設定 enable 時的行為)完整範例(Node.js API 服務)
$ sudo vim /etc/systemd/system/myapi.service[Unit]
Description=My Node.js API Server
Documentation=https://github.com/example/myapi
# 網路就緒後才啟動
After=network.target
# 若 MySQL 存在,則在其啟動後才啟動
After=mysql.service
Wants=mysql.service
[Service]
Type=simple
User=www-data
Group=www-data
WorkingDirectory=/opt/myapi
# 環境變數(機密資訊用 EnvironmentFile)
Environment="NODE_ENV=production"
Environment="PORT=3000"
EnvironmentFile=-/opt/myapi/.env # - 前綴表示檔案不存在時不報錯
ExecStart=/usr/bin/node /opt/myapi/server.js
ExecReload=/bin/kill -HUP $MAINPID # reload 指令(可選)
# 崩潰時自動重啟
Restart=on-failure
RestartSec=5s # 重啟前等待 5 秒
StartLimitIntervalSec=60 # 60 秒內
StartLimitBurst=5 # 最多重啟 5 次(防止無限重啟)
# 資源限制
LimitNOFILE=65536 # 最大開啟檔案數
MemoryMax=512M # 記憶體上限(超過則被終止)
# 標準輸出導向 systemd journal
StandardOutput=journal
StandardError=journal
SyslogIdentifier=myapi
[Install]
WantedBy=multi-user.target$ sudo systemctl daemon-reload # 必須在修改 service 檔案後執行
sudo systemctl enable --now myapi # 啟用並立即啟動Type 類型說明
| Type | 說明 | 適用情境 |
|---|---|---|
simple(預設) | ExecStart 啟動後即視為就緒 | 大多數常駐程序 |
forking | 主程序 fork 後退出(傳統 daemon) | Nginx、Apache |
oneshot | 執行完就結束,適合一次性工作 | 資料庫 migration、初始化腳本 |
notify | 程序用 sd_notify() 告知 systemd 已就緒 | 支援此協定的程式(如 gunicorn) |
exec | 等待 ExecStart 執行完成才視為就緒 | 需要確保啟動完成後才繼續的場景 |
Restart 策略
| Restart= | 重啟時機 |
|---|---|
no(預設) | 不重啟 |
always | 無論退出原因,總是重啟 |
on-failure | 非正常退出(回傳碼非 0、被訊號殺死)時重啟 |
on-abnormal | 被訊號殺死或逾時時重啟 |
on-watchdog | watchdog 逾時時重啟 |
環境變數設定
# 方式 1:直接在 service 檔案中設定
Environment="KEY=value"
Environment="KEY2=value2"
# 方式 2:使用獨立的環境變數檔案(推薦機密設定)
EnvironmentFile=/opt/myapp/.env
# .env 格式:
# DB_PASSWORD=secret
# API_KEY=abc123
機密資訊(密碼、API key)不要直接寫在 service 檔案中,改用 EnvironmentFile 並設定嚴格的檔案權限(
chmod 600 .env)。
管理與除錯
| 操作 | 指令 |
|---|---|
| 重新載入 unit 設定(修改後必做) | sudo systemctl daemon-reload |
| 查看服務狀態(含最近 log) | sudo systemctl status myapi |
| 即時查看服務日誌 | sudo journalctl -u myapi -f |
| 查看最近 100 行日誌 | sudo journalctl -u myapi -n 100 |
| 查看啟動失敗原因 | sudo journalctl -u myapi -b --priority=err |
| 驗證 service 檔案語法 | systemd-analyze verify /etc/systemd/system/myapi.service |
| 查看啟動時間 | systemd-analyze blame |