[AWS] CodePipeline의 변화를 Slack으로 알림받기

예리에르·2024년 6월 14일
2

AWS

목록 보기
3/3
post-thumbnail
post-custom-banner

🤔 배포할 때 마다 AWS 페이지 접속 안 할 수 없을까?

현재 CI/CD는 AWS의 codePipline을 구축해서 자동으로 배포가 있습니다. 이전의 Filezila를 통해 수동하던 거에 비해서 훨씬 편하게 배포가 되고 있지만 하나의 불편한점이 있었습니다!!

그것은 바로,
배포 성공 여부를 aws 사이트에 직접 접속해 과정을 확인해야 한다는 것이었습니다.

메신저로 사용하는 slack을 활용해서 아래의 효과를 이루고 싶었습니다.

  1. 배포가 성공했다.
    • aws 사이트 접근없이 기존의 하던 작업 계속유지.
  2. 배포가 실패했다.
    • 어떤 파이프라인이 실패했는가.
    • 실패한 파이프라인에 바로 접속하자.
    • 배포는 중요한 작업인 만큼 히스토리를 모두 공유하자.

Amazon CloudWatch Events, AWS Lambda

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

1. Amazon CloudWatch Events

AWS 서비스와 사용자 지정 애플리케이션 간의 시스템 이벤트를 추적하고 응답할 수 있는 기능을 제공하는 서비스입니다. 이를 통해 다양한 AWS 리소스에서 발생하는 이벤트에 대한 실시간 모니터링과 대응이 가능해집니다. 다음은 AWS CloudWatch Events의 주요 기능과 개념입니다.

주요 기능

  1. 이벤트 생성 및 관리: 특정 조건이 충족될 때 트리거되는 이벤트 규칙을 생성할 수 있습니다.
  2. 대상 지정: 이벤트가 발생하면 자동으로 실행될 작업이나 서비스(예: Lambda 함수, SQS 큐, SNS 주제 등)를 지정할 수 있습니다.
  3. 이벤트 버스(Event Bus): 이벤트를 수신하고 전달하는 역할을 합니다. 기본 이벤트 버스와 사용자 지정 이벤트 버스를 사용할 수 있습니다.
  4. 스케줄링: 정기적으로 발생해야 하는 작업을 스케줄링할 수 있습니다(예: 매일 오전 9시에 Lambda 함수 실행).

사용 예시

  1. 자동 백업: EC2 인스턴스의 상태 변경 이벤트를 감지하여 자동으로 스냅샷을 생성하는 작업을 설정할 수 있습니다.
  2. 알림 시스템: S3 버킷에 파일이 업로드되면 SNS 주제를 통해 알림을 전송하는 시스템을 구축할 수 있습니다.
  3. 자동화된 응답: 특정 조건이 충족될 때 Lambda 함수를 실행하여 대응 작업을 자동화할 수 있습니다.

밑줄 친 기능과 예시를 활용하였습니다. codepipile의 이벤트를 트리거로 설정하고 특정 이름에 해당하는 경우 알림을 보내는 AWS Lambda를 실행하게 할 것입니다.

2. AWS Lambda

AWS에서 제공하는 서버리스 컴퓨팅 서비스로, 서버를 관리하지 않고도 코드를 실행할 수 있게 해줍니다. 사용자는 특정 이벤트에 반응하여 자동으로 실행되는 함수를 작성할 수 있습니다. Lambda는 다음과 같은 특징을 가지고 있습니다.
Lambda는 사용한 만큼 비용이 지불되고 Node.js, Python, C#, Ruby, Go등 다양한 프로그래밍 언어를 지원합니다.

만들어보자!

1. Slack Incoming Webhooks 만들기

Slack Incoming Webhook 2가지 방법 블로그를 참고하여 만들었습니다.

충분히 자세한 설명으로 따라만가면 되지만 하나 막혔던 부분만 추가하자면

4) Incoming Webhooks 선택 > On > Add New Webhook to Workspace > 채널 선택 > 허용
의 채널을 선택하는 과정이었습니다.

저는 기존의 채널을 사용하는 것이 아니라 CI/CD 전용 채널을 따로 개설하고 싶었기때문에 채널의 추가로 생성해주었습니다.

알림 전용 채널 추가하기


#모양의 채널 버튼을 클릭합니다.

원하는 이름을 작성하면 됩니다. 참~~ 쉽죠? 😘

