通过Serverless架构实现监控告警能力

本文涉及的产品
Serverless 应用引擎免费试用套餐包,4320000 CU,有效期3个月
函数计算FC,每月15万CU 3个月
简介: 在实际生产中,我们经常需要做一些监控脚本监控我们的网站服务或者API服务是否可用。传统的方法是通过一些网站监控平台(例如DNSPod监控、360网站服务监控,以及阿里云监控等),这些监控平台的原理是通过用户自己设置要监控的服务地址和监测的时间阈值,由监控平台定期发起请求对网站或服务的可用性进行判断。

前言


在实际生产中,我们经常需要做一些监控脚本监控我们的网站服务或者API服务是否可用。传统的方法是通过一些网站监控平台(例如DNSPod监控、360网站服务监控,以及阿里云监控等),这些监控平台的原理是通过用户自己设置要监控的服务地址和监测的时间阈值,由监控平台定期发起请求对网站或服务的可用性进行判断。当然,这些服务很多都是大众化的,通用性很强,但是并不一定适合我们,例如说,我现在需要做这样一个监控程序,我要监控我的网站状态码,不同区域的延时,并且通过监控得到的数据,设定一个阈值,超过这个阈值,会通过邮件等进行统治告警,针对这样一个定制化需求,可能大部分的监控平台就很难完成我们的需求,由此定制开发一个网站状态监控工具就显得尤为重要。

Serverless服务的一个很重要的应用场景就是运维、监控与告警,所以本实验将会通过阿里云函数计算以及腾讯云云函数等FaaS平台,部署相关的监控脚本,对目标网站或者目标服务进行监控告警。


Web服务监控告警


针对Web服务,我们先设计一个简单的监控告警功能的流程:



在这个流程中,我们仅对网站的状态码进行监控,即返回的状态为200,则判定网站可正常使用,否则进行告警:


# -*- coding: utf8 -*-

import ssl

import json

import smtplib

import urllib.request

from email.mime.text import MIMEText

from email.header import Header


ssl._create_default_https_context = ssl._create_unverified_context



def sendEmail(content, to_user):

   sender = 'service@anycodes.cn'

   receivers = [to_user]


   mail_msg = content

   message = MIMEText(mail_msg, 'html', 'utf-8')

   message['From'] = Header("网站监控", 'utf-8')

   message['To'] = Header("站长", 'utf-8')


   subject = "网站监控告警"

   message['Subject'] = Header(subject, 'utf-8')


   try:

       smtpObj = smtplib.SMTP_SSL("smtp.exmail.qq.com", 465)

       smtpObj.login('发送邮件的邮箱地址', '密码')

       smtpObj.sendmail(sender, receivers, message.as_string())

   except smtplib.SMTPException as e:

       print(e)



def getStatusCode(url):

   return urllib.request.urlopen(url).getcode()



def handler(event, context):

   url = "http://www.anycodes.cn"

   if getStatusCode(url) == 200:

       print("您的网站%s可以访问!" % (url))

   else:

       sendEmail("您的网站%s 不可以访问!" % (url), "接受人邮箱地址")

   return None


在这里,timer表示时间触发器,cronExpression是表达式:


创建定时触发器时,用户能够使用标准的 Cron 表达式的形式自定义何时触发。定时触发器现已推出秒级触发功能,为了兼容老的定时触发器,因此 Cron 表达式有两种写法。

Cron 表达式语法一(推荐)

Cron 表达式有七个必需字段,按空格分隔。

其中,每个字段都有相应的取值范围:

Cron 表达式语法二(不推荐)

Cron 表达式有五个必需字段,按空格分隔。

其中,每个字段都有相应的取值范围:

通配符

注意事项

在 Cron 表达式中的“日”和“星期”字段同时指定值时,两者为“或”关系,即两者的条件分别均生效。

示例

*/5 * * * * * * 表示每5秒触发一次

0 0 2 1 * * * 表示在每月的1日的凌晨2点触发

0 15 10 * * MON-FRI * 表示在周一到周五每天上午10:15触发

0 0 10,14,16 * * * * 表示在每天上午10点,下午2点,4点触发

0 */30 9-17 * * * * 表示在每天上午9点到下午5点内每半小时触发

0 0 12 * * WED * 表示在每个星期三中午12点触发


所以可以认为我们上面的代码是每5秒触发一次,当然,我们还可以设置没分钟,每小时触发,这就看我们对我们网站监控的密度是多少了。

以阿里云函数计算为例,我们可以创建函数部署项目:

当我们网站服务不可用时,可以收到告警:


