systemd 創建自定義服務
- 簡述創建自定義服務步驟
- 文件覆蓋優先級
- 創建服務流程
- 在 /etc/systemd/system/ 目錄下創建 .service 文件(需 root 權限):
- 編寫服務配置模板
- Systemd 服務文件三大區塊詳解
- [Unit] 區塊 - 服務元數據與依賴
- [Service] 區塊 - 進程運行配置
- [Install] 區塊 - 開機啟動配置
- 完整示例
- 設置權限和路徑
- 管理服務命令
- 啟動失敗排查
- 注意事項
簡述創建自定義服務步驟
- 創建服務文件:在 /etc/systemd/system/ 目錄下創建一個新的服務文件,例如 myapp.service。使用 sudo 權限進行創建。
- 編輯服務文件:使用文本編輯器(如 vi 或 nano)編寫服務單元文件。
- 重新加載 systemd 配置:每次修改服務文件后,需要運行 sudo systemctl daemon-reload 來重新加載配置。
- 啟用服務:使用 sudo systemctl enable myapp.service 設置開機啟動。
- 啟動服務:使用 sudo systemctl start myapp.service 啟動服務。
- 檢查服務狀態:使用 sudo systemctl status myapp.service 查看服務狀態。
文件覆蓋優先級
在 CentOS 7 的 systemd 系統中,/usr/lib/systemd/system/ 和 /etc/systemd/system/ 兩個目錄有明確的區別:
- systemd 加載配置的順序(從高到低):
/etc/systemd/system/service.d/*.conf
/etc/systemd/system/service
/run/systemd/system/service.d/*.conf(臨時配置)
/usr/lib/systemd/system/service.d/*.conf
/usr/lib/systemd/system/service
需求 | 操作位置 |
---|---|
安裝新服務 | /etc/systemd/system/ |
修改現有服務參數 | /etc/systemd/system/service.d/ |
完全替換服務文件 | /etc/systemd/system/ |
查看軟件原始配置 | /usr/lib/systemd/system/ |
添加開機啟動依賴 | /etc/systemd/system/service.d/ |
/lib/systemd/system/(實際上是 /usr/lib/systemd/system/ 的符號鏈接)
特性 | /usr/lib/systemd/system/ | /etc/systemd/system/ |
---|---|---|
來源 | 軟件包安裝的原始服務文件 | 管理員自定義的服務文件或覆蓋配置 |
優先級 | 低 | 高(覆蓋 /lib/ 中的配置) |
是否會被覆蓋 | 軟件升級時可能被覆蓋 | 不會被軟件升級覆蓋 |
推薦修改方式 | 不應直接修改 | 應在此目錄添加自定義配置 |
目錄類型 | 系統默認目錄(只讀) | 管理員配置目錄(可寫) |
- 作用:存放軟件包(通過 yum/rpm 安裝)提供的原始服務文件
- 特點:
- 系統級別的默認配置
- 軟件升級時會覆蓋此目錄的文件
- 不要直接修改這里的文件(修改會被覆蓋)
創建服務流程
在 /etc/systemd/system/ 目錄下創建 .service 文件(需 root 權限):
sudo vi /etc/systemd/system/myapp.service
編寫服務配置模板
[Unit]
Description=服務描述
After=network.target # 依賴關系[Service]
Type=forking # 服務類型
ExecStart=/path/to/command
User=username # 運行用戶
Restart=on-failure # 重啟策略[Install]
WantedBy=multi-user.target
Systemd 服務文件三大區塊詳解
[Unit] |
---|
? 服務標識 (Description) |
? 啟動順序 (After/Before) |
? 依賴關系 (Requires/Wants) |
[Service] | [Install] |
---|---|
? 進程控制 (Type/ExecStart) | ? 開機啟動目標 (WantedBy) |
? 運行環境 (User/Env) | ? 別名管理 (Alias) |
? 資源限制 (MemoryLimit) | |
? 安全策略 (PrivateTmp) |
[Unit] 區塊 - 服務元數據與依賴
定義服務的描述、依賴關系和啟動順序
指令 | 說明 | 示例值 |
---|---|---|
Description | 必填 服務描述信息(顯示在 systemctl status 中) | Description=Nginx Web Server |
After | 定義啟動順序依賴(在此服務之后啟動) | After=network.target |
Before | 定義反向依賴(在此服務之前啟動) | Before=shutdown.target |
Requires | 強依賴 - 依賴服務失敗則本服務失敗 | Requires=mysql.service |
Wants | 弱依賴 - 依賴服務失敗不影響本服務 | Wants=postfix.service |
Conflicts | 沖突服務 - 不能同時運行的服務 | Conflicts=httpd.service |
Documentation | 服務文檔鏈接 | Documentation=man:nginx(8) |
Condition… | 啟動條件檢查(如 ConditionPathExists=/etc/nginx.conf) |
[Unit] 區塊示例
[Unit]
Description=High Performance Web Server
After=network.target remote-fs.target nss-lookup.target
Wants=postgresql.service
Documentation=https://nginx.org/en/docs/
[Service] 區塊 - 進程運行配置
定義服務進程的執行方式和運行時行為
類別 | 指令 | 說明 | 常用值 |
---|---|---|---|
啟動類型 | Type | 必填 進程啟動類型 | simple、forking、oneshot、notify |
ExecStart | 必填 啟動命令(絕對路徑) | /usr/sbin/nginx -g “daemon off;” | |
ExecStartPre | 主命令前執行的預備命令 | /bin/mkdir -p /run/nginx | |
ExecStartPost | 主命令后執行的后續命令 | /bin/echo “Service started” | |
ExecReload | 重載服務時執行的命令 | /bin/kill -HUP $MAINPID | |
運行環境 | User | 運行服務的用戶 | nginx、nobody |
Group | 運行服務的用戶組 | www-data | |
WorkingDirectory | 工作目錄 | /var/www/html | |
Environment | 設置環境變量 | PORT=8080 | |
EnvironmentFile | 從文件加載環境變量 | /etc/sysconfig/nginx | |
重啟策略 | Restart | 服務退出時重啟策略 | no、always、onfailure、on-abort |
RestartSec | 重啟前等待時間 | 5s、1min | |
資源限制 | MemoryLimit | 內存限制 | 512M |
CPUQuota | CPU配額 | 80% | |
安全控制 | PrivateTmp | 使用私有/tmp目錄(增強安全) | true |
ProtectSystem | 文件系統保護級別 | full、strict |
[Service]示例
[Service]
Type=forking
PIDFile=/run/nginx.pid
ExecStartPre=/usr/bin/rm -f /run/nginx.pid
ExecStart=/usr/sbin/nginx -c /etc/nginx/nginx.conf
ExecReload=/bin/kill -s HUP $MAINPID
User=nginx
Group=nginx
Environment="NGINX_LOGLEVEL=info"
Restart=on-failure
RestartSec=5s
MemoryLimit=500M
PrivateTmp=true
常見 Type 類型:
特性 | Type=simple | Type=forking |
---|---|---|
工作方式 | 主進程直接在前臺運行 | 主進程 fork 子進程后退出 |
systemd 監控對象 | ExecStart 啟動的進程 | fork 出來的子進程 |
適用場景 | 現代應用 (Python/Node.js/Go 等) | 傳統守護進程 (Nginx/MySQL 等) |
PID 文件 | 不需要 | 必須 通過 PIDFile= 指定 |
啟動完成判定 | 立即標記為 active | 需等待主進程退出 |
日志處理 | 自動捕獲 stdout/stderr | 需程序自行處理日志 |
啟動速度 | 快 (直接啟動) | 稍慢 (需完成 fork 過程) |
典型應用 | Flask, Redis, systemd 自身服務 | Apache, PostgreSQL, Zabbix Server |
判斷應該使用哪種類型:
# 手動測試啟動程序
/usr/sbin/nginx -c /etc/nginx/nginx.conf# 觀察行為:
# 1. 如果命令立即返回且后臺有進程 → forking
# 2. 如果命令阻塞在前臺 → simple
[Install] 區塊 - 開機啟動配置
定義服務安裝到哪個運行級別(target)
指令 | 說明 | 示例值 |
---|---|---|
WantedBy | 必填 指定服務關聯的 target(實現開機啟動) | multi-user.target |
RequiredBy | 指定強依賴本服務的 target | graphical.target |
Alias | 服務別名 | Alias=webserver.service |
Also | 安裝時同時啟用的其他服務 | Also=nginx-sockets.service |
[Install] 示例
[Install]
WantedBy=multi-user.target
Alias=web.service
完整示例
[Unit]
Description=My Custom Application # 服務描述
After=network.target # 在網絡啟動后運行[Service]
Type=simple # 服務類型(常用 simple 或 forking)
User=appuser # 運行服務的用戶
Group=appgroup # 運行服務的組
WorkingDirectory=/opt/myapp # 工作目錄
ExecStart=/usr/bin/java -jar /opt/myapp/app.jar # 啟動命令(必須絕對路徑)
Restart=on-failure # 失敗時自動重啟
RestartSec=5s # 重啟間隔
Environment="PORT=8080" # 設置環境變量
PrivateTmp=true # 啟用私有臨時目錄(安全增強)# 日志配置(可選)
StandardOutput=syslog
StandardError=syslog
SyslogIdentifier=myapp[Install]
WantedBy=multi-user.target # 多用戶模式下啟用
設置權限和路徑
# 確保可執行文件有權限
sudo chmod +x /opt/myapp/start.sh
# 環境變量文件(可選)
sudo vi /etc/sysconfig/myapp # 定義變量 APP_ENV=production
服務文件中引用:
EnvironmentFile=/etc/sysconfig/myapp
ExecStart=/opt/myapp/start.sh ${APP_ENV}
管理服務命令
# 重載 systemd 配置(修改服務文件后必須執行)
sudo systemctl daemon-reload# 啟動/停止服務
sudo systemctl start myapp.service
sudo systemctl stop myapp.service# 設置開機自啟
sudo systemctl enable myapp.service# 查看狀態和日志
systemctl status myapp # 服務狀態
journalctl -u myapp -f # 實時日志(-f 跟蹤日志)
journalctl -u myapp --since "2020-01-01" --until "1 hour ago" # 時間篩選
啟動失敗排查
systemctl status myapp # 查看錯誤摘要
journalctl -xe # 檢查詳細日志
- 測試啟動命令: 手動執行 ExecStart 中的命令,驗證路徑和權限。
- 環境變量問題: 使用 systemctl show myapp 檢查最終環境變量
注意事項
- 路徑必須為絕對路徑(包括腳本和命令)
- 修改服務文件后必須執行 daemon-reload
- 生產環境建議用非 root 用戶運行(User=)