公司的應用程序有時候會莫名其妙地掛掉,若是咱們常常去登陸服務器看是否是程序掛了,掛了再拉起,那樣是很是耗時和麻煩的事情。
後來咱們經過使用 supervisor 去守護啓動,實現方法以下:
那什麼是 supervisor了?
Supervisor是用 Python 開發的一個client/server服務,是Linux/Unix系統下的一個進程管理工具,不支持Windows系統。它能夠很方便地監聽、啓動、中止、重啓一個或多個進程。用Supervisor管理的進程,當一個進程意外被殺死,或者是意外被中止(系統負載太高,cpu佔用率很高等),supervisor 監聽到進程死後,會自動將它從新拉起來,很方便地作到進程自動恢復的功能,再也不須要本身寫shell腳原本控制。通常狀況下,yum直接安裝便可。yum install supervisor
首先咱們須要首先注意的一個地方是配置文件的後綴。
vim /etc/supervisord.conf
[include]html
files = supervisord.d/*.ini node
若是你想配置文件爲其餘格式,好比 conf 格式的話, 須要更改 iles = supervisord.d/*.conf 。
好比咱們須要守護啓動一個進程,咱們就以守護Prometheus 爲例:
vim /etc/supervisord.d/proms.ini python
[program:proms]shell
command=/opt/prometheus/server/prometheus/prometheus編程
directory=/opt/prometheus/server/prometheusvim
stdout_logfile=/home/data/logs/prometheus/sever.log服務器
autostart=true運維
autorestart=trueide
redirect_stderr=true模塊化
user=root
startsecs=3
supervisor配置文件詳解:
program: 指定的守護進程名
command: 命令
stdout_logfile: 日誌路徑
autostart: supervisor啓動的時候是否隨着同時啓動,默認爲 true
autorestart: 是否掛了自動重啓
redirect_stderr:標準錯誤重定向
startsecs: 子進程啓動多少秒以後,此時的狀態是running
啓動supervisor--(yum方式安裝的)
/usr/bin/python /usr/bin/supervisord -c /etc/supervisord.conf
或者
systemctl start supervisord.service
所以 咱們可使用以下的命令進行進程的中止,啓動,重啓等操做。
supervisorctl status # 查看應用啓動狀態
supervisorctl stop proms # 中止prometheus應用
supervisorctl start proms # 啓動prometheus應用
supervisorctl restart proms # 重啓prometheus應用
雖然使用上面的策略 supervisor, 能夠實現進程的守護啓動,若是進程掛了,會自動拉起,可是並無告警通知的功能。因此咱們須要監控進程的狀態並實現告警 通知到對應的開發人員以及運維人員。對於這樣的場景,咱們如何去實現了?
分析
對於這種狀況,咱們可使用以下的方案去實現:
方案一:使用 Zabbix/Prometheus監控系統,對Java應用程序作 TCP 端口檢測。若是檢測端口不通,就設置檢測失敗的觸發器。而後實現告警.
方案二: 使用 Zabbix的自定義Key去實現告警,自定義key 的內容是執行一個shell腳本,Shell腳本用來檢測進程是否存在,不存在的話,輸出爲 0,存在輸出爲1。而後Zabbix 的觸發器 設置最近的T值 不爲1,從而來實現進程狀態檢測的告警。
方案三:編寫Python腳本用來檢測進程的狀態,而後使用Python的內置庫,實現郵件告警。
解決
思路整理
這裏咱們重點講下python如何檢測。
1.首先Python程序須要檢測 Java進程是否存在。
2.檢測到進程若是存在不作任何處理,若是不存在,就須要觸發郵件告警的函數
3.Python程序須要定時按期地去執行檢測腳本。
代碼實現
第一階
首先咱們先來實現判斷進程是否存在的邏輯,判斷進程是否存在,咱們採用 psutil來實現。庫的安裝方法以下:
pip3 install psutil
檢測進程存在的代碼邏輯以下:
#coding:utf-8
import psutil
def checkprocess(processname):
pl = psutil.pids() for pid in pl: if p.utilutil.Process.pid).name() == processname: return pid
if isinstance(checkprocess("notepad++.exe"),int):
print("進程存在")
else:
print(:進程不存在")
解析:
首先咱們先定義一個 checkprocess 函數,函數的第一個參數傳入進程名, 其中 pl = psutil.pids() 表示把全部的進程列出來。
接着咱們for循環一下pid的列表,當找到 psutil.Process(pid).name() 的名詞爲傳入的參數的名字的時候就返回pid值,沒有就不作任何操做(能夠認爲返回內容爲空)
接着 isinstance 用於檢測返回內容。
那什麼是 isinstance 了?
咱們能夠直接看看官方文檔, https://docs.python.org/3/library/functions.html ,https://docs.python.org/3/library/functions.html
isinstance(object, classinfo)
Return True if the object argument is an instance of the classinfo argument, or of a (direct, indirect or virtual) subclass thereof. If object is not an object of the given type, the function always returns False. If classinfo is a tuple of type objects (or recursively, other such tuples), return True if object is an instance of any of the types. If classinfo is not a type or tuple of types and such tuples, a TypeError exception is raised.
咱們翻譯成中文,能夠這樣理解:
1.格式
isinstance(object,type-or-tuple-or-class) -> bool
2.做用
判斷一個對象是某個類或子類的實例。
3.參數介紹
第一個參數(object)爲對象,第二個參數(type)爲類型名(int...)或類型名的一個列表((int,list,float)是一個列表)。其返回值爲布爾型(True or flase)。
當第二個參數是type-or-tuple時
若第二個參數只有一個單獨的類型,對象的類型與參數二的類型相同則返回True;
若第二個參數爲一個元組類型,則若對象類型與元組中類型名之一相同即返回True。
第二階段
咱們在第一階段實現了檢測進程的相關代碼,如今咱們來實現發送郵件的代碼實現,代碼:內容以下:
#coding:utf-8
import smtplib
from email.mime.text import MIMEText
#第三方 SMTP 服務
mail_host = "smtp.exmail.qq.com" # SMTP服務器
mail_user = "tech.sys@aa.cn" # 用戶名
mail_pass = "aapwd" # 密碼
sender = 'tech.sys@aa.cn' # 發件人郵箱
#多個郵箱用逗號隔開構成列表
receivers = ['yyy@qq.com','xxx@qq.com'] # 接收人郵箱
定義函數。傳入3個參數,第一個是接收者,第二個是主題,第三個是正文內容
def SendMail(receivers,title,content):
# content = '這是正文' # title = '這是主題' # 郵件主題 message = MIMEText(content, 'plain', 'utf-8') # 內容, 格式, 編碼 message['From'] = "{}".format(sender) message['To'] = ",".join(receivers) message['Subject'] = title try: smtpObj = smtplib.SMTP_SSL(mail_host, 465) # 啓用SSL發信, 端口通常是465 smtpObj.login(mail_user, mail_pass) # 登陸驗證 smtpObj.sendmail(sender, message['To'].split(','), message.as_string()) #郵件發" 發" int("mail has been send successfully.") except smtplib.SMTPException as e: print(e)
##測試郵件發送
SendMail(receivers,"主題","正文2")
解析:
上面的代碼已經作了註釋,代碼功能不作詳解。咱們要注意的一點的是:smtpObj.sendmail(sender, message['To'].split(','), message.as_string())
由於以前咱們的收件人,是列表的形式,因此在發送郵件的時候,咱們須要 使用, 用 收件人進行逐一發送郵件。
第三階段
整合以上兩段代碼,既能夠檢測進程又能夠發送郵件:
#coding:utf-8
import smtplib
from email.mime.text import MIMEText
import psutil
#定義第三方 SMTP 服務
mail_host = "smtp.exmail.qq.com" # SMTP服務器
mail_user = "tech.sys@aa.cn" # 用戶名
mail_pass = "aapwd" # 密碼
sender = 'tech.sys@aa.cn' # 發件人郵箱
receivers = ['yy@qq.com','xx@qq.com'] ## 多個郵箱用逗號隔開構成列表
#定義進程名
P_name = "node_exporter"
#郵件發送函數
def SendMail(receivers,title,content):
# content = '這是正文' # title = '這是主題' # 郵件主題 message = MIMEText(content, 'plain', 'utf-8') # 內容, 格式, 編碼 message['From'] = "{}".format(sender) message['To'] = ",".join(receivers) message['Subject'] = title try: smtpObj = smtplib.SMTP_SSL(mail_host, 465) # 啓用SSL發信, 端口通常是465 smtpObj.login(mail_user, mail_pass) # 登陸驗證 smtpObj.sendmail(sender, message['To'].split(','), message.as_string()) # 發送 print("mail has been send successfully.") except smtplib.SMTPException as e: print(e)
#定義檢測進程函數
def checkprocess(processname):
pl = psutil.pids() for pid in pl: if psutil.Process(pid).name() == processname: return pid
#SendMail(receivers,"主題","正文2")
if isinstance(checkprocess(P_name),int):
pass # 進程存在
else:
print("{0}進程不存在,發送郵件".format(P_name)) SendMail(receivers,"{0}進程down掉了".format(P_name),"{0}進程down掉了,請檢測緣由....".format(P_name))
解析:
上面的代碼邏輯只是整合了兩段代碼, 並無作其餘的處理。首先是導入須要的模塊,而後定義所須要的變量,以及函數,最後經過format函數將變量傳入函數中而已。
須要注意的一點是,由於收件人是列表,那麼在郵件發送的時候須要把列表進行切分,也就是使用split把收件人一個個地拿出來,而後再去進行郵件發送。
message['To'].split 切分出每一個收件人(',') #split 切分出每一個收件人
咱們執行一下這段函數結果以下:
[root@me03 www]# python3 check_mail.py
node_exporter進程不存在,發送郵件
mail has been send successfully.
至此咱們基本上實現了能夠經過檢測進程而後實現告警了。可是在咱們編程當中,咱們須要有模塊化的編程思想,也就是有一些組件若是能模塊化就進行模塊化,那樣子若是你有其餘需求的話,想複用原來腳本的函數的話就不須要再去寫重複的函數了。
因此咱們能夠經過類的方式進行導入。而後實現模塊化的編程。
第四階段
模塊化編程,咱們能夠把郵件發送封裝成一個類,用的時候直接導入便可。目錄結構以下:
[www@me03 ~]$ tree ee/
ee/
├── check.py
└── S_mail.py
0 directories, 2 files
[www@me03 ~]$
S_mail.py 代碼以下:
#coding:utf-8
import smtplib
from email.mime.text import MIMEText
class SendEMail(object):
# 定義第三方 SMTP 服務 def __init__(self): self.mail_host = "smtp.exmail.qq.com" # SMTP服務器 self.mail_user = "tech.sys@aa.cn" # 用戶名 self.mail_pass = "aapwd" # 密碼 self.sender = 'tech.sys@aa.cn' # 發件人郵箱 self.smtpObj = smtplib.SMTP_SSL(self.mail_host, 465) self.smtpObj.login(self.mail_user, self.mail_pass) # 登陸驗證 def sendmail(self, receivers, title, content): message = MIMEText(content, 'plain', 'utf-8') # 內容, 格式, 編碼 message['From'] = "{}".format(self.sender) message['To'] = ",".join(receivers) message['Subject'] = title try: self.smtpObj.sendmail(self.sender, message['To'].split(','), message.as_string()) # 發送 print("mail has been send successfully.") except smtplib.SMTPException as e: print(e)
if name == 'main':
sm = SendEMail() sm.sendmail(['1093381395@qq.com'], '主題', '正文')
解析:
構造函數中初始化了郵件發送人、 smtp服務器等等。mail郵件發送函數進行發送郵件。要注意sm.sendmail` 傳入的收件人是列表
咱們再看看check.py 的內容。
#coding:utf-8
from S_mail import SendEMail #導入郵件類
import psutil
#實例化郵件類
sm = SendEMail()
#定義收件人
receivers = ['1093381395@qq.com','xx@qq.com'] # 接收人郵箱
#定義進程名
P_name="node_exporter"
#定義檢測進程函數
def checkprocess(processname):
pl = psutil.pids() for pid in pl: if psutil.Process(pid).name() == processname: return pid
if isinstance(checkprocess(P_name),int):
pass # 進程存在
else:
print("{0}進程不存在,發送郵件".format(P_name)) sm.sendmail(receivers,"{0}進程down掉了".format(P_name),"{0}進程down掉了,請檢測緣由....".format(P_name))
</code></pre>
check.py的代碼就更加簡單了,首先咱們先是導入了郵件類,類的做用是用來發送郵件用的,而後實例化郵件類,再定義一些變量信息。好比收件人等。最後咱們經過編寫進程檢測函數,用來檢測進程,若是進程不存在則會調用郵件類裏的郵件發送函數來實現告警
第四階段:
咱們實現了檢測進程是否存在而後實現告警的代碼,如今咱們須要它定時去檢測,而後實現告警。咱們加到計劃任務裏去。這兩個文件放到一個指定的目錄 下。(必定要放到同一個目錄下,否則沒法導入。若是放到其餘的目錄的話,須要修改check.py的模塊導入路徑才行)
/1 * python3 /home/data/scripts/check.py到此,咱們經過定時執行腳本檢測進程並實現告警的需求得以實現。