这种网站监控方法是比较简单的,当然也可能会不准。我们经常对网站或者服务进行监控并不是简简单单的看其能不能返回200的状态码,还要看链接耗时、下载耗时以及不同区域、不同运营商访问我们的网站或者服务的延时信息等。所以,我们需要对这个代码进行额外的更新与优化:

  1. 通过在线网速测试的网站,抓包获取不通地区不同运营商的请求特征;
  2. 编写爬虫程序,进行在线网速测试模块的编写;
  3. 集成到刚刚的项目中;
    我们这里以站长工具网站中国内网站测速工具 为例,可以通过网页查到相关信息:
    所以我们可以对依稀网站测速工具进行封装,例如:



通过对网页进行分析,获取请求特征,包括Url,Form data,以及Headers等相关信息,其中该网站在使用不同监测点对网站进行请求时,是通过Form data中的guid的参数实现的,例如部分监测点的guid:


广东佛山 电信 f403cdf2-27f8-4ccd-8f22-6f5a28a01309

江苏宿迁 多线 74cb6a5c-b044-49d0-abee-bf42beb6ae05

江苏常州 移动 5074fb13-4ab9-4f0a-87d9-f8ae51cb81c5

浙江嘉兴 联通 ddfeba9f-a432-4b9a-b0a9-ef76e9499558


此时,我们可以编写基本的爬虫代码,来对Response进行初步解析,以62a55a0e-387e-4d87-bf69-5e0c9dd6b983 江苏宿迁[电信]为例,编写代码:


import urllib.request

import urllib.parse


url = "*某测速网站地址*"

form_data = {

   'guid': '62a55a0e-387e-4d87-bf69-5e0c9dd6b983',

   'host': 'anycodes.cn',

   'ishost': '1',

   'encode': 'ECvBP9vjbuXRi0CVhnXAbufDNPDryYzO',

   'checktype': '1',

}

headers = {

   'Host': 'tool.chinaz.com',

   'Origin': '*某测速网站地址*',

   'Referer': '*某测速网站地址*',

   'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_3) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/74.0.3729.108 Safari/537.36',

   'X-Requested-With': 'XMLHttpRequest'

}


print(urllib.request.urlopen(

   urllib.request.Request(

       url=url,

       data=urllib.parse.urlencode(form_data).encode('utf-8'),

       headers=headers

   )

).read().decode("utf-8"))


获得结果:


({

state: 1,

msg: '',

result: {

ip: '119.28.190.46',

httpstate: 200,

alltime: '212',

dnstime: '18',

conntime: '116',

downtime: '78',

filesize: '-',

downspeed: '4.72',

ipaddress: '新加坡新加坡',

headers: 'HTTP/1.1 200 OK br>Server: ...',

pagehtml: ''

}

})


在这个结果中,我们可以提取部分数据,例如江苏宿迁[电信]访问目标网站的基础数据:


总耗时:alltime:'212'

链接耗时:conntime:'116'

下载耗时:downtime:'78'


此时,我们可以改造代码对更多的节点,进行测试:


江苏宿迁[电信] 总耗时:223 链接耗时:121 下载耗时:81

广东佛山[电信] 总耗时:44 链接耗时:27 下载耗时:17

广东惠州[电信] 总耗时:56 链接耗时:34 下载耗时:22

广东深圳[电信] 总耗时:149 链接耗时:36 下载耗时:25

浙江湖州[电信] 总耗时:3190 链接耗时:3115 下载耗时:75

辽宁大连[电信] 总耗时:468 链接耗时:255 下载耗时:170

江苏泰州[电信] 总耗时:180 链接耗时:104 下载耗时:69

安徽合肥[电信] 总耗时:196 链接耗时:110 下载耗时:73

...


并对项目中的index.py进行代码修改:


# -*- coding: utf8 -*-

import ssl

import json

import re

import socket

import smtplib

import urllib.request

from email.mime.text import MIMEText

from email.header import Header


socket.setdefaulttimeout(2.5)

ssl._create_default_https_context = ssl._create_unverified_context


