2021-05-12 14:32:11
systemd實踐: 依據情況自動重新啟動服務
systemd服務異常自動重新啟動很好用,但有的時候希望某些服務只在特定情況下進行重新啟動,其他時候不要自動重新啟動(比如OOM,需要人工介入)。
本文拋磚引玉,旨在能夠讓讀者對systemd的重新啟動機制有一定了解。
1.最簡單的自動重新啟動範例
[Unit] Description=mytest [Service] Type=simple ExecStart=/root/mytest.sh Restart=always RestartSec=5 StartLimitInterval=0 [Install] WantedBy=multi-user.target
引數詳解
- Restart=always: 只要不是通過systemctl stop來停止服務,任何情況下都必須要重新啟動服務,預設值為no
- RestartSec=5: 重新啟動間隔,比如某次異常後,等待5(s)再進行啟動,預設值0.1(s)
- StartLimitInterval: 無限次重新啟動,預設是10秒內如果重新啟動超過5次則不再重新啟動,設定為0表示不限次數重新啟動
2.案例需求
需求:有個業務,當程式因受到OOM而退出的時候,不希望自動重新啟動(此時需要人工介入排查),其他情況下可以自動重新啟動
分析:OOM就是通過kill -9來殺進程,因此只要找到方法,告訴systemd當該服務遇到kill -9時候不自動重新啟動即可
3.RestartPreventExitStatus引數
查詢man systemd.service發現,systemd的[Service]段落裡支援一個引數,叫做RestartPreventExitStatus
該引數從字面上看,意思是當符合某些退出狀態時不要進行重新啟動。
該引數的值支援exit code和信號名2種,可寫多個,以空格分隔,例如
RestartPreventExitStatus=143 137 SIGTERM SIGKILL
表示,當退出情況只要符合以下4種情況中任意一種時候,則不再進行重新啟動
- exit code為143
- exit code為137
- 信號為TERM
- 信號為KILL
但實際情況並沒這麼簡單,請繼續往下看
4.測試方法
/usr/lib/systemd/system/mytest.service
[Unit] Description=mytest [Service] Type=simple ExecStart=/root/mem Restart=always RestartSec=5 StartLimitInterval=0 RestartPreventExitStatus=SIGKILL [Install] WantedBy=multi-user.target
/root/mem.c(不斷消耗記憶體直至發生OOM)
#include <stdio.h> #include <malloc.h> #include <stdlib.h> #include <string.h> #include <unistd.h> int main () { char *p = NULL; int count = 1; while(1){ p = (char *)malloc(1024*1024*100); if(!p){ printf("malloc error!n"); return -1; } memset(p, 0, 1024*1024*100); printf("malloc %dM memoryn", 100*count++); usleep(500000); } }
編譯及執行
gcc -o /root/mem /root/mem.c systemctl daemon-reload systemctl start mytest
5.測試結果
[root@fzxiaomange ~]# systemctl status mytest ● mytest.service - mytest Loaded: loaded (/usr/lib/systemd/system/mytest.service; disabled; vendor preset: disabled) Active: failed (Result: signal) since Sat 2018-10-20 23:32:24 CST; 45s ago Process: 10555 ExecStart=/root/mem (code=killed, signal=KILL) Main PID: 10555 (code=killed, signal=KILL) Oct 20 23:31:55 fzxiaomange.com systemd[1]: Started mytest. Oct 20 23:31:55 fzxiaomange.com systemd[1]: Starting mytest... Oct 20 23:32:24 fzxiaomange.com systemd[1]: mytest.service: main process exited, code=killed, status=9/KILL Oct 20 23:32:24 fzxiaomange.com systemd[1]: Unit mytest.service entered failed state. Oct 20 23:32:24 fzxiaomange.com systemd[1]: mytest.service failed.
重點看上面第6行
Main PID: 10555 (code=killed, signal=KILL),這行表示主進程的狀態,常見有2種情況
- code=exited, status=143:表示systemd認為主進程自行退出的,exit code為143
- code=killed, signal=KILL:表示systemd認為主進程是被kill的,接收到的信號是SIGKILL
等待5秒後,並沒有自動重新啟動,符合預期
此時將RestartPreventExitStatus=SIGKILL改為RestartPreventExitStatus=SIGTERM
執行systemctl restart mytest,再進行一次觀察,等待5秒後,服務自動重新啟動,符合預期
6.注意事項
6.1.RestartPreventExitStatus與Restart的關係
設定RestartPreventExitStatus=後,並沒有完全忽略Restart=,而是指當退出情況與RestartPreventExitStatus=匹配的時候,才忽略Restart=,若沒有匹配,根據Restart=該怎麼樣還怎麼樣(具體詳見後面的擴充套件部分)
6.2.kill子進程會是什麼情況
若systemd啟動的不是一個簡單進程,而是會派生子進程的情況(比如執行shell指令碼,shell指令碼裡啟動多個程式),那麼當另外開一個視窗通過kill -信號測試時,會是什麼情況呢,先貼出測試方法
ExecStart=/root/mem改為ExecStart=/root/mytest.sh
/root/mytest.sh內容為
#!/bin/bash
sleep 100000 &
sleep 200000
測試結果
-
若kill 主進程PID(kill不帶引數),則主進程狀態為
code=killed, signal=TERM
-
若kill -9 主進程PID,則主進程狀態為
code=killed, signal=KILL
-
若kill 最後一個子進程PID(kill不帶引數),則systemd不認為是接收到信號,而是根據最後一個進程的exit code進行處理,此時主進程狀態為
code=exited, status=143
-
若kill -9 最後一個子進程PID,此時主進程狀態為
code=exited, status=137
7.擴充套件
上面有提到RestartPreventExitStatus和Restart的關係,但沒有資料說明
另外,kill和kill -9的區別,也需要有一份資料說明
因此做了一個詳細對比,這裡附上詳細資料
相關文章