IT/AWS

AWS CodePipeline에 Slack 알람 적용(Lambda, CloudWatch Events 연동)

twofootdog 2020. 3. 18. 17:36

이번 글에서는 CI/CD 파이프라인(CodePipeline)과 Slack 알람을 연동하는 방법에 대해서 알아볼 것이다.

CodePipeline(CodeCommit & CodeBuild) 시작/종료(성공/실패) 시 Amazon CloudWatch Events에서 해당 이벤트를 감지한 후 AWS Lambda로 트리거 신호를 보내면 AWS Lambda에서 Slack채널로 알람을 보내는 프로세스다.

글의 순서는 다음과 같다.

 

  1. Amazon CloudWatch Events, AWS Lambda란?
  2. 실습 전 준비사항
  3. Slack Webhook 생성
  4. AWS Lambda 함수 생성
  5. AWS CloudWatch Events 생성
  6. 테스트

위와 같은 방식으로 구축을 하면 최종 프로세스는 다음 그림과 같다

(우리가 이 글에서 진행하는 내용은 빨간색으로 표시된 부분)

 

 

 


1. Amazon CloudWatch Events, AWS Lambda란?

1-1. Amazon CloudWatch Events란?

Amazon CloudWatch Event(혹은 AWS CloudWatch Events)는 AWS 상태 변경 등을 감시할 수 있는 기능이다. 만약 사용자가 만든 규칙에 맞는 이벤트가 발생하게 되면 해당 이벤트가 사정에 정의된 규칙과 일치할 경우 하나 이상의 대상 작업을 호출한다. 이벤트 유형에 따라 알람을 보내거나, 이벤트 정보를 캡쳐하거나, 교정작업을 수행하거나, 이벤트를 시작하거나, 기타 작업을 수행할 수 있다. 

이벤트가 발생할 경우 대상 작업은 AWS Lambda함수, AWS Kinesis 스트림, AWS SQS, Amazon SNS 등이 있다. 

사용되는 사례로는 1) 이벤트가 발생하면 Lambda함수를 사용하여 Slack채널로 알림을 전달하거나, 2) AWS 상태 이벤트가 발생하면 Lambda 및 CloudWatch Events를 사용하여 Amazon SNS로 사용자 지정 텍스트 또는 SMS 알림을 보내는 등이다. 

 

 

1-2. AWS Lambda란?

AWS Lambda는 AWS에서 제공하는 FaaS(Function as a Service)다. Lambda 함수를 사용하게 되면 서버를 프로비저닝 하거나 관리할 필요 없이 코드를 실행할 수 있으며, 사용한 컴퓨팅 시간에 대해서만 비용을 지불하면 된다. 다른 AWS 서비스에서 AWS Lambda 함수로 작성한 코드를 자동으로 트리거하도록 설정하거나 웹 또는 모바일 앱에서 직접 코드를 호출할 수 있다. Lambda말고도 Google Functions, Azure Functions 등이 존재하지만 아직까진 Lambda의 사용률이 높다. 

2020년 3월 19일 기준으로 Lambda 함수에서 지원되는 언어는 Java11, Node.js 12.x, Python3.8 , Ruby2.7, Go 1.x, .NET core2.1 가 있다.

 

 

 


2. 실습 전 준비사항