def getWebTime():


   final_list = []

   final_status = True


   total_list = '''62a55a0e-387e-4d87-bf69-5e0c9dd6b983 江苏宿迁[电信]

   f403cdf2-27f8-4ccd-8f22-6f5a28a01309 广东佛山[电信]

   5bea1430-f7c2-4146-88f4-17a7dc73a953 河南新乡[多线]

   1f430ff0-eae9-413a-af2a-1c2a8986cff0 河南新乡[多线]

   ea551b59-2609-4ab4-89bc-14b2080f501a 河南新乡[多线]

   2805fa9f-05ea-46bc-8ac0-1769b782bf52 黑龙江哈尔滨[联通]

   722e28ca-dd02-4ccd-a134-f9d4218505a5 广东深圳[移动]

8e7a403c-d998-4efa-b3d1-b67c0dfabc41 广东深圳[移动]'''


   url = "*某测速网站地址*"

   for eve in total_list.split('\n'):

       id_data, node_name = eve.strip().split(" ")

       form_data = {

           'guid': id_data,

           'host': 'anycodes.cn',

           'ishost': '1',

           'encode': 'ECvBP9vjbuXRi0CVhnXAbufDNPDryYzO',

           'checktype': '1',

       }

       headers = {

           'Host': '*某测速网站地址*',

           'Origin': '*某测速网站地址*',

           'Referer': '*某测速网站地址*',

           'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_3) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/74.0.3729.108 Safari/537.36',

           'X-Requested-With': 'XMLHttpRequest'

       }

       try:

           result_data = urllib.request.urlopen(

               urllib.request.Request(

                   url=url,

                   data=urllib.parse.urlencode(form_data).encode('utf-8'),

                   headers=headers

               )

           ).read().decode("utf-8")

           try:

               alltime = re.findall("alltime:'(.*?)'", result_data)[0]

               conntime = re.findall("conntime:'(.*?)'", result_data)[0]

               downtime = re.findall("downtime:'(.*?)'", result_data)[0]

               final_string = "%s\t总耗时:%s\t链接耗时:%s\t下载耗时:%s" % (node_name, alltime, conntime, downtime)

           except:

               final_string = "%s链接异常!" % (node_name)

               final_status = False

       except:

           final_string = "%s链接超时!" % (node_name)

           final_status = False

       final_list.append(final_string)

       print(final_string)

   return (final_status,final_list)

def sendEmail(content, to_user):

   sender = 'service@anycodes.cn'

   receivers = [to_user]

   mail_msg = content

   message = MIMEText(mail_msg, 'html', 'utf-8')

   message['From'] = Header("网站监控", 'utf-8')

   message['To'] = Header("站长", 'utf-8')

   subject = "网站监控告警"

   message['Subject'] = Header(subject, 'utf-8')

   try:

       smtpObj = smtplib.SMTP_SSL("smtp.exmail.qq.com", 465)

       smtpObj.login('service@anycodes.cn', '密码')

       smtpObj.sendmail(sender, receivers, message.as_string())

   except smtplib.SMTPException:

       pass


def getStatusCode(url):

   return urllib.request.urlopen(url).getcode()


def handler(event, context):

   url = "http://www.anycodes.cn"

   final_status,final_list = getWebTime()

   if not final_status:

       sendEmail("您的网站%s的状态:<br>%s" % (url, "<br>".join(final_list)), "service@52exe.cn")


由于,是学习为主,所以这里我已经将节点列表进行了缩减,只留了几个。通过部署,可得到结果:



当然,告警的灵敏度和监控的频率,在实际生产过程中可以根据自己的需求进行调整。


云服务监控告警


上一部分,我们以阿里云函数计算为例,对网站状态以及健康等信息进行了监控与告警,在实际的生产运维中,还非常有必要对所使用的服务进行监控,例如在使用Hadoop、Spark的时候要对节点的健康进行监控,在使用K8S的时候要对API网关、ETCD等多维度的指标进行监控,在使用Kafka的时候,也要对数据积压量,以及Topic、Consumer等进行监控;这些服务的监控,往往不能通过简单的URL以及某些状态来进行判断,在传统的运维中,通常会在额外的机器上设置一个定时任务,对相关的服务进行旁路监控。本章则会通过Serverless技术,对云产品进行相关的监控与告警。

在使用云上的Kafka时,我们通常要看数据积压量,因为我们的Consumer集群如果挂掉了,或者消费能力突然降低导致数据积压,很可能会对我们的服务产生不可预估的影响,这个时候对Kafka的数据积压量进行监控告警,就显得额外重要。这一部分我们将会以腾讯云云函数以及相关的产品为例进行实践,并将会通过多个云产品进行组合(包括云监控,Ckafka,云API以及云短信等)来实现一个短信告警、邮件告警以及企业微信告警功能。

首先,可以设计简单的流程图:



在开始项目之前,我们要准备一些基础的模块:

  • Kafka数据积压量获取模块:


