CI/CD with AWS & Serverless (Bitbucket, CodeBuild, CodeDeploy, CodePipeline)

By Moka

AWS와 Serverless 를 활용하여 Bitbucket / Slack 환경에서 CI,CD 를 구축한다. 코드를 푸시 후 배포가 되고, 실패시 슬랙으로 노티가 되는 최소한의 과정이다.

image

제약사항 Bitbucket

CodePipeline 에서 코드를 받아와서 CodeBuild -> CodeDeploy 로 진행 할 수 있도록 할 수 있다. 하지만 CodePipeline 의 Source 공급설정을 보면 S3, Github, CodeCommit 은 가능한데, Bitbucket 에서 코드를 바로 가져올수가 없어, 위의 그림과 같이 CodePipeline 에서는 CodeDeploy 만을 활용하고, CodeBuild 는 개별로 Bitbucket 과 연결을 하였다.

Bitbucket pipeline

CodePipeline 에서 바로 Bitbucket 을 활용 할 수 없는 문제를 해결하기 위한 다른 방법으로 Bitbucket 에 pipeline 기능이 있다. 코드가 푸시되면 Bitbucket 이 특정 스크립트를 실행 할 수 있도록 기능을 지원한다. 해당 기능을 활용하여 코드를 S3로 업로드 하는 스크립트를 작성하여 S3에 올라온 코드를 캐치하여 CodePipeline 을 동작 하도록 할 수 있다. 하지만 본 글에서는 CodeBuild 에서 Bitbucket 의 코드를 가져와서 동작하도록 구축 하였다.

Setup S3

# Setup S3 Bucket to artifact of CodePipeline
ArtifactOfPipelineBucket:
  Type: AWS::S3::Bucket
  Properties:
    BucketName: artifact-of-pipeline

# Setup S3 Bucket to upload output of CodeBuild
SourceOfPipelineBucket:
  Type: AWS::S3::Bucket
  Properties:
    BucketName: ${self:custom.S3_BUCKET_NAME}
    VersioningConfiguration: 
      Status: Enabled

참고 : https://docs.aws.amazon.com/ko_kr/AWSCloudFormation/latest/UserGuide/aws-properties-s3-bucket.html

CodeBuild 를 통해 build 된 결과를 업로드할 S3 bucket 을 먼저 만든다. 해당 버킷은 CodePipeline 이 공급받는 코드이기 떄문에 필수로 VersioningConfiguration 을 설정해주어야 한다. 그리고 CodePipeline 이 사용할 S3 버킷을 만들어 준다.

Setup CodeBuild

# Setup Code Build 
CodeBuild:
  Type: AWS::CodeBuild::Project
  Properties:
    Name: test0521-CodeBuild
    Description: Bitbucket 에서 코드를 가져와서 Build 해준다
    ServiceRole: !Ref DeployRole
    Artifacts:
      Type: S3
      Packaging: zip
      Name: ${self:custom.CODE_ARTIFACTS_NAME}
      Location: ${self:custom.S3_BUCKET_NAME}
    Environment:
      Type: LINUX_CONTAINER
      ComputeType: BUILD_GENERAL1_SMALL
      Image: aws/codebuild/standard:2.0
    Source:
      Type: BITBUCKET
      Location: ${self:custom.REPOSITORY_URL}
      BuildSpec: deploy/buildspec.yml
    Triggers:
      Webhook: true
      FilterGroups:
        - - Type: EVENT
            Pattern: PUSH
    TimeoutInMinutes: 10

참고 : https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-codebuild-project.html

그리고 CodeBuild 세팅으로 Artifacts(빌드 결과물을 올릴 곳 설정) / Environment(빌드 환경 설정) / Source(빌드할 코드와, 빌드 spec 파일 설정) / Triggers(Bitbucket webhook 설정) 설정 한다. 빌드를 할 소스코드의 buildspec.yml 파일을 읽어서 build 를 진행하게 된다.

Setup CodeDeploy

