一、要求

    最近公司大佬要求将应用每天的存活状态,输出成报表的形式,每天以邮件的方式发送给他们。报表里面要输出:应用名、应用所在服务器IP、应用挂掉的时间点、应用挂掉的持续时间。


二、环境介绍

    我们公司用的监控系统,是小米的open-falcon,而app-alive的监控是用小米推荐的urlooker,urlooker监控的模式,是每分钟将应用状态推送到open-falcon。而我们这边又用自己拿框架开发的ops系统,ops直接调用open-falcon的接口,在ops里面,可以看到应用存活的状态,且ops.app_detail表记录了应用id(唯一标识)及应用名。所以一开始我的想法是:去ops记录应用状态的表直接查询。结果去查询才发现,ops有两张表是记录跟应用状态有关的数据。一张记录的是应用down掉持续时间聚合后的信息,一张记录的是1天、30天应用的存活率。。。。这根本跟我要的东西不一样,所以最后思考半天,我决定直接去urlooker拿取数据。


关于urlooker数据库:

   有两张表跟app-alive有关:urlooker.strategy和 urlooker.item_status00,第一张表有两个字段有用:urlooker.strategy.ops_cp_app_id(与ops.app_detail.id一一对应)和urlooker.strategy.environment(区分测试、预发布、release环境),第二张表urlooker.item_status00即是urlooker每分钟push的数据,这张表默认保存12小时的数据。



三、查询urlooker数据库获取数据源

通过跨库联表查询,获取所需的数据源,我这里查询的结果是所有 result不为0即应用挂掉状态的信息,并通过组内排序输出。


SELECT
        ops.app_detail.app_name,
        urlooker.item_status00.sid AS sid,
        urlooker.strategy.ops_cp_app_id,
        urlooker.item_status00.ip,
        urlooker.strategy.note,
        urlooker.item_status00.result,
        ROUND( urlooker.item_status00.push_time ) AS down_time,
        FROM_UNIXTIME( urlooker.item_status00.push_time ) AS new_time 
FROM
        urlooker.item_status00
        LEFT JOIN urlooker.strategy ON urlooker.item_status00.sid = urlooker.strategy.id
        LEFT JOIN ops.app_detail ON ops.app_detail.id = urlooker.strategy.ops_cp_app_id 
WHERE
        urlooker.strategy.environment = "release" 
        AND urlooker.item_status00.result <> "0" 
ORDER BY
        sid DESC,
        down_time DESC;





本来我想直接通过sql取做定时任务,但是关于数据源的计算及处理,实在不知道咋整(吃了没技术的亏啊,应该用存储过程可以实现)。所以直接将数据源导出,用shell去处理。






四、处理数据源


编写shell脚本,处理数据源:

# cat send_app_alived.sh 
#!/bin/bash
DataInputPath=/home/app-alive/source.txt
DataOutputPath=/home/app-alive/$(date +%F)-output.csv
#data 
mysql -uops_ro -pxxxx -e "source /home/app-alive/select.sql" > $DataInputPath  2>/dev/null
app_id=$(awk '{ print $2}' $DataInputPath |grep -v sid|uniq)
if [ ! -f "$DataOutputPath" ];
then
  echo "应用名,服务器IP,应用停止时间,停止持续时间" > $DataOutputPath