def GetSignature(param):

   # 公共参数

   param["SecretId"] = ""

   param["Timestamp"] = int(time.time())

   param["Nonce"] = random.randint(1, sys.maxsize)

   param["Region"] = "ap-guangzhou"

   # param["SignatureMethod"] = "HmacSHA256"

   # 生成待签名字符串

   sign_str = "GETckafka.api.qcloud.com/v2/index.php?"

   sign_str += "&".join("%s=%s" % (k, param[k]) for k in sorted(param))

   # 生成签名

   secret_key = ""

   if sys.version_info[0] > 2:

       sign_str = bytes(sign_str, "utf-8")

       secret_key = bytes(secret_key, "utf-8")

   hashed = hmac.new(secret_key, sign_str, hashlib.sha1)

   signature = binascii.b2a_base64(hashed.digest())[:-1]

   if sys.version_info[0] > 2:

       signature = signature.decode()

   # 签名串编码

   signature = urllib.parse.quote(signature)

   return signature


def GetGroupOffsets(max_lag, phoneList):

   param = {}

   param["Action"] = "GetGroupOffsets"

   param["instanceId"] = ""

   param["group"] = ""

   signature = GetSignature(param)

   # 生成请求地址

   param["Signature"] = signature

   url = "https://ckafka.api.qcloud.com/v2/index.php?Action=GetGroupOffsets&"

   url += "&".join("%s=%s" % (k, param[k]) for k in sorted(param))

   req_attr = urllib.request.urlopen(url)

   res_data = req_attr.read().decode("utf-8")

   json_data = json.loads(res_data)

   for eve_topic in json_data['data']['topicList']:

       temp_lag = 0

       result_list = []

       for eve_partition in eve_topic["partitions"]:

           lag = eve_partition["lag"]

           temp_lag = temp_lag + lag

       if temp_lag > max_lag:

           result_list.append(

               {

                   "topic": eve_topic["topic"],

                   "lag": lag

               }

           )

       print(result_list)

       if len(result_list)>0:

           KafkaLagRobot(result_list)

           KafkaLagSMS(result_list,phoneList)


  • 接入企业微信机器人模块:


def KafkaLagRobot(content):

   url = ""

   data = {

       "msgtype": "markdown",

       "markdown": {

           "content": content,

       }

   }

   data = json.dumps(data).encode("utf-8")

   req_attr = urllib.request.Request(url, data)

   resp_attr = urllib.request.urlopen(req_attr)

   return_msg = resp_attr.read().decode("utf-8")


  • 接入腾讯云短信服务模块:


def KafkaLagSMS(infor, phone_list):

   url = ""

   strMobile = phone_list

   strAppKey = ""

   strRand = str(random.randint(1, sys.maxsize))

   strTime = int(time.time())

   strSign = "appkey=%s&random=%s&time=%s&mobile=%s" % (strAppKey, strRand, strTime, ",".join(strMobile))

   sig = hashlib.sha256()

   sig.update(strSign.encode("utf-8"))


   phone_dict = []

   for eve_phone in phone_list:

       phone_dict.append(

           {

               "mobile": eve_phone,

               "nationcode": "86"

           }

       )

   data = {

       "ext": "",

       "extend": "",

       "params": [

           infor,

       ],

       "sig": sig.hexdigest(),

       "sign": "你的sign",

       "tel": phone_dict,

       "time": strTime,

       "tpl_id": 你的模板id

   }

   data = json.dumps(data).encode("utf-8")

   req_attr = urllib.request.Request(url=url, data=data)

   resp_attr = urllib.request.urlopen(req_attr)

   return_msg = resp_attr.read().decode("utf-8")


  • 发送邮件告警模块:


def sendEmail(content, to_user):

   sender = 'service@anycodes.cn'

   message = MIMEText(content, 'html', 'utf-8')

   message['From'] = Header("监控", 'utf-8')

   message['To'] = Header("站长", 'utf-8')

   message['Subject'] = Header("告警", 'utf-8')

   try:

       smtpObj = smtplib.SMTP_SSL("smtp.exmail.qq.com", 465)

       smtpObj.login('service@anycodes.cn', '密码')

       smtpObj.sendmail(sender, [to_user], message.as_string())

   except smtplib.SMTPException as e:

       logging.debug(e)


完成模块编写,和上面的方法一样,进行项目部署,部署成功,可以进行测试,测试可看到功能可用:

  • 短信告警样式:



  • 企业微信告警样式:



总结

通过该场景实践,希望读者可以了解到Serverless相关产品在运维行业中的基本应用,尤其是监控告警的基本使用方法和初步灵感。设计一个网站监控程序实际上是一个很初级的入门场景,同时也希望大家可以将更多的监控告警功与Serverless技术进行结合,例如监控自己的Mysql压力情况、监控自己已有服务器的数据指标等,通过这些指标的监控告警,不仅仅可以让管理者及时发现服务的潜在风险,也可以通过一些自动化流程实现项目的自动化运维。

