Flask监控程序:Python脚本监控Flask,异常重启发送邮件

摘要:Flaskgunicorn

利用gunicorn部署Flask应用

编写shell脚本利用gunicorn部署Flask

# vim run.sh
#! /bin/bash
cd /home/gp/myproject/my_web
gunicorn -c gun.conf.py -D manage:app

其中gun.conf.py指定了pid文件名在项目目录下gunicorn.pid

# cat gunicorn.pid
9306

监控脚本

从gunicorn.pid获取进程号,使用psutil模块每隔几秒检查进程中是否存在该进程,如果不存在重启3次,在Python中调用shell命令执行run.sh,并且发送邮件告知程序异常和是否重启成功,其中发送邮件采用Python自带的模块smtplibemail

import os
import time
import datetime
import smtplib
import traceback
from email.mime.multipart import MIMEMultipart
from email.mime.text import MIMEText
from email.header import Header

import config
from utils import get_host_ip, get_pid_from_pid_file, check_pid, ERROR_HTML, RESTART_SUCCESS_HTML, RESTART_FAIL_HTML

IP = get_host_ip()
SERVER_NAME = config.get_string("server.name")
PID_FILE_PATH = config.get_string("pid.file.path")
SMTP_HOST = config.get_string("smtp.host")
SMTP_PORT = int(config.get_string("smtp.port"))
FROM_EMAIL_ACCOUNT = config.get_string("from.email.account")
FROM_EMAIL_PASSWORD = config.get_string("from.email.password")
TO_EMAIL_ACCOUNT = config.get_string("to.email.account")
RETRY_COUNT = int(config.get_string("retry.count"))
SLEEP_TIME = int(config.get_string("sleep.time"))


def send_error_email():
    conn = None
    try:
        conn = smtplib.SMTP_SSL(SMTP_HOST, SMTP_PORT)
        conn.login(FROM_EMAIL_ACCOUNT, FROM_EMAIL_PASSWORD)
        msg = MIMEMultipart()
        subject = Header("{}:{}应用进程死亡了".format(IP, SERVER_NAME), 'utf-8').encode()
        msg['Subject'] = subject
        msg['From'] = FROM_EMAIL_ACCOUNT
        msg['To'] = TO_EMAIL_ACCOUNT
        text = MIMEText(ERROR_HTML.format(IP, SERVER_NAME, datetime.datetime.today().strftime("%Y-%m-%d %H:%M:%S")),
                        'html', 'utf-8')
        msg.attach(text)
        conn.sendmail(FROM_EMAIL_ACCOUNT, TO_EMAIL_ACCOUNT.split(","), msg.as_string())
    except Exception as e:
        traceback.print_exc()
    finally:
        if conn:
            conn.quit()


def send_restart_email(success=True):
    timestamp = datetime.datetime.today().strftime("%Y-%m-%d %H:%M:%S")
    subject = Header("{}:{}应用已经重启".format(IP, SERVER_NAME), 'utf-8').encode()
    text = MIMEText(RESTART_SUCCESS_HTML.format(IP, SERVER_NAME, timestamp), 'html', 'utf-8')
    if not success:
        subject = Header("{}:{}应用重启失败".format(IP, SERVER_NAME), 'utf-8').encode()
        text = MIMEText(RESTART_FAIL_HTML.format(IP, SERVER_NAME, timestamp), 'html', 'utf-8')
    conn = None
    try:
        conn = smtplib.SMTP_SSL(SMTP_HOST, SMTP_PORT)
        conn.login(FROM_EMAIL_ACCOUNT, FROM_EMAIL_PASSWORD)
        msg = MIMEMultipart()
        msg['Subject'] = subject
        msg['From'] = FROM_EMAIL_ACCOUNT
        msg['To'] = TO_EMAIL_ACCOUNT
        msg.attach(text)
        conn.sendmail(FROM_EMAIL_ACCOUNT, TO_EMAIL_ACCOUNT.split(","), msg.as_string())
    except Exception as e:
        traceback.print_exc()
    finally:
        if conn:
            conn.quit()


def restart_server():
    os.system(config.get_string("start.gunicorn.cmd"))
    time.sleep(3)
    pid_num = get_pid_from_pid_file(PID_FILE_PATH)
    if check_pid(pid_num):
        send_restart_email(success=True)
        return "success"
    else:
        send_restart_email(success=False)
        return "fail"


if __name__ == '__main__':
    dead = None
    while True:
        pid_num = get_pid_from_pid_file(PID_FILE_PATH)
        result = check_pid(pid_num)
        if result:
            dead = False
        if not result and not dead:
            send_error_email()
            count = 1
            status = None
            while count <= RETRY_COUNT and status != "success":
                print("-----------------重启:{}".format(datetime.datetime.today().strftime("%Y-%m-%d %H:%M:%S")))
                status = restart_server()
                count += 1
            if status == "fail":
                dead = True
        time.sleep(SLEEP_TIME)

utils.py编写辅助功能包括获取IP地址,获取pid,检查pid等。

import traceback
import socket

import psutil


def get_host_ip():
    """获得本机的ip地址"""
    s = None
    ip = None
    try:
        s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
        s.connect(('8.8.8.8', 80))
        ip = s.getsockname()[0]
    except Exception as e:
        print("获取ip地址错误")
        traceback.print_exc()
    finally:
        if s:
            s.close()
    return ip


def get_pid_from_pid_file(pid_text_path):
    """通过文件获得pid"""
    with open(pid_text_path) as pid_text:
        pid_num = int(pid_text.read().strip())
    return pid_num


def check_pid(pid_num):
    """检查进程中是否有pid"""
    if psutil.pid_exists(pid_num):
        return True
    return False


ERROR_HTML = """
<h1>[WARNING]监控报警</h1>
<h1>Endpoint:{}</h1>
<h1>ServerName:{}</h1>
<h1>Note:应用进程死亡了</h1>
<h1>Timestamp:{}</h1>
"""

RESTART_SUCCESS_HTML = """
<h1>[WARNING]监控报警</h1>
<h1>Endpoint:{}</h1>
<h1>ServerName:{}</h1>
<h1>Note:应用已经重启</h1>
<h1>Timestamp:{}</h1>
"""

RESTART_FAIL_HTML = """
<h1>[WARNING]监控报警</h1>
<h1>Endpoint:{}</h1>
<h1>ServerName:{}</h1>
<h1>Note:应用重启失败</h1>
<h1>Timestamp:{}</h1>
"""

config.yml读取配置

import yaml

YAML_FILE = "./etc/config.yml"


def load_yaml_config(yaml_file):
    with open(yaml_file) as f:
        config = yaml.load(f, Loader=yaml.FullLoader)
    return config


conf = load_yaml_config(YAML_FILE)


def get_string(key: str, default: str = None):
    if key in conf.keys():
        return str(conf.get(key))
    return default

监控脚本测试

后台杀死gunicorn进程,静静地等待邮件和重启。


监控程序发送邮件.png
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 220,295评论 6 512
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 93,928评论 3 396
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 166,682评论 0 357
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 59,209评论 1 295
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 68,237评论 6 397
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,965评论 1 308
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,586评论 3 420
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,487评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 46,016评论 1 319
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 38,136评论 3 340
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 40,271评论 1 352
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,948评论 5 347
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,619评论 3 331
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 32,139评论 0 23
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 33,252评论 1 272
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,598评论 3 375
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 45,267评论 2 358

推荐阅读更多精彩内容