fi
for id in $app_id
do
  down_time=$(awk '{if($2=='$id') print $7}' $DataInputPath)
  app_name=$(awk '{if($2=='$id') print $1}' $DataInputPath|uniq)
  app_ip=$(awk '{if($2=='$id') print $4}' $DataInputPath|uniq)
  down_time_num=$(awk '{if($2=='$id') print $7}' $DataInputPath|wc -l)
  if [ "$down_time_num" -eq "1" ];
  then
    G_time=$(date -d "1970-01-01 UTC $down_time seconds" +"%Y-%m-%d %H:%M")
    echo "$app_name,$app_ip,$G_time,1分钟" >> $DataOutputPath
  else
    sum=0
    count=0
    for i in $down_time
    do
      sum=$((i-sum))
      if [ $sum -ne -60 ] && [ $sum -ne $i ];
      then
       G_time=$(date -d "1970-01-01 UTC $down_sum seconds" +"%Y-%m-%d %H:%M")
       echo "$app_name,$app_ip,$G_time,${count}分钟" >> $DataOutputPath
       count=0
      fi
      if [ $count -eq 0 ];
      then
        down_sum=$i
      fi
      count=$((count + 1))
      sum=$i
    done
    if [ $count -eq 1 ];
    then
      G_time=$(date -d "1970-01-01 UTC $down_sum seconds" +"%Y-%m-%d %H:%M")
      echo "$app_name,$app_ip,$G_time,1分钟" >> $DataOutputPath
    fi
  fi
done


这个脚本处理完后,输出的结果为:





# cat 2019-10-24-output.csv

用名,服务器IP,应用停止时间,停止持续时间

app01,10.25.100.36,2019-10-24 02:21,1分钟

app01,10.81.126.19,2019-10-24 02:17,1分钟

app02,10.81.126.19,2019-10-24 10:43,14分钟

......




但是这里输出的只是12小时的数据,所以加个计划任务:


59 11,23 * * * /usr/bin/bash /home/app-alive/send_app_alived.sh




五、python脚本发送邮件


网上随便找个python脚本,修改下:

# cat sendmail.py
# !/usr/bin/env python
# -*- coding: utf-8 -*-
import smtplib
import email.mime.multipart
import email.mime.text
from email.mime.text import MIMEText
from email.mime.multipart import MIMEMultipart
from email.mime.application import MIMEApplication
from email.mime.multipart import MIMEMultipart
from email.mime.base import MIMEBase
import email
import base64
import time
import os

class send_mail:
def __init__(self, From, To, pw, file_path, file_header, file_body):
# 发送人
self.From = From
# 收件人['[email protected]','[email protected]']
self.To = list(To)
#抄送人
# self.Cc = list(Cc)
# 登录邮件密码base64.encodestring('明文')加密后密码
self.pw = pw
# 文件具体路径(路径+文件名称)
self.file_path = file_path
# 标题头
self.file_header = file_header
# 内容
self.file_body = file_body

def login(self):
server = smtplib.SMTP('smtp.qq.com')
server.starttls()
# pwd = base64.decodestring(self.pw)
server.login(self.From, self.pw)
try:
receive = self.To
#receive.extend(self.Cc)
server.sendmail(self.From,self.To,self.atta())
finally:
server.quit()

def atta(self):
main_msg = MIMEMultipart()
# 内容
text_msg = MIMEText(self.file_body)
main_msg.attach(text_msg)
try:
contype = 'application/octet-stream'
maintype, subtype = contype.split('/', 1)

data = open(self.file_path, 'rb')
file_msg = MIMEBase(maintype, subtype)
file_msg.set_payload(data.read())
data.close()
email.encoders.encode_base64(file_msg)
basename = os.path.basename(self.file_path.split('/')[-1])
file_msg.add_header('Content-Disposition', 'attachment', filename=basename)
main_msg.attach(file_msg)
except Exception as ret:
print(ret)

main_msg['From'] = self.From
main_msg['To'] = ";".join(self.To)
# main_msg['Cc'] = ";".join(self.Cc)

# 标题头
main_msg['Subject'] = self.file_header
main_msg['Date'] = email.utils.formatdate()

fullText = main_msg.as_string()

return fullText


if __name__ == '__main__':
fileTime = time.strftime("%Y-%m-%d", time.localtime())
s = send_mail('[email protected]', ['[email protected]','[email protected]'],'jshntbmbejj', "/home/app-alive/"+fileTime+"-output.csv", '应用存活记录', '')
s.login()

	
    print('发送成功!')





好了,就完成了。。。。虽然实现了,但是感觉好傻的样子,open-falcon不好用啊,公司赶紧招个运维开发吧,我好难~~