# Setup Code Deploy Application
CodeDeployApplication:
  Type: AWS::CodeDeploy::Application
  Properties:
    ApplicationName: test0521-CodeDeploy
    ComputePlatform: Server

# Setup Code Deploy Application
CodeDeployGroup:
  Type: AWS::CodeDeploy::DeploymentGroup
  Properties: 
    ApplicationName: !Ref CodeDeployApplication
    Ec2TagFilters: 
      - 
        Key: ${self:custom.EC2_TAG_KEY}
        Value: ${self:custom.EC2_TAG_VALUE}
        Type: KEY_AND_VALUE
    ServiceRoleArn: 
      Fn::GetAtt: [ DeployRole, Arn ]

참고 : https://docs.aws.amazon.com/ko_kr/AWSCloudFormation/latest/UserGuide/cfn-reference-codedeploy.html

CodeDeploy 에서는 배포할 ec2 세팅이 핵심이다. CodePipeline 통해서 배포가 만들어지며 CodeBuild 의 빌드된 코드를 S3 에서 받아와서 ec2 에 접속하여 배포를 진행하게 된다. 이때는 appspec.yml 파일을 읽어서 원하는 스크립트를 실행 할 수 있다.

Setup CodePipeline

# Setup CodePipeline 
CodePipeline:
  Type: AWS::CodePipeline::Pipeline
  Properties:
    RoleArn:
      Fn::GetAtt: [ DeployRole, Arn ]
    ArtifactStore: 
      Type: S3
      Location: !Ref ArtifactOfPipelineBucket
    Stages: 
      - 
        Name: Source
        Actions: 
          - 
            Name: SourceCode from CodeBuild(build & test)
            ActionTypeId: 
              Category: Source
              Owner: AWS
              Version: 1
              Provider: S3
            OutputArtifacts: 
              - 
                Name: SourceOutput
            Configuration: 
              S3Bucket: ${self:custom.S3_BUCKET_NAME}
              S3ObjectKey: ${self:custom.CODE_ARTIFACTS_NAME}
              PollForSourceChanges: true
            RunOrder: 1
      - 
        Name: Deploy
        Actions: 
          - 
            Name: Deploy
            InputArtifacts: 
              - 
                Name: SourceOutput
            ActionTypeId: 
              Category: Deploy
              Owner: AWS
              Version: 1
              Provider: CodeDeploy
            Configuration: 
              ApplicationName: 
                Ref: CodeDeployApplication
              DeploymentGroupName: 
                Ref: CodeDeployGroup
            RunOrder: 2

참고 : https://docs.aws.amazon.com/ko_kr/AWSCloudFormation/latest/UserGuide/aws-resource-codepipeline-pipeline.html

CodePipeline 까지 설정해주면 코드 푸시시 배포까지 자동으로 된다. 현재는 Stage 를 두단계로 Source(진행할 코드를 가져오고) / Deploy(해당 코드를 배포) 구성 하였다. ArtifactStore 는 Source 에서 받은 코드를 저장해두는 S3 버킷이다. 여기서는 위에서 설정했던 CodeDeploy 를 설정해주는 것으로 끝이 난다.

Noti to Slack

functions:
  cloudwatchToSlack:
    handler: src/entries/cloudwatch-to-slack.handler
    timeout: 300
    events:
      - cloudwatchEvent:
          name: CodePipelineToLamdda
          description: AWS CodePipeline 의 실행결과 중 FAIL 의 이벤트를 받아서 Lambda 로 넘겨준다.
          enabled: true
          event:
            source:
              - aws.codepipeline
            detail-type:
              - CodePipeline Pipeline Execution State Change
            detail:
              state:
                - FAILED

위의 설정은 모두 CloudFormation 의 yml 포멧이다. 해당 설정을 Serverless Framework 의 Resources 에서 설정해주면 Serverless Framework 를 통해 세팅을 할 수 있다. 또한 functions 로 lambda 를 만들어 실패시 Slack 으로 노티 보내는 코드를 만든다. 이 때 events 에서 CloudWatch 로 걸어주어 pipeline 이 FAILED 일때 호출 되도록 설정 해준다.