2. AWS Lambda 함수 생성

  1. [AWS Management Console] -> [Lambda] -> [함수] -> [함수생성] 으로 가서 함수를 생성합니다.

  2. 새로 작성 을 선택하고 함수 이름을 작성합니다. 런타임에는 자신이 사용할 언어를 선택합니다.
    (프런트엔드 개발자로 Node.js를 선택했습니다.)

  3. 간단하게 Lambda 함수를 생성할 수 있습니다.

  4. Slack Webhook을 호출하는 소스 코드를 작성해봅시다.
    cloudWatch에서 보내는 이벤트는 상태 변경 메시지를 참고해서 작성하시면 됩니다.
    파이프라인 실행이 시작되면 다음 내용이 포함된 알림을 보내는 이벤트를 내보냅니다. 이는 us-east-1 리전의 "myPipeline"이라는 파이프라인에 대한 예제입니다.

{
    "version": "0",
    "id": "01234567-EXAMPLE",
    "detail-type": "CodePipeline Pipeline Execution State Change",
    "source": "aws.codepipeline",
    "account": "123456789012",
    "time": "2020-01-24T22:03:07Z",
    "region": "us-east-1",
    "resources": [
        "arn:aws:codepipeline:us-east-1:123456789012:myPipeline"
    ],
    "detail": {
        "pipeline": "myPipeline",
        "execution-id": "12345678-1234-5678-abcd-12345678abcd",
        "start-time": "2023-10-26T13:49:39.208Z",
        "execution-trigger": {
            "trigger-type": "StartPipelineExecution",
            "trigger-detail": "arn:aws:sts::123456789012:assumed-role/Admin/my-user"
        },
        "state": "STARTED",
        "version": 1.0,
        "pipeline-execution-attempt": 1.0
    }
}

이벤트 메시지를 참고해서 작성한 코드 예시입니다.
블로그에서 가져온 예시입니다. 여기에 의도에 맞게 추가로 코드를 수정했습니다.

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();
};

process.env는 Lambd 탭중에 [구성] -> [환경변수] 에서 설정할 수 있습니다.

3. CloudWatch Event 생성하기

이제 Lambda 함수를 실행할 트리거를 만들어야 합니다.
[CloudWatch] -> [이벤트] -> [규칙] -> [규칙생성 버튼]을 통해 만들 수 있습니다.

1. 2단계 이벤트 패턴 작성 : 이벤트 패턴 작성을 통해 트리거가 발생살 이벤트를 작성해 줍니다. CodePipeline 의 실행 상태가 바뀔때 마다 트리거가 작동되어야 하기 때문에 source와 detail-type을 목적에 맞게 작성해줍니다.

추가로 저는 pipeline의 접두사로 "dmp-" 혹은 "mi-f-"가 있을 때만 트리거가 실행되기를 원했때문에 추가 조건을 설정해주었습니다.
관련 문법은 공식 홈페이지에서 확인할 수 있습니다.


2. 이번트 대상 선택에서 아까 저희가 만들었던 Lambda를 선택해줍니다.

  1. 규칙생성이 완료되면 Lambda에 CloudWatch Event나 연결된 것을 확인 할 수 있습니다.👍

4. 테스트 해보기

[테스트] 탭을 통해 원하는 이벤트를 작성하고 테스트를 해 볼 수 있습니다.

왜 이름에 'dmp-'와 'mi-f-' 가 없는데 메시지가 가나요?

Lambda의 테스트는 이벤트가 발생이 된 후 실행되기 때문에 여기서의 테스트는 이미 트리거가 발생된 후 입니다. 테스트의 용도로는 슬랙으로 메시지가 잘 간다면 잘 완성 된 것입니다! ㅎㅎ

후기

지금까지 CodePipeline의 상태가 변경될 때마다 Slack으로 메시지 보내기를 만들어보았습니다. 확실히 기존의 프로세스보다 메시지로 간편하게 상태를 확인할 수 있고 실패를 하더라고 링크를 통해 빠르게 문제점을 볼수 있게 되었습니다.
또한 팀원과 함께 상태를 공유하면서 문제가 생기면 모두가 빠르게 상태를 알 수 있어 협업에도 좋다고 느껴졌습니다!

저와 같은 고민을 하고 해결방법을 찾는 분들에게 부디 도움이 되었기를 바랍니다!!! 😎

참고

https://twofootdog.tistory.com/49
https://velog.io/@king/slack-incoming-webhook

profile
궁금한 프론트엔드 개발자의 개발일기😈 ✍️
post-custom-banner

0개의 댓글