Linux 多進程服務配置 systemd
整個項目由多個進程共同運行,現在需要一個可靠的保活機制,以便能夠在進程崩潰的時候能夠快速把它拉起來。有什么想法呢?最直觀的解決方案無非就是寫個保活腳本,在后台一直運行,如果發現某進程被關閉了,那么由腳本拉起來,但是腳本它自己掛掉怎么辦?(總不能使用腳本繼續保活保活腳本套娃吧)。此外,另一個辦法就是配置出來一個服務,讓Linux操作系統幫你守護進程,顯然,這種辦法完全不需要擔心守護進程自己掛掉,畢竟是systemd幫你守護,如果它掛掉了,操作系統應該也沒了。
sysvinit和systemd
sysvinit和systemd分別是兩個Linux操作系統的初始化系統,Linux操作系統的服務就是它們運行起來的。在centos7之前使用的是sysvinit,centos7及之后使用的是systemd。此外還有ubuntu的UpStart,不過新版的Ubuntu也使用systemd了。
比較明顯的特征就是,舊版的linux使用service httpd start啟動服務,新版的linux使用systemctl start httpd來啟動服務,此外使用initd作為初始化系統的操作系統添加服務是在/etc/init.d/中添加腳本,而使用systemd作為初始化系統的操作系統只需要在/etc/systemd/system/文件夾中添加配置文件就好了。
具體和systemd相關的介紹可以看這里:淺析init和systemd【圖文】_babylater_51CTO博客
一般來說,為了保持系統ABI的兼容性,系統的systemd版本不會特別激進,所以需要老一點文檔,比如CentOS7運行的systemd對應的版本文檔應該是這個systemd.service (archive.org)
多進程保活
首先最官方的文檔在這里:systemd.service (www.freedesktop.org)
不過需要注意的是,需要注意操作系統上面使用的systemd的版本,通過執行systemd --version看到,比如我這個版本就是systemd 219,而最新的文檔是systemd 250(這個可以在上面url頁面的右上角看到)
但是,如果正在使用的systemd的版本和文檔版本相差太大,可能會出現不准確的情況。比如在systemd 230之前是沒有StartLimitInterSec這個選項的。此時就需要這個網站https://web.archive.org/,它會給網頁拍攝快照,相當棒。此外,[systemd.unit 中文手冊 金步國] (jinbuguo.com)這位譯者翻譯了相關文檔,不過也請注意文檔版本的問題。
創建配置文件(設定重試次數)
配置文件需要創建在/etc/systemd/system/文件夾里面,比如一個配置文件如下,文件名/etc/systemd/system/program.service
[Unit]
Description=program's service
[Service]
Type=idle
Environment=LD_LIBRARY_PATH=.:/export/home/admin/usr/install/gcc940/lib
ExecStart=/export/home/admin/gamed/program/bin/program gamesys.conf 1
Restart=always
RestartSec=5
StandardOutput=null
StandardError=null
StartLimitInterval=40000000
StartLimitBurst=3
[Install]
WantedBy=multi-user.target
新版本的系統(
systemd 230之后),沒有StartLimitInterval需要使用StartLimitIntervalSec,並且StartLimitIntervalSec和StartLimitBurst兩個項在[Unit]節區!!!詳情請通過https://web.archive.org/查詢和系統systemd版本對應的文檔
然后通過systemctl start program.service啟動服務,通過systemctl status program.service查看服務狀態和啟動失敗原因,通過systemctl stop program.service關閉服務。
首先一個服務的配置文件是.service作為后綴的,結構類似於windows的.ini配置文件,其中一般由三個部分。分別是
[Unit]:包含與單元類型無關的通用信息[Service]:其中是服務的屬性[Install]: 該小節包含單元的啟用信息。 事實上,systemd(1) 在運行時並不使用此小節。 只有 systemctl(1) 的 enable 與 disable 命令在啟用/停用單元時才會使用此小節(個人理解:決定在systemd處於什么目標狀態下有效)
其中有多個選項,例如上面的配置文件,含義分別如下
-
Description是作為服務的描述 -
Type表示服務的類型,可以是simple exec forking oneshot dbus notify,一般使用simple -
Environment可以指定服務會用到的環境變量 -
ExecStart是通過systemctl start xxx啟動服務時執行的命令- 需要注意的是,實測這里不能直接指定
>重定向到文件,如有需要見下面StandardOutput
- 需要注意的是,實測這里不能直接指定
-
Restart表示重啟的條件,可以取no,on-success,on-failure,on-abnormal,on-watchdog,on-abort,always之一 -
RestartSec如果重啟,兩次重啟之間的間隔 默認是100ms -
StandardOutput標准輸出,老版本(比如Systemd 219)不能直接指定文件,新版本可以這樣指定文件file:/var/log/xxx.log -
StandardError同上不過是標准錯誤 -
StartLimitInterval和StartLimitBurst這兩個表示在StartLimitInterval指定的時間里重試失敗StartLimitBurst次則放棄重試,- 可以通過
systemctl reset-failed重置失敗次數, - 需要注意的是,手動停止服務也會被計數,並且如果設定上面兩個參數,失敗后必須手動重置
- 再者,
Systemd 230之后使用StartLimitIntervalSec替代了StartLimitInterval這里很坑...所以需要注意文檔版本 - 新版本這個選項是在
Unit節區
- 可以通過
-
WantedBy它的值是一個或多個Target,當前Unit激活時(enable)符號鏈接會放入/etc/systemd/system目錄下面以Target名 +.wants后綴構成的子目錄中(這里放一個我自己的理解,
systemd有類似於initd的運行級別的東西,詳見淺析init和systemd【圖文】_babylater_51CTO博客 的Sysvinit 運行級別和 systemd 目標的對應表,所以WantedBy設置為multi-user.target的含義就比較明確了,也即是systemdctl enable xx.service之后,在對應目標狀態下啟用)
具體的含義見systemd.service (www.freedesktop.org)
以上配置能夠實現的效果就是進程因為任何方式停止運行(除了手動stop),都會觸發重啟,但是在4000000秒之內只重啟3次,重試三次不成功,就不再嘗試,此時並不能直接systemctl start program,需要首先systemctl reset-failed(重置失敗計數)之后才能再次start
如果不使用
StartLimitIntervalSec和StartLimitBurst就不需要考慮systemctl reset-failed的使用了
多進程服務管理
如果需要創建很多服務,但是服務的配置文件只有ExecStart項有細微區別,那么可以考慮使用模板功能。
比如,創建服務文件/etc/systemd/system/ping@.service
[Unit]
Description=Ping service %i
[Service]
Type=simple
ExecStart=/usr/bin/ping %i
[Install]
WantedBy=multi-user.target
啟動進程可以這樣systemctl start ping@127.0.0.1.service,實際上,啟動服務可以省略.service后綴,也即是systemctl start ping@127.0.0.1,如果要一次啟動多個服務,可以systemctl start ping@127.0.0.1 ping@127.0.0.2,停止服務也是類似。
這樣,如果想要ping別的地址,只需要修改命令中@之后的字符串就好了。
也支持如下這樣拼接字符串
[Unit]
Description=Ping service %i
[Service]
Type=simple
ExecStart=/usr/bin/ping 127.0.0.%i
[Install]
WantedBy=multi-user.target
systemctl start ping@1就能執行ping 127.0.0.1服務
對於配置文件中的%i其實是有大小寫區別的,%i是轉義之后的字符串 %I是不轉義的字符串,對於完整的說明符列表,見systemd.unit (www.freedesktop.org)
鏈式啟動(服務依賴)
有這么一種情況,需要同時啟動多個服務,並且他們有啟動順序的限制。那么可以像下面這么配置
假設有 A進程、 B進程、 C進程,想要按順序依次啟動,那么可以這么配置
/etc/systemd/system/C.service
[Unit]
Description=C Process
Requires=B.service
After=B.service
[Service]
Type=simple
ExecStart=/export/CProgram
[Install]
WantedBy=multi-user.target
/etc/systemd/system/B.service
[Unit]
Description=B Process
Requires=A.service
After=A.service
[Service]
Type=simple
ExecStart=/export/BProgram
[Install]
WantedBy=multi-user.target
/etc/systemd/system/A.service
[Unit]
Description=A Process
[Service]
Type=simple
ExecStart=/export/AProgram
[Install]
WantedBy=multi-user.target
效果是,systemctl start C.service之后幾個進程會依次啟動,Requires指定了幾個服務之間的依賴關系,因為通過After選擇指定了服務間啟動順序,所以幾個服務是依次啟動的。如果沒有After,啟動順序不被保證
如果,此時systemctl stop A.service,那么幾個服務都會被關閉,因為Requires要求了前置服務必須存在,否則自身也不應該啟動。如果不想自身服務被關閉,那么可以把Requires(要求)替換成Wants(想要)。
如果要形成依賴鏈,除了After也可以使用Before
完整說明參見systemd.unit (www.freedesktop.org)
指定關閉進程方式 - ExecStop
可以通過ExecStop選項關閉由ExecStart啟動的服務,因為有些程序需要發送特定的信號才能安全退出,所以這個選項會很有用。而對於其他被主進程拉起來的進程,按照KillMode的設置處理,默認情況下,停止服務會關閉主進程以及主進程啟動的所有子進程。
而對於KillMode有如下幾個設置,分別是
control-group:會干掉主進程及子進程這是默認選項mixed:SIGTERM信號被發送到主進程,而隨后的SIGKILL信號被發送到單元控制組的所有剩余進程,可以通過KillSignal設置關閉主進程的信號process: 僅關閉主進程none: 什么也不干
詳情見:systemd.kill (www.freedesktop.org)
例如,如果要事先約定某進程需要發送SIGUSR1信號才能安全結束,那么可以在[Service]節區設定ExecStop=kill -SIGUSR1 $MAINPID。
上文
$MAINPID類似於環境變量,表示主進程的pid,完整列表見[systemd.exec (www.freedesktop.org)](https://www.freedesktop.org/software/systemd/man/systemd.exec.html#Environment Variables Set or Propagated by the Service Manager)
查看服務輸出 - journalctl
systemd不僅用來運行服務,它同時也有日志服務,用於取代老系統的syslog。
運行的服務標准輸出和錯誤輸出會被交給journald管理,查看某個服務可以使用這樣的命令journalctl -u ping@1 帶-e參數可以跳到最新一行 -f參數可以看到實時輸出,-n參數可以指定輸出的行數,-r反序輸出。
例如journalctl -u ping@1 -e 或者 journalctl -u ping@1 -f
如果服務的輸出太多,那么可以在.servive文件中的[Unit]節區配置StandardOutput=null
也可以通過systemctl status xx.service查看服務的部分輸出
具體使用參考
