亚马逊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持续报警,提升您的报警触达率,加速问题的解决。


