본문 바로가기

AWS

[AWS] Cloudwatch 로그 슬랙 전송하기 #2

AWS의 다양한 서비스에서 발생하는 로그들을 슬랙에서 모니터링하기
사전 지식 : 슬랙 webhook, lambda, sns 등 

 

https://hyeongbin.tistory.com/17

 

[AWS] Cloudwatch 로그 슬랙 전송하기 #1

해당 포스팅에선 aws의 서비스 로그들이 어떤 식으로 cloudwatch에 저장되고, 어떤 식으로 가공하는지에 대한 방법 위주로 정리  개요개발자 분과 협업중 개발자 분이 ECS에서 실행되는 배치 로그

hyeongbin.tistory.com

(기존에 작성한 람다에서 cloudwatch 로그그룹에 저장된 로그를 슬랙으로 전송하는 과정에 대한 내용)

 

AWS의 모든 서비스는 실행, 중지, 종료와 같은 이벤트가 발생될 때마다 이벤트에대한 로그가 생성된다.

이런 로그들을 수집하기 위한 다양한 방법들이 있는데 대표적으로 아래와 같은 방법이 있다.

  • Cloudwatch 서비스의 로그그룹에 저장하여 조회하는 방법
  • AWS 서비스와 SNS을 연동 후 람다에서 SNS을 구독하여 수집하는 방법

이 밖에도 여러 방법들이 있겠지만, 본인은 주로 2번째 방법을 사용한다.

이번 글에선 aws의 비용 알림 서비스인 budget에서 발생한 알람을 람다를 통해 슬랙으로 전송하는 과정에 대해 정리해보려고 한다.

동작 과정에 대한 아키텍처이다. 예시는 2개의 계정에서 각각 Budget과 람다를 구성했지만, 개인 계정에선 단일로 구성하면 된다.

이 글은 서비스에서 발생한 로그, 람다가 주된 내용이므로 Budget과 SNS에 대한 내용은 정리하지 않겠다.

 

Budgets에 대한 자세한 내용은 아래 글을 참고하자

 

[AWS] Budgets로 비용 모니터링

Budgets 설정으로 비용 폭탄 방지하기 https://aws.amazon.com/ko/aws-cost-management/aws-budgets/ 사용자 지정 비용 및 사용 예산 설정 - AWS Budgets - Amazon Web ServicesAWS Budgets를 사용하면 사용자 지정 예산을 설정

hyeongbin.tistory.com

 

 

 

람다 테스트 코드 작성

먼저 각자 사용할 언어에 맞게 람다 함수를 생성하고 아래 테스트 항목에서 sns-notification을 지정하여 이벤트 로그 형식을 보자

sns는 이벤트 발생 시 위와 같이 이벤트 로그가 발생한다. (템플릿을 보면 각종 서비스들에서 발생하는 이벤트 json을 확인할 수 있음)

"Subjent", "Message"을 제외한 key는 SNS에 대한 정보이고 SNS와 연동된 서비스에서 발생한 메시지는 "Message"에 포함된다. 

 

아래는 실제 Budget을 통해 발생된 이벤트 로그이다.

{
    "Records": [
        {
            "EventSource": "aws:sns",
            "EventVersion": "1.0",
            "EventSubscriptionArn": "arn:aws:sns:ap-northeast-2:012345678912:aws-budget-slack:07b48005-1176-4399-a2dd-82f3c619b56e",
            "Sns": {
                "Type": "Notification",
                "MessageId": "09783327-7fe6-5f19-86c4-6d827cf5a450",
                "TopicArn": "arn:aws:sns:ap-northeast-2:012345678912:aws-budget-slack",
                "Subject": "AWS Budgets: budgets-alarm has exceeded your alert threshold",
                "Message": "AWS Budget Notification May 28, 2024\nAWS Account 012345678912\n\nDear AWS Customer,\n\nYou requested that we alert you when the ACTUAL Cost associated with your budgets-alarm budget is greater than $17.75 per day. Yesterday, the ACTUAL Cost associated with this budget is $36.35. You can find additional details below and by accessing the AWS Budgets dashboard [1].\n\nBudget Name: budgets-alarm\nBudget Type: Cost\nBudgeted Amount: $88.75\nAlert Type: ACTUAL\nAlert Threshold: > $17.75\nACTUAL Amount: $36.35\n\n[1] https://console.aws.amazon.com/billing/home#/budgets\n",
                "Timestamp": "2024-04-26T01:18:11.507Z",
                "SignatureVersion": "1",
                "Signature": ""
                "SigningCertUrl": ""
                "UnsubscribeUrl": ""
            }
        }
    ]
}

메시지 내용에서 아래 부분이 budget 관련된 내용이다. (민감 정보는 block 처리했다.)

  • Budgeted Amount : $88.75
  • Alert Threshold : $17.75
  • ACTUAL Amount : $36.35

결과적으로 메시지 내용을 정리해보면, 설정한 예산(88.75$)에서 임계 값으로 설정한 17.75$이 넘는 36.35$이 발생했다는 내용이다.

이제 이 내용을 람다를 통해 파싱하고 슬랙으로 전송해보자

 

 

 

람다 함수 작성

import re
import boto3
from datetime import datetime
import pytz
from urllib.request import Request, urlopen
import json
import logging

