본문 바로가기

퍼블릭 클라우드

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

반응형

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
728x90

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

 

728x90
반응형