Dev/DevOps, Infra

[AWS] SQS event - Lambda - Slack 알람 - PostgresQL 쿼리 실행 2) AWS Lambda에서 slack alert 보내기

HJChung 2022. 5. 7. 15:34

SQS(dead-letter queue) event 발생 시 Lambda를 사용해서 Slack alert를 보내고 PostgresQL에 쿼리를 실행하도록 설정하는 방법을 아래 3가지 순서로 기록해보고자 한다. 

  1. AWS Lambda를 Serverless framework를 사용해서 local에서 개발하고 cli로 쉽게 배포하기
  2. AWS Lambda에서 slack alert 보내기
  3. AWS Lambda에서 PostgresDB로 쿼리 실행하기

완성 시 전체적인 구조는 다음과 같으며 aws credential은 미리 되어있다고 가정한다. 

 

이번에는 <2. AWS Lambda에서 slack alert 보내기>에 대한 내용이다. 

1. slack webhook 생성

우선 slack webhook을 생성하여 해당 slack url을 얻은 뒤 환경변수를 활용한다. 

1) Slack에서 Add an app 클릭한 후 검색 필터에 WebHooks을 검색한다. 

2) Incoming WebHooks 추가

3) 채널을 선택하거나 생성 후 Incoming WebHooks 생성

4) 생성된 Webhook URL 복사하여 저장

5) setting도 가능. 봇이름 및 아이콘 등 기본 설정 변경하여 저장

6) 잘 동작 하는지 확인

$ curl -s -d "payload={'text':'메세지 내용'}" "위에서 복사해 둔 Webhook URL"

 

이렇게 slack alert를 받을 webhook url이 준비되었다면 이를 활용해서 post message를 보내는 Lambda function을 작성한다. 

2. handler.ts

이제 src/functions/hello가 아니라 queueHandler라는 새로운 functions folder를 생성하여 slack alert를 보내는 lambda function을 작성해보자. sample로 주어진 hello와 같은 파일(handler.ts, index.ts)을 생성해주고 hello/handler.ts, hello/index.ts를 참고한다. 

 

handler.ts에는 event 발생시 trigger 될 함수를 작성해준다. 

 실제로는 type validation이나 에러 처리 등 좀 더 고려해서 짜야하지만 이번에는 사용법에 대한 기록에 좀 더 중점을 맞추어서 이 정도로만 작성하였다. 

slack webhook 이나 아래 코드에 대한 자세한 설명은 Use Slack to Monitor SQS Dead-Letter Queue을 참고할 수 있다. 

// src/functions/queueHandler/handler.ts

import axios from 'axios';
import { SQSEvent, SQSRecord } from 'aws-lambda';
import { middyfy } from '@libs/lambda';


const dlqSlackAlert = async (event: SQSEvent) => {
    const record: SQSRecord = event.Records[0];
    const slackUrl: string = process.env.SLACK_URL;
    const payload: payload = {
        blocks: [
            {
                type: 'section',
                text: {
                    type: 'mrkdwn',
                    text: '*🔔 SQS Dead-Letter Queue Alarm | Send undeliverable messages to a dead-letter queue.*',
                },
            },
            {
                type: 'section',
                text: {
                    type: 'mrkdwn',
                    text: `*Message Id: * ${record['messageId']}`,
                },
            },
            {
                type: 'section',
                text: {
                    type: 'mrkdwn',
                    text: `*Event Source ARN: * ${record['eventSourceARN']}`,
                },
            },
            {
                type: 'section',
                text: {
                    type: 'mrkdwn',
                    text: `*AWS Region: * ${record['awsRegion']}`,
                },
            },
            {
                type: 'section',
                text: {
                    type: 'mrkdwn',
                    text: `*Approximate Receive Count: * ${record['attributes']['ApproximateReceiveCount']}`,
                },
            },
            {
                type: 'section',
                text: {
                    type: 'mrkdwn',
                    text: `*Body: * ${record['body']}`,
                },
            },
        ],
    };

    await axios({
        method: 'POST',
        url: slackUrl,
        headers: { 'Content-Type': 'application/json' },
        data: payload,
    });
};



const queueHandler = async (event: SQSEvent) => {
    console.log(`Processing data from queue with records ${JSON.stringify(event.Records)}`);
    try {
        await dlqSlackAlert(event);
    } catch (error) {
        console.error(error);
    }
};

export const main = middyfy(queueHandler);

3.  index.ts

그리고 index.ts에 이 function의 trigger가 되는 event가 뭔지, timeout은 어느 정도인지를 명시해준다. 

// src/functions/queueHandler/index.ts

import { handlerPath } from '@libs/handler-resolver';

export default {
    handler: `${handlerPath(__dirname)}/handler.main`,
    events: [
        {
            sqs: {
                arn: [event trigger로 사용할 sqs dql arn],
            },
        },
    ],
    timeout: [몇 초(s)],
};

4. serverless.ts

그리고 serverless.ts에서 이 queueHandler function을 Lambda에서 사용할 것이며, 환경변수(우리의 경우 위에서 생성한 slack webhook url)에는 뭐가 있다고 명시해준다. 

// serverless.ts

import queueHandler from '@functions/queueHandler';

service: 'dlq-sqs-handler',
frameworkVersion: '2',
plugins: ['serverless-webpack', 'serverless-prune-plugin'],
provider: {
    name: 'aws',
    runtime: 'nodejs14.x',
    region: 'us-west-2',
    role: [생성해 준 role arn],
    environment: {
            AWS_NODEJS_CONNECTION_REUSE_ENABLED: '1',
            NODE_OPTIONS: '--enable-source-maps --stack-trace-limit=1000',
            SLACK_URL: 'https://hooks.slack.com/services/어쩌구 저쩌구'
        },
}

// import the function via paths
functions: { queueHandler }, // HERE!

........
어쩌구
저쩌구

5. Test

여기까지 한 후 위에서 해 본 것처럼

sls invoke local -f queueHandler -p test/sqsErrorSample.json

로 Test 해보면? 의도했던 대로 slack channel에

이런 식으로 alert가 뜰 것이다. 

 

6. Lambda에 배포

테스트 시 문제없이 예상대로 동작한다면 Lambda에 배포해보자.

$ sls deploy

배포 후 aws lambda console에서도 

를 통해 잘 동작하는지 Test 해 볼 수 있다. 

+ 추가로 해보면 좋을 것

Lambda 배포 같은 경우도 Github actions로 CI/CD를 세팅해둘 수 있다. 

또한 Github의 secrets 기능을 이용하면 slack weburl 같은 민감 정보들을 코드에 직접 작성하지 않게 할 수 도 있다. 

이에 대한 자세한 내용은

을 참고할 수 있다. 

 

Reference

Use Slack to Monitor SQS Dead-Letter Queue

Setup CI/CD for your AWS Lambda with Serverless Framework and GitHub Actions

How to deploy Serverless applications using Github Actions