본문 바로가기

퍼블릭 클라우드

AWS CloudWatch 경보를 Slack으로 보내는 방법

728x90
반응형

AWS CloudWatch 경보를 Slack으로 보내는 방법

CloudWatch 경보가 발생하면 Lambda 함수가 실행되어 Slack으로 메시지를 전송합니다.

시스템 구성

시스템 구성

1. Amazon SNS

1-1. 주제 생성

  • 이름 : cloudwatch-notification

cloudwatch_notification1
cloudwatch_notification2

1-2 구독

cloudwatch_notification3

메일 확인

cloudwatch_notification4
cloudwatch_notification5
cloudwatch_notification6

1-3 메시지 게시(메시지 발송 테스트)

cloudwatch_notification7
cloudwatch_notification8

메일 확인

cloudwatch_notification9

2. Slack Webhook URL 생성

Slack 워크스페이스에서 Incoming Webhooks을 활성화하고 Webhook URL을 생성합니다.

slack webhooks(incoming webhooks) 생성 방법

#cluodwatchalert
https://hooks.slack.com/services/T018562TM6A/T017ZZ55YUV/wE5gd1fbfjNgxUeLqPUDMTbLR

3. AWS Lambda

새로운 Lambda 함수를 생성합니다.

실행 역할에는 AWSLambdaBasicExecutionRole과 AmazonSNSFullAccess 정책을 추가합니다.

 

AWS 콘솔에서 Lambda > 함수 > 함수 생성 > 블루프린트 > cloudwatch-alarm-to-slack-python > 환경 변수 값 입력

  • 블루프린트 : cloudwatch-alarm-to-slack-python

cloudwatch_notification10
cloudwatch_notification11
cloudwatch_notification12

환경 변수

  • slackChannel : test
  • kmsEncryptedHookUrl : test

lambda 함수 생성 후 코드 및 환경 변수 편집

Lambda 함수 코드에서 CloudWatch 경보를 Slack 메시지로 전송하는 로직을 작성합니다.

 

lambda_function

import boto3
import json
import logging
import os

from base64 import b64decode
from urllib.request import Request, urlopen
from urllib.error import URLError, HTTPError


# The Slack channel to send a message to stored in the slackChannel environment variable
SLACK_CHANNEL = os.environ['slackChannel']


HOOK_URL = os.environ['hookUrl']

logger = logging.getLogger()
logger.setLevel(logging.INFO)


def lambda_handler(event, context):
    logger.info("Event: " + str(event))
    message = json.loads(event['Records'][0]['Sns']['Message'])
    logger.info("Message: " + str(message))

    alarm_name = message['AlarmName']
    #old_state = message['OldStateValue']
    new_state = message['NewStateValue']
    reason = message['NewStateReason']

    slack_message = {
        'channel': SLACK_CHANNEL,
        'text': "%s state is now %s: %s" % (alarm_name, new_state, reason)
    }

    req = Request(HOOK_URL, json.dumps(slack_message).encode('utf-8'))
    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)

환경 변수 편집

  • 키:값 생성
    • hookUrl:webhook URL
    • slackChannel:#채널명

cloudwatch_notification13

4. Amazon IAM 정책 및 역할 생성

이 정책은 Lambda 함수가 SNS에 메시지를 게시할 수 있도록 허용해야 합니다.

이후 Lambda 함수를 실행할 IAM 역할을 생성하고 위에서 생성한 정책을 연결합니다.

  • CloudWatchReadOnlyAccess
  • AWSLambdaBasicExecutionRole-xxxx
  • kms derypt

cloudwatch_notification14

kms-lambda-cloudwatch-notification

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Sid": "Stmt1443036478000",
            "Effect": "Allow",
            "Action": [
                "kms:Decrypt"
            ],
            "Resource": [
                "arn:aws:kms:ap-northeast-2:123456789:key/d3s3ceb5-cc9b-4a25-b123-11d86gr346ce4"
            ]
        }
    ]
}

5. Amazon CloudWatch 경보 생성

새로운 경보를 생성합니다.

"액션" 부분에서 "SNS 토픽"을 선택하고 위에서 생성한 SNS 토픽을 연결합니다.

6. slack 메시지

cloudwatch_notification15

7. 기타(메시지 포맷 편집)

lambda > lambda_function > slack_message 편집

import boto3
import json
import logging
import os

from base64 import b64decode
from urllib.request import Request, urlopen
from urllib.error import URLError, HTTPError

# The Slack channel to send a message to stored in the slackChannel environment variable
SLACK_CHANNEL = os.environ['slackChannel']
HOOK_URL = os.environ['hookUrl']

logger = logging.getLogger()
logger.setLevel(logging.INFO)

def lambda_handler(event, context):
    logger.info("Event: " + str(event))
    message = json.loads(event['Records'][0]['Sns']['Message'])
    logger.info("Message: " + str(message))

    alarm_name = message['AlarmName']
    #old_state = message['OldStateValue']
    new_state = message['NewStateValue']
    reason = message['NewStateReason']

    color = '00e200'
    username = 'CloudWatch'
    
    if new_state == 'ALARM':
        color = '#ff0000'

    slack_message = {
        'channel': SLACK_CHANNEL,
        'username': username,
        'pretext': "%s: state - %s" % (alarm_name, new_state),
        'color': color,
        'text': "%s state is now %s: %s" % (alarm_name, new_state, reason)
    }
    
    req = Request(HOOK_URL, json.dumps(slack_message).encode('utf-8'))
    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)

cloudwatch_notification16

 

반응형