실습을 진행하기 위해서는 다음과 같이 준비가 되어 있어야 한다.

  1. CI/CD 파이프라인 필요. 필자는 이번 실습을 하기 전에 CodeCommit, CodeBuild가 합쳐진 CodePipeline으로 CI/CD 파이프라인을 만들었다(https://twofootdog.tistory.com/47?category=868323 참고). 배포 대상이 쿠버네티스 클러스터이기 때문에 CodeDeploy는 구축하지 않았다.
  2. 필자는 kops를 활용하여 AWS에 쿠버네티스 클러스터를 만들었다(https://twofootdog.tistory.com/43?category=868323 참고). 하지만 이 글은 CloudWatch, Lambda, Slack 연동이기 때문에 CodePipeline만 구축되어 있다면 굳이 쿠버네티스 클러스터를 구축할 필요는 없다.
  3. 알람을 받을 Slack Workspace 및 채널 필요

 

 

 

 


3. Slack Webhook 생성

가장 처음으로 Slack Webhook을 만들어 볼 것이다.

Slack Webhook을 만들게 되면 Webhook URL이 생기게 되는데, 해당 URL은 특정 이벤트가 발생할 때마다 AWS Lambda함수에서 호출하게 될 것이다.

우선 https://api.slack.com/ 로 가서 Webhook을 검색한 후, [Incoming Webhooks]를 선택한다. 

 

 

 

다음에 [Create your Slack app]을 클릭한 후 [App Name]을 임의로 작성하고, [Development Slack Workspace]에 Slack 알람을 보낼 Slack Workspace를 선택한다.

 

 

 

그러면 [Basic Information] 페이지로 넘어가게 되는데 이곳에서는 조금 전 만든 Webhook에 대한 Credential 정보나 Display 정보 등을 확인할 수 있다. 이제 이 Webhook을 내 Slack 채널에 생성해야 한다. [Incomming Webhooks]를 선택해보자.

 

 

 

[Activate Incoming Webhooks] 의 오른쪽 슬라이드를 Off->On으로 바꾸면 아래 Webhook 정보가 나온다. 

맨 밑에 [Add New Webhook to Workspace]를 선택하자.

 

 

 

그러면 Webhook을 내 Slack Workspace에 permission을 요청하는 페이지가 뜨고 Slack 채널을 선택하는 화면이 나온다. 알람을 보낼 채널을 선택하고 [Allow]를 누르자.

 

 

 

그러면 Webhook URL과 Channel이 추가된다. 해당 정보는 AWS Lambda함수 생성 시, Lambda 함수에서 Slack으로 알람을 보내는 곳에서 사용된다. 잘 적어놓자.

 

 

 

Slack App을 실행시켜서 조금 전 Webhook을 추가한 채널을 확인해보면 다음과 같은 메시지가 온 것을 확인할 수 있다. 

메시지가 왔으면 Webhook이 정상적으로 추가된 것이다. 

 

 

 

 


4. AWS Lambda 함수 생성

Slack Webhook을 생성했으니 이제 해당 Slack Webhook에 알람을 보낼 AWS Lambda 함수를 만들어보자. 

[AWS Management Console] -> [Lambda] -> [함수] -> [함수생성] 으로 간다. 

 

 

 

[함수 생성]에서 [새로 작성]을 선택한다. 기본 정보의 [함수 이름]을 Lambda 함수이름을 작성하고, [런타임]에서는 해당 함수가 사용할 언어를 선택하자. 필자는 가장 예제코드가 많은 nodejs를 선택했다. 셋팅이 완료되었으면 [함수생성] 버튼을 클릭하자. 

 

 

 

그러면 Lambda함수가 생성된다. 우선 Designer에는 방금 생성한 Lambda함수만 순서도처럼 그려져있다. 이 화면에서 해당 Lambda함수를 호출하는 트리거나, Lambda함수가 호출하는 대상이 추가된다. 나중에 트리거에 CloudWatch Events를 등록해서 해당 이벤트가 발생할 때마다 Lambda함수가 호출되게 할 것이다.

조금 아래로 내려보면 [함수 코드] 항목이 나온다. 해당 항목에서는 Lambda함수가 호출하는 함수 코드를 작성하는 곳이다. 그 다음에는 Lambda함수에서 사용되는 환경변수와 태그, 그리고 IAM 역할 정보 등이 나온다.

 

 

 

 

Lambda 함수를 만들었으니 함수에 Slack Webhook을 호출하는 소스 코드를 작성해보자.

코드를 작성할 때 CloudWatch Events에서 Lambda로 보내는 event 변수는https://docs.aws.amazon.com/ko_kr/codepipeline/latest/userguide/detect-state-changes-cloudwatch-events.html상태 변경 메시지 를 참고하면 된다. 

 

 

 

이벤트 메시지를 참고해가며 필자가 작성한 nodejs 소스 코드는 다음과 같다.

var services = process.env.SERVICES;  // Slack webhook url의 /services/ 다음 문자열
var channel = process.env.CHANNEL;  // 알람을 전송할 Slack channel

var https = require('https');
var util = require('util');

// 타임존을 UTC -> KST로 변경
function toYyyymmddhhmmss(date) {

    if(!date){
        return '';
    }

    function utcToKst(utcDate) {
        return new Date(utcDate.getTime() + 32400000);
    }

    function pad2(n) { return n < 10 ? '0' + n : n }

    var kstDate = utcToKst(date);
    return kstDate.getFullYear().toString()
        + '-'+ pad2(kstDate.getMonth() + 1)
        + '-'+ pad2(kstDate.getDate())
        + ' '+ pad2(kstDate.getHours())
        + ':'+ pad2(kstDate.getMinutes())
        + ':'+ pad2(kstDate.getSeconds());
}

// Slack 메시지 필드 정의
var formatFields = function(event) {
    var fields  = [];

    // Make sure we have a valid response
    if (event) {
        fields = [
            {
                "title" : "type",
                "value" : event['detail-type'],
                "short" : true
            },
            {
                "title" : "time",
                "value" : toYyyymmddhhmmss(new Date(event.time)),
                "short" : true
            },
            {
                "title" : "region",
                "value" : event.region,
                "short" : true
            },
            {
                "title" : "link",
                "value" : "https://"+event.region+".console.aws.amazon.com/codesuite/codepipeline/pipelines/"+event.detail.pipeline+"/executions/"+event.detail['execution-id']+"/timeline?region="+event.region,
                "short" : true
            },
            {
                "title" : "pipeline",
                "value" : event.detail.pipeline,
                "short" : true
            },
            {
                "title" : "execution_id",
                "value" : event.detail['execution-id'],
                "short" : true
            },
            {
                "title" : "state",
                "value" : event.detail.state,
                "short" : true
            }
        ];

    }

    return fields;
};

// Slack 으로 알람 보내는 부분
exports.handler = function(event, context) {

    var postData = {
        "channel": channel, // Slack 알람 받는 채널
        "text": "*" + event.detail.pipeline + " Notify" + "*"   // Slack 알람 제목
    };

    var fields = formatFields(event);

    // Slack color와 메시지 필드
    postData.attachments = [
        {
            "color": event.detail.state == "SUCCESS" ? "good" : (event.detail.state == "STARTED" ? "good" : "danger"),
            "fields": fields
        }
    ];

    var options = {
        method: 'POST',
        hostname: 'hooks.slack.com',
        port: 443,
        path: services  // Slack webhook url의 /services/ 다음 문자열. 위에서 정의됨
    };

    var req = https.request(options, function(res) {
        res.setEncoding('utf8');
        res.on('data', function (chunk) {
            context.done(null);
        });
    });

    req.on('error', function(e) {
        console.log('problem with request: ' + e.message);
    });

    req.write(util.format("%j", postData));
    req.end();
};

코드에 대한 간단한 설명은 주석을 달아놨다. 추가로 detail-type과 execution-id같은 경우는 CloudWatch에서 넘어오는 event JSON 값이 'detail-type': "". 'execution-id': "" 와 같은 식으로 넘어오기 때문에, event.detail-type, event.detail.execution-id로는 데이터를 받을 수 없고, event['detail-type'], event.detail['execution-id']와 같은 방식으로 데이터를 받아와야 한다.

 

 

 

위 코드에서 나오는 proccess.env는 Lambda에서 정의하는 환경변수이다. 이 코드에서는 process.env.SERVICES와 process.env.CHANNEL을 사용했으니 그에 맞는 환경변수를 설정해주자. CHANNEL은 Slack 알람을 받을 채널명을 적어주면 되고, SERVICES는 조금 전 Slack Webhook을 만들었을 때 생성된 Webhook URL에서 https://hooks.slack.com 뒷부분을 적어주면 된다.

 

 

 

 

 

 


5. CloudWatch Event 생성

Lambda 함수도 만들었으니 이제 해당 Lambda 함수를 실행시킬 트리거(Trigger)를 만들어보자. 이 글에서는 트리거로 Amazon CloudWatch Events를 사용할 것이다. 

[AWS Management Console] -> [CloudWatch] -> [이벤트] -> [규칙] -> [규칙 생성]을 선택한다. 

 

 

 

[1단계: 규칙 생성]에서는 이벤트의 규칙을 생성한다. 그래서 해당 규칙과 일치하는 이벤트가 발생하면 특정 대상을 트리거하게 된다. 

[이벤트 패턴]을 선택해서 특정 이벤트가 발생한 경우로 규칙을 정한다. 만약 [일정]을 선택하게 되면 특정 일정마다 트리거가 발생하게 된다.

[서비스 이름]에서는 어떤 이벤트에 대한 규칙인지를 선택한다. 이 글에서는 CodePipeline에서 발생한 이벤트를 감지할 것이기 때문에 CodePipeline을 선택했다. 

[이벤트 유형]에서는 CodePipeline에서 발생 가능한 이벤트 유형을 선택한다. 이 글에서는 CodePipeline 실행 상태가 변경될 때마다 트리거를 일으키는 것으로 선택했다.

필요한 내용을 선택하면 [이벤트 패턴 미리 보기]에 코드가 자동으로 작성된다.

만약 특정 CodePipeline의 상태가 변경되었을 때만 트리거를 일으키고 싶다면 [편집]을 클릭한 후 코드를 추가하면 된다.

 

 

 

특정 CodePipeline의 이벤트만 감지하고 트리거를 일으켜야 하는 경우 다음과 같이 코드를 수정하면 된다.

 

 

 

다음으로 이벤트 감지 후 트리거 시킬 대상을 선택하자. 화면 우측에 있는 [대상 추가] 버튼을 클릭하자.

 

 

 

우리가 트리거 시킬 대상인 Lambda 함수를 선택하자. 그 밑에 [버전/별칭 구성]과 [입력구성]은 기본값으로 선택한다. 

만약에 입력구성의 [입력 변환기]를 선택하게 될 경우 Lambda함수에서 발생하는 코드가 아닌 이 Page에서 작성한 코드가 Slack 알람으로 나가게 된다. 모두 선택했으면 [세부 정보 구성]을 클릭해서 다음 페이지로 넘어가자.

 

 

 

[규칙 정의]에서는 [이름]만 작성하고 [규칙 생성]을 클릭하자. 그러면 CloudeWatch Events가 생성된다. 

 

 

 

위와 같이 설정을 한 후 [AWS Management Console] -> [Lambda]로 가서 조금 전 생성했던 Lambda 함수를 선택해보면 트리거로 CloudeWatch Events가 Lambda함수에 연결된 것을 확인할 수 있다.

 

 

 

 


6. 테스트

설정이 완료되었으니 테스트를 진행해보자. 

테스트는 Lambda 함수에서 진행을 할 수도 있다. 

우선 Lambda 함수 상단의 [테스트 이벤트 구성]을 선택한다.

 

 

 

그리고 다음과 같이 테스트 이벤트를 작성한다.

{
  "version": "0",
  "id": "event_Id",
  "detail-type": "CodePipeline Pipeline Execution State Change",
  "source": "aws.codepipeline",
  "account": "Pipeline_Account",
  "time": "TimeStamp",
  "region": "us-east-1",
  "resources": [
    "arn:aws:codepipeline:us-east-1:account_ID:myPipeline"
  ],
  "detail": {
    "pipeline": "myPipeline",
    "version": "1",
    "state": "SUCCESS",
    "execution-id": "execution_Id"
  }
}

 

 

 

 

그리고 생성한 테스트 이벤트를 선택한 후 [테스트]를 클릭하면 Lambda함수 테스트가 진행된다.

 

 

 

Slack 채널로도 알람이 정상적으로 오는 것을 확인할 수 있다. 성공!!!

 

 

 

위와 같이 Lambda 테스트 이벤트로도 테스트가 가능하며, CodePipeline을 실행시켜도 테스트는 진행된다. 

테스트가 진행된 후 로그를 확인하고 싶으면 [AWS Management Console] -> [CloudWatch] -> [로그 그룹]에서 Lambda 함수의 로그도 확인할 수 있다.

 

 

 

 


참고

https://aws.amazon.com/ko/

 

클라우드 서비스 | 클라우드 컴퓨팅 솔루션| Amazon Web Services

미디어 및 엔터테인먼트 Live Nation Entertainment, AWS를 사용하여 혁신 속도를 높이고 비용을 절감하며 애플리케이션 가용성을 개선 세계 최고의 라이브 엔터테인먼트 기업이 어떻게 AWS 클라우드를 사용하여 고객에게 개선된 경험을 더욱 빠르게 제공하는지 알아보십시오. 자세히 알아보기  보다 나은 고객 경험 추구 Live Nation은 콘서트를 제작하고, 티켓을 판매하고, 밴드를 음악과 연결해주는 세계 최고의 라이브 엔터테인먼트 기업입니다

aws.amazon.com

https://jojoldu.tistory.com/298

 

AWS Lambda로 Code Deploy 배포 상세 결과 알람 받기

Code Deploy와 다른 CI 툴 (ex: TravisCI)를 조합해서 사용할때 가장 불편한 점은 알람입니다. 저의 경우 TravisCI에서 빌드 & 테스트를 한 뒤에, Code Deploy를 통해 EC2로 배포 하는 구조를 자주 사용합니다. 구..

jojoldu.tistory.com

https://medium.com/@krishnakuntala/aws-codepipeline-slack-integration-41dfaff2414e

 

AWS CodePipeline & Slack integration

Slack is an integral part of every Delivery & DevOps team. Technical team do not want to waste their time looking at different UIs or…

medium.com