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行 ,這行表示主程序的狀态,常見有2種情況
MainPID:10555(code=killed,signal=KILL)
- 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的差別,也需要有一份資料說明
是以做了一個詳細對比,這裡附上詳細資料