slack_webhook_url = "slackchannel url"
ses_client = boto3.client("ses", region_name="ap-northeast-2")

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

# UTC → KST 변환 함수
def time_convert(aws_time):
    utc_time = datetime.strptime(aws_time, "%Y-%m-%dT%H:%M:%S.%fZ").replace(
        tzinfo=pytz.utc
    )
    kst = pytz.timezone("Asia/Seoul")
    kst_time = utc_time.astimezone(kst)
    return kst_time.strftime("%Y년 %m월 %d일 %H시 %M분")

# 메인 람다 함수(이벤트 로그 파싱 후 슬랙에 전달)
def lambda_handler(event, context):
    result = event["Records"]
    logger.info("Event: " + str(event))
    logger.info("Message " + str(result))
    
    if "alert threshold" in str(result[0]["Sns"]["Subject"]):
    # 계정, 시간
        account_id = (re.split(":", result[0]["EventSubscriptionArn"]))[4]
        time = time_convert(result[0]["Sns"]["Timestamp"])
        
    # 슬랙으로 전송할 비용 관련 항목 추출하여 변수에 저장
    budgeted_amount = re.search(r"Budgeted Amount: (\$[\d,]+\.\d{2})", result[0]["Sns"]["Message"])
    alert_threshold = re.search(r"Alert Threshold: > (\$[\d,]+\.\d{2})", result[0]["Sns"]["Message"]    )
    actual_amount = re.search(r"ACTUAL Amount: (\$[\d,]+\.\d{2})", result[0]["Sns"]["Message"])

    if budgeted_amount and alert_threshold and actual_amount :
        budgeted_amount = budgeted_amount.group(1)
        alert_threshold = alert_threshold.group(1)
        actual_amount = actual_amount.group(1)

    slack_message = {
        "text": (
            "*%s 계정 Budget 알림*\n\n"
            ">>>*발생 날짜 :* %s\n"
            "*예산 금액 :* %s\n"
            "*임계 금액 :* %s\n"
            "*실제 금액 :* %s\n\n"
            "%s"
        ) % (
            account_id,
            time,
            budgeted_amount,
            alert_threshold,
            actual_amount,
            f"설정한 예산을 초과하는 비용이 발생했습니다. 특이사항이 발생했는지 확인이 필요합니다.\nhttp://us-east-1.console.aws.amazon.com/costmanagement/home?region=ap-northeast-2#/cost-explorer"
        )
    }
    req = Request(slack_webhook_url, json.dumps(slack_message).encode("utf-8"))
    response = urlopen(req)
    response.read()

어렵지 않게 람다 함수를 작성하였다. 참고로 필요한 라이브러리는 로컬에서 zip 형태로 말아 람다의 layer에 업로드하여 사용했다.

람다 코드에 정의한 기능은 아래와 같다.

  • UCT > KST 변환 함수 (time_convert 함수)
  • 계정 정보와 시간 정보 추출 (account_id, time)
  • 정규표현식을 사용하여 Message의 내용중 슬랙에 전달할 비용 항목 부분을 추출하여 변수에 저장
  • 슬랙에 전달할 템플릿 정의 (slack_message)
  • 슬랙 전송 (req, response) > 함수로 구성해도 됨

이제 작성한 코드를 deploy하고 TEST 시, 아래와 같이 슬랙으로 알람이 잘 전송되는걸 확인할 수 있다.

 

 

 

 

(추가 내용)

위와 같이 람다를 작성해도 동작은 하지만 추가로 고려할 부분이 있다.

 

Budget 경우, budget을 통해 발생하는 이벤트는 총 2종류이다. (임계 비용에 대한 데일리 알람, 실시간 임계치 초과 알람) 

각각의 알람에 따라 추출해야할 데이터가 다르기에 A알람이 발생했을 때 필요한 함수, B알람이 발생했을 때 필요한 함수가 모두 다르다.

 

또한 A, B알람이 발생했을 때 A는 항상 A에 대한 함수, B알람은 항상 B에 대한 함수가 실행되어 같은 결과가 나올 수 있도록 멱등성에 대한 부분도 고려하여 코드를 작성해야 한다.

 

만약 멀티어카운트 환경이라면, 관리 계정의 람다에서 각각의 계정 정보를 관리한다면 이벤트에 포함된 계정 정보 등을 비교하여 특정 계정에서 발생한 알림임을 판단하여 각 계정의 슬랙에 전송할 수도 있다.

만약 계정이 추가되면 딕셔너리에 계정 정보만 추가해주면 되니, 신규 계정이 생성됐을 때 간단하게 연동이 가능하다.

 

항상 현재 상황을 고려하고, 이후 유지보수 편의성까지 고려하여 코드를 작성해야함이 필요하다.

'AWS' 카테고리의 다른 글

[AWS] AWS API 동작 방식  (1) 2024.07.21
[AWS] Secrets Manager  (0) 2024.06.27
[AWS] Budgets로 비용 모니터링  (0) 2024.05.29
[AWS] ACM 인증서 발급 과정 (Certificate Manager)  (0) 2024.03.31
[AWS] Cloudwatch 로그 슬랙 전송하기 #1  (0) 2024.03.06