通过本场景实践,我们也可以对项目进行额外的优化或者应用在不同的领域以及场景中。例如,我们可以通过增加短信告警、微信告警、企业微信告警等多个维度(后续的应用场景会举例说明),以确保相关人员可以及时收到告警信息;我们也可以通过监控某个小说网站、视频网站等时刻看到我们关注的小说或者视频的更新情况,便于我们追更等。

最后,其实无论是以任何一个云厂商的FaaS平台为例,其实整体效果都是一致的,本文有两个例子,分别以阿里云函数计算和腾讯云云函数为例,也希望读者可以发挥自己的想象,可以在更多的FaaS平台进行学习和实践

相关实践学习
【文生图】一键部署Stable Diffusion基于函数计算
本实验教你如何在函数计算FC上从零开始部署Stable Diffusion来进行AI绘画创作,开启AIGC盲盒。函数计算提供一定的免费额度供用户使用。本实验答疑钉钉群:29290019867
建立 Serverless 思维
本课程包括: Serverless 应用引擎的概念, 为开发者带来的实际价值, 以及让您了解常见的 Serverless 架构模式
目录
打赏
0
1
0
0
1882
分享
相关文章
云端问道9期实践教学-省心省钱的云上Serverless高可用架构
详细介绍了云上Serverless高可用架构的一键部署流程
61 10
卓越效能,极简运维,Serverless高可用架构
本文介绍了Serverless高可用架构方案,当企业面对日益增长的用户访问量和复杂的业务需求时如何实现更高的灵活性、更低的成本和更强的稳定性。
云端问道9期方案教学-省心省钱的云上Serverless高可用架构
本文介绍了省心省钱的云上Serverless高可用架构,主要分为两个部分:1. Serverless的发展历程、特点及高可用架构;2. SAE(Serverless Application Engine)产品介绍。Serverless作为一种云计算模式,让用户无需管理底层基础设施,自动弹性扩展资源,按需付费,极大提高了资源利用率和业务灵活性。SAE作为Serverless计算服务,提供了简便的应用部署、运维自动化、丰富的弹性策略和可观测性等功能,帮助企业降低运营成本、提升研发效率。通过极氪汽车、南瓜电影等客户案例展示了SAE在实际应用中的优势。
卓越效能,极简运维,体验Serverless高可用架构,完成任务可领取转轮日历!
卓越效能,极简运维,体验Serverless高可用架构,完成任务可领取转轮日历!
探索Serverless架构:开发实践与优化策略
本文深入探讨了Serverless架构的核心概念、开发实践及优化策略。Serverless让开发者无需管理服务器即可运行代码,具有成本效益、高可扩展性和提升开发效率等优势。文章还详细介绍了函数设计、安全性、监控及性能和成本优化的最佳实践。
阿里云 SAE 邀您参加 Serverless 高可用架构挑战赛,赢取精美礼品
阿里云 SAE 邀您参加 Serverless 高可用架构挑战赛,赢取精美礼品。
探索Serverless架构:云计算的新前沿
【10月更文挑战第26天】本文探讨了Serverless架构作为新兴的云计算范式,如何改变应用的构建和部署方式。文章介绍了Serverless的核心概念、优势和挑战,并提供了开发技巧和实用工具,帮助开发者更好地理解和利用这一技术。
Serverless架构通过提供更快的研发交付速度、降低成本、简化运维、优化资源利用、提供自动扩展能力、支持实时数据处理和快速原型开发等优势,为图像处理等计算密集型应用提供了一个高效、灵活且成本效益高的解决方案。
Serverless架构通过提供更快的研发交付速度、降低成本、简化运维、优化资源利用、提供自动扩展能力、支持实时数据处理和快速原型开发等优势,为图像处理等计算密集型应用提供了一个高效、灵活且成本效益高的解决方案。
124 1
探索 Serverless 架构:云计算的新浪潮
【10月更文挑战第23天】Serverless 架构是一种新兴的云计算范式,允许开发者构建和运行应用程序而无需管理服务器。本文深入探讨了 Serverless 的核心概念、优势、挑战及最佳实践,帮助开发者更好地理解和应用这一技术。
揭秘云计算中的Serverless架构:优势、挑战与实践
揭秘云计算中的Serverless架构:优势、挑战与实践
147 0

热门文章

最新文章

相关产品

  • 函数计算