なぜ、アラートの重要度で色分けが必要なのか
現状、CloudWatchメトリクスでしきい値を超えるとSlackへアラートが飛ぶようになっています。
しかし、新たにSlackチャンネルへ入ってきた人は、通知を見ても何が重大なのかわからないのが実際のところです。
さらに、通知が来るたびにダッシュボードに確認しに行くのも手間です。
そもそも、必要ない通知があることも、、、
そこで、通知の精査と重要度分けが必要だなということで色分けすることにしました。
なにを使っているのか
以下、構成
CloudWatchメトリクス → Simple Notification Service (SNS) → AWS Lambda Function (Lambda) → Slack通知
どうやって構築するか
SNSを重要度別に複数作成し、LambdaでどのSNSかを判定し、色分けしてSlackへ通知
というのがミソになります
Slack通知用のLambdaを作成
Slack通知に関しては基本的に以下を参考にしています。
通知用のLambdaで実行するpythonコードです。
※ ちなみに、以下のコードではKMSを使っていません。
from __future__ import print_function
import boto3
import json
import logging
import os
from base64 import b64decode
from urllib2 import Request, urlopen, URLError, HTTPError
HOOK_URL = os.environ['HookUrl']
SLACK_CHANNEL = os.environ['slackChannel']
logger = logging.getLogger()
logger.setLevel(logging.INFO)
AWS_REGION = os.environ['AWS_REGION']
def lambda_handler(event, context):
logger.info("Event: " + str(event))
message = json.loads(event['Records'][0]['Sns']['Message'])
topicArn = str(event['Records'][0]['Sns']['TopicArn'])
logger.info("Message: " + str(message))
alarm_name = message['AlarmName']
new_state = message['NewStateValue']
reason = message['NewStateReason']
# topicArnをeventからパースして、WarningかCriticalが含まれているかを判定しています
if ("slack-alert-warning" in topicArn):
color = "warning"
elif ("slack-alert-critical" in topicArn):
color = "danger"
if (new_state == "ALARM"):
new_state = ":exclamation: " + new_state
elif (new_state == "OK"):
new_state = ":white_check_mark: " + new_state
color = "good"
url = "<https://" + AWS_REGION + ".console.aws.amazon.com/cloudwatch/home?region=" + AWS_REGION + "#cw:dashboard=Home|Link to cloudwatch>"
# attachmentsを使えばcolorで色付けできます
slack_message = {
'channel': SLACK_CHANNEL,
'attachments': [{ "color": color, "text": "%s \n alarm_name: `%s` \n state is now %s: ```%s```" % (url, alarm_name, new_state, reason) }]
}
req = Request(HOOK_URL, json.dumps(slack_message))
try:
response = urlopen(req)
response.read()
logger.info("Message posted to %s", slack_message['channel'])
except HTTPError as e:
logger.error("Request failed: %d %s", e.code, e.reason)
except URLError as e:
logger.error("Server connection failed: %s", e.reason)
LambdaをCloudFormationで作る場合は以下を参考にしてください。
上記のURL通りに、手で作るのもOKです!
LambdaFunction:
Type: "AWS::Lambda::Function"
Properties:
Code:
S3Bucket: << ソースを置いているバケット名 >>
S3Key: << バケットからソースへの相対パス >>
FunctionName: Alert-message-to-slack
Handler: lambda_function.lambda_handler
MemorySize: 128
Role: lambda_basic_execution
Runtime: python3.6
Environment:
Variables:
HookUrl: << IncomingWebのURL >>
slackChannel: << アラートを流すSlackのChannel名 >>
CloudFormationでSNSを作成
---
AWSTemplateFormatVersion: '2010-09-09'
Parameters:
LambdaFunction:
Type: String
Default: << lambda fuction の ARN >>
Resources:
# SNS Topic
WarningLevelAlert:
Type: AWS::SNS::Topic
Properties:
DisplayName: slack alert
TopicName: slack-alert-warning
Subscription:
- Endpoint: ! Ref LambdaFunction
Protocol: lambda
CriticalLevelAlert:
Type: AWS::SNS::Topic
Properties:
DisplayName: slack alert
TopicName: slack-alert-critical
Subscription:
- Endpoint: ! Ref LambdaFunction
Protocol: lambda
# Lambda Permission
WarningLevelAlertPermission:
Type: AWS::Lambda::Permission
Properties:
Action: lambda:InvokeFunction
FunctionName: !GetAtt LambdaFunction.Arn
Principal: sns.amazonaws.com
SourceArn: !Ref WarningLevelAlert
CriticalLevelAlertPermission:
Type: AWS::Lambda::Permission
Properties:
Action: lambda:InvokeFunction
FunctionName: !GetAtt LambdaFunction.Arn
Principal: sns.amazonaws.com
SourceArn: !Ref CriticalLevelAlert
Outputs:
WarningLevelAlertSNSTopic:
Description: SNS Topic Name
Value: !Ref WarningLevelAlert
Export:
Name: WarningLevelAlertSNSTopic
CriticalLevelAlertSNSTopic:
Description: SNS Topic Name
Value: !Ref CriticalLevelAlert
Export:
Name: CriticalLevelAlertSNSTopic
試しに、lambda function作成時にアラームを設定
LambdaのメトリクスにSNSを登録する部分は共通部分になるので、1つのyamlに分けS3へアップロードしておく。
---
AWSTemplateFormatVersion: '2010-09-09'
Description: Lambda Cloudwatch
Parameters:
FunctionName:
Type: String
Default: << lambda function の名前 >>
Errors:
Type: Number
Default: 1
Resources:
ErrorsAlarm:
Type: AWS::CloudWatch::Alarm
Properties:
AlarmName:
Fn::Join:
- ''
- - !Ref FunctionName
- '/Lambda/Errors'
AlarmDescription:
Fn::Join:
- ''
- - 'Lambda/Errors/'
- !Ref FunctionName
- '/'
- !Ref Errors
- 'over'
AlarmActions:
- !ImportValue CriticalLevelAlertSNSTopic
MetricName: Errors
Namespace: AWS/Lambda
Statistic: Sum
Period: '60'
EvaluationPeriods: '1'
Threshold: !Ref Errors
ComparisonOperator: GreaterThanOrEqualToThreshold
Dimensions:
- Name: FunctionName
Value: !Ref FunctionName
Lambda作成時に、LambdaのメトリクスにSNSを登録する
# 省略
LambdaFunction:
Type: "AWS::Lambda::Function"
# 省略
CloudWatch:
Type: AWS::CloudFormation::Stack
Properties:
TemplateURL:
Fn::Join:
- ''
- - 'https://s3.amazonaws.com/'
- << S3 バケット名 >>
- '/cloudwatch-lambda-alert.yaml'
Parameters:
FunctionName: !Ref LambdaFunction
結果
Slackのアラート通知がわかりやすくなった、、かな
Linkの名前が変わっているのはご愛嬌ということでw