亚马逊AWS官方博客
使用Amazon Step Functions实现Amazon CloudWatch持续报警
背景介绍
监控与报警系统对企业的生产环境来说是不可或缺的,完善的监控与及时的报警通知可以帮助企业快速定位并解决问题,从而减少经济损失。
Amazon CloudWatch是亚马逊云上的统一监控平台,实现对云上所有资源的监控。Amazon CloudWatch Alarm可以基于Amazon CloudWatch Metrics里面的指标,根据您自定义的规则触发警报。通过集成Amazon SNS和Amazon Lambda,您可以实现邮件、短信、企业微信、钉钉、飞书等多种方式的通知。
但是,Amazon CloudWatch Alarm仅会在警报从【正常】状态转变为【告警】状态时触发一次警报。此后,即使警报仍然处于【告警】状态,也不会有新的通知产生。
本文将介绍,如何基于无服务器函数编排工具Amazon Step Functions,在警报被触发后轮询警报状态,实现持续报警的效果,以强调警报的存在,确保您系统中的问题可以得到足够的重视。
架构图
- 您可以根据您的需要,创建Amazon CloudWatch Alarm。
- Amazon CloudWatch Alarm会通过Amazon EventBridge,触发Amazon Step Functions工作流
- Amazon Step Functions工作流的具体内容见下文。工作流中会包含以下两个函数:
- HandleAlarm:您自定义的警报处理函数,用来实现通知逻辑。
- CheckAlarm:下文会指导您创建的警报状态检查函数,用来判断是否继续发出通知。
配置步骤
创建Lambda函数以检查警报状态
创建一个Lambda函数,命名为CheckAlarm
,使用Amazon SDK检测警报状态。比如,如果使用Python 3.8作为runtime创建此函数,可以参考如下代码:
import boto3
cloudwatch = boto3.client('cloudwatch')
def lambda_handler(event, context):
print(f'event={event}')
try:
# check alarm state
res = cloudwatch.describe_alarms(AlarmNames=[event['alarmName']])
print(f'res={res}')
if 'MetricAlarms' in res:
return res['MetricAlarms'][0][
'StateValue'] # 'OK'|'ALARM'|'INSUFFICIENT_DATA'
elif 'CompositeAlarms' in res:
return res['CompositeAlarms'][0][
'StateValue'] # 'OK'|'ALARM'|'INSUFFICIENT_DATA'
else:
print('no alarms found')
return 'ERROR'
except Exception as e:
print(e)
return 'ERROR'
此函数需要调用cloudwatch:DescribeAlarms
API,所以需要为此函数所使用的IAM Role添加此权限。
此函数会自动从event
参数中解析出警报的名称。最终返回值为OK/ALARM/INSUFFICIENT_DATA/ERROR中的一个。
创建Amazon Step Functions
使用如下内容创建Step Functions工作流:
{
"Comment": "Handle Alarm Periodically",
"StartAt": "In Alarm?",
"States": {
"In Alarm?": {
"Type": "Choice",
"Choices": [
{
"Variable": "$.detail.state.value",
"StringEquals": "ALARM",
"Next": "HandleAlarm"
}
],
"Default": "Pass"
},
"HandleAlarm": {
"Type": "Task",
"Resource": "arn:aws:states:::lambda:invoke",
"OutputPath": "$.Payload",
"Parameters": {
"FunctionName": "<AlarmHandler>",
"Payload.$": "$"
},
"Retry": [
{
"ErrorEquals": [
"Lambda.ServiceException",
"Lambda.AWSLambdaException",
"Lambda.SdkClientException"
],
"IntervalSeconds": 2,
"MaxAttempts": 6,
"BackoffRate": 2
}
],
"Next": "Wait",
"InputPath": "$$.Execution.Input"
},
"Wait": {
"Type": "Wait",
"Seconds": 300,
"Next": "CheckAlarm"
},
"CheckAlarm": {
"Type": "Task",
"Resource": "arn:aws:states:::lambda:invoke",
"OutputPath": "$.Payload",
"Parameters": {
"FunctionName": "<CheckAlarm>",
"Payload.$": "$"
},
"Retry": [
{
"ErrorEquals": [
"Lambda.ServiceException",
"Lambda.AWSLambdaException",
"Lambda.SdkClientException"
],
"IntervalSeconds": 2,
"MaxAttempts": 6,
"BackoffRate": 2
}
],
"Next": "Still In Alarm?",
"InputPath": "$$.Execution.Input.detail"
},
"Still In Alarm?": {
"Type": "Choice",
"Choices": [
{
"Variable": "$",
"StringEquals": "ALARM",
"Next": "HandleAlarm"
},
{
"Variable": "$",
"StringEquals": "OK",
"Next": "Pass"
}
],
"Default": "Fail"
},
"Pass": {
"Type": "Pass",
"End": true
},
"Fail": {
"Type": "Fail"
}
}
}
您需要把上述文档中的如下内容进行替换:
<AlarmHandler>
: 响应警报的Lambda函数的ARN。您需要自行创建此函数,并在里面实现您的通知/处理逻辑。<CheckAlarm>
: 刚才创建的CheckAlarm
函数的ARN。
如果使用此文档创建Step Functions,那么轮询周期为5分钟(300秒)。您可以通过修改文档中的下述内容来修改轮询周期:
"Wait": {
"Type": "Wait",
"Seconds": 300, // 修改这个值,单位为秒
"Next": "CheckAlarm"
},
当警报状态改变,触发此状态机时,会顺序执行下述操作:
- 警报的状态是否是
ALARM
。如果不是,则跳转到Pass
状态并结束状态机 - 调用
HandleAlarm
函数,根据您自定义的逻辑发出通知 - 进入
Wait
状态,等待一段时间 - 调用
CheckAlarm
函数,判断警报状态- 如果函数返回
OK
,即警报已经恢复正常,则进入Pass
状态并结束状态机 - 如果函数返回
ALARM
,即警报仍然在告警,则回到步骤1 - 如果函数返回其他值,视为故障并进入
Fail
状态,结束状态机
- 如果函数返回
此状态机的两个Lambda函数都在InputPath
中引用了$$.Execution.Input 变量,使它们即使被多次触发,或者在循环中被触发,也会有一致的输入值。
配置CloudWatch警报
根据您的需求,创建CloudWatch警报。此方案会使用EventBridge触发Step Functions,所以它在创建的时候不需要配置任何通知。删除所有的通知,并直接点击Next.
创建完毕后,复制EventBridge规则
创建EventBridge规则,目标为刚才创建的Step Functions状态机:
至此,每当警报状态发生改变的时候,状态机就会被触发。如果警报的状态为【报警中】,状态机就会发出持续通知。
结束语
综上所述,您只需要准备好您自定义的警报处理函数HandleAlarm ,并将其集成到上述系统中,即可实现CloudWatch
持续报警,提升您的报警触达率,加速问题的解决。