Systemd實踐: 依據情況自動重啓服務

原文出處:https://mp.weixin.qq.com/s/W6_Z1zPn3MeDgfGDP263vg


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 memory\n", 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行 MainPID: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的區別,也需要有一份數據說明

因此做了一個詳細對比,這裏附上詳細數據

640?tp=webp&wxfrom=5&wx_lazy=1&wx_co=1


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章