Amazon Web Services 한국 블로그

AWS Step Functions와 CodeBuild를 기반한 연속적 통합 빌드 워크플로 기능

소프트웨어 빌드를 자동화하는 작업은 DevOps 모범 사례를 도입하기 위한 중요한 단계입니다. 이를 돕기 위해 소스 코드를 컴파일하고, 테스트를 실행하고, 바로 배포할 수 있는 패키지를 생성하는 완전 관리형 연속적 통합 서비스인 AWS CodeBuild를 개발했습니다.

그러나 고객의 빌드 프로세스는 수없이 많은 방식으로 사용자 지정이 가능할 뿐만 아니라, 저희는 그동안 개발자들이 소프트웨어 빌드에 필요한 여러 가지 활동을 조정하기 위해 사용자 지정 워크플로를 만드는 데 시간을 쏟는 것도 지켜보았습니다. 예를 들어 개발자가 어떤 테스트를 실행하거나 실행하지 않을 수도 있고 신속히 수정 사항을 배포해야 할 때 코드의 정적 분석을 건너뛰어야 할 수도 있습니다. 장치 테스트 결과에 따라 다른 조치를 취하거나 SNS로 알림을 받고자 할 수도 있습니다.

이런 과정을 단순화하기 위해 오늘 CodeBuild가 포함된 AWS Step Functions 서비스 통합을 출시했습니다. 이제 상태 머신을 실행하는 동안 빌드를 시작하거나 정지하고, 빌드 보고서 요약을 받아보고, 과거의 빌드 실행 기록을 삭제할 수 있습니다.

이렇게 하면 직접 워크플로 중심적 빌드 프로세스를 정의하고 이를 수동 또는 자동으로 트리거할 수 있습니다. 예를 들어 다음과 같은 작업을 수행할 수 있습니다.

이 통합에서는 소프트웨어 빌드를 자동화하는 모든 Step Functions의 기능을 사용할 수 있습니다. 예를 들어 Parallel 상태를 사용하여 빌드의 독립적 구성 요소에 대해 병렬 빌드를 생성할 수 있습니다. 코드 리포지토리의 모든 분기 목록에서 Map 상태를 사용하여 각 분기에 일련의 단계(빌드, 장치 테스트, 통합 테스트 자동화)를 실행할 수 있습니다. 또한, 동일한 워크플로에서 다른 Step Functions 서비스 통합을 활용할 수도 있습니다. 예를 들어 SQS 대기열로 메시지를 보내서 활동을 추적하거나 Amazon ECSAWS Fargate를 사용하여 조금 전에 빌드한 컨테이너화된 애플리케이션을 시작할 수도 있습니다.

워크플로 중심적 빌드 프로세스에서 Step Functions 사용하기
저는 Java 웹 애플리케이션을 개발하고 있습니다. 새로운 기능을 추가할 때 애플리케이션이 작동하는지 확인하기 위해 JUnit Jupiter를 사용한 몇 가지 테스트를 작성했습니다. 이 테스트는 항상 실행되는 것이 아니라 빌드 프로세스가 실행된 직후에만 실행되어야 합니다. 테스트가 즉각적인 반복(iteration)의 속도를 늦출 수 있기 때문입니다. 테스트를 실행할 때는 CodeBuild를 사용해서 테스트 보고서를 저장하고 확인하려고 합니다. 마지막으로는 테스트가 실행되었는지, 테스트가 성공했는지 SNS 주제로 알림을 받으려고 합니다.

CodeCommit에 리포지토리를 생성하고 CodeBuild에 두 개의 buildspec 파일을 포함했습니다.

  • buildspec.yml은 기본값이며 Apache Maven을 사용하여 빌드와 테스트를 실행한 다음, 테스트 결과를 보고서로 저장합니다.
version: 0.2
phases:
  build:
    commands:
      - mvn package
artifacts:
  files:
    - target/binary-converter-1.0-SNAPSHOT.jar
reports:
  SurefireReports:
    files:
      - '**/*'
    base-directory: 'target/surefire-reports'
  • buildspec-notests.yml은 빌드만 실행하고 테스트를 실행하지 않습니다.
version: 0.2
phases:
  build:
    commands:
      - mvn package -DskipTests
artifacts:
  files:
    - target/binary-converter-1.0-SNAPSHOT.jar

빌드를 자동화하도록 CodeBuild 프로젝트와 Step Functions 상태 머신을 설정하기 위해 다음과 같은 템플릿으로 AWS CloudFormation을 사용하고 있습니다.

AWSTemplateFormatVersion: 2010-09-09
Description: AWS Step Functions sample project for getting notified on AWS CodeBuild test report results
Resources:
  CodeBuildStateMachine:
    Type: AWS::StepFunctions::StateMachine
    Properties:
      RoleArn: !GetAtt [ CodeBuildExecutionRole, Arn ]
      DefinitionString:
        !Sub
          - |-
            {
              "Comment": "An example of using CodeBuild to run (or not run) tests, get test results and send a notification.",
              "StartAt": "Run Tests?",
              "States": {
                "Run Tests?": {
                  "Type": "Choice",
                  "Choices": [
                    {
                      "Variable": "$.tests",
                      "BooleanEquals": false,
                      "Next": "Trigger CodeBuild Build Without Tests"
                    }
                  ],
                  "Default": "Trigger CodeBuild Build With Tests"
                },
                "Trigger CodeBuild Build With Tests": {
                  "Type": "Task",
                  "Resource": "arn:${AWS::Partition}:states:::codebuild:startBuild.sync",
                  "Parameters": {
                    "ProjectName": "${projectName}"
                  },
                  "Next": "Get Test Results"
                },
                "Trigger CodeBuild Build Without Tests": {
                  "Type": "Task",
                  "Resource": "arn:${AWS::Partition}:states:::codebuild:startBuild.sync",
                  "Parameters": {
                    "ProjectName": "${projectName}",
                    "BuildspecOverride": "buildspec-notests.yml"
                  },
                  "Next": "Notify No Tests"
                },
                "Get Test Results": {
                  "Type": "Task",
                  "Resource": "arn:${AWS::Partition}:states:::codebuild:batchGetReports",
                  "Parameters": {
                    "ReportArns.$": "$.Build.ReportArns"
                  },
                  "Next": "All Tests Passed?"
                },
                "All Tests Passed?": {
                  "Type": "Choice",
                  "Choices": [
                    {
                      "Variable": "$.Reports[0].Status",
                      "StringEquals": "SUCCEEDED",
                      "Next": "Notify Success"
                    }
                  ],
                  "Default": "Notify Failure"
                },
                "Notify Success": {
                  "Type": "Task",
                  "Resource": "arn:${AWS::Partition}:states:::sns:publish",
                  "Parameters": {
                    "Message": "CodeBuild build tests succeeded",
                    "TopicArn": "${snsTopicArn}"
                  },
                  "End": true
                },
                "Notify Failure": {
                  "Type": "Task",
                  "Resource": "arn:${AWS::Partition}:states:::sns:publish",
                  "Parameters": {
                    "Message": "CodeBuild build tests failed",
                    "TopicArn": "${snsTopicArn}"
                  },
                  "End": true
                },
                "Notify No Tests": {
                  "Type": "Task",
                  "Resource": "arn:${AWS::Partition}:states:::sns:publish",
                  "Parameters": {
                    "Message": "CodeBuild build without tests",
                    "TopicArn": "${snsTopicArn}"
                  },
                  "End": true
                }
              }
            }
          - {snsTopicArn: !Ref SNSTopic, projectName: !Ref CodeBuildProject}
  SNSTopic:
    Type: AWS::SNS::Topic
  CodeBuildProject:
    Type: AWS::CodeBuild::Project
    Properties:
      ServiceRole: !Ref CodeBuildServiceRole
      Artifacts:
        Type: NO_ARTIFACTS
      Environment:
        Type: LINUX_CONTAINER
        ComputeType: BUILD_GENERAL1_SMALL
        Image: aws/codebuild/standard:2.0
      Source:
        Type: CODECOMMIT
        Location: https://git-codecommit.us-east-1.amazonaws.com/v1/repos/binary-converter
  CodeBuildExecutionRole:
    Type: "AWS::IAM::Role"
    Properties:
      AssumeRolePolicyDocument:
        Version: "2012-10-17"
        Statement:
          - Effect: Allow
            Action: "sts:AssumeRole"
            Principal:
              Service: states.amazonaws.com
      Path: "/"
      Policies:
        - PolicyName: CodeBuildExecutionRolePolicy
          PolicyDocument:
            Version: "2012-10-17"
            Statement:
              - Effect: Allow
                Action:
                  - "sns:Publish"
                Resource:
                  - !Ref SNSTopic
              - Effect: Allow
                Action:
                  - "codebuild:StartBuild"
                  - "codebuild:StopBuild"
                  - "codebuild:BatchGetBuilds"
                  - "codebuild:BatchGetReports"
                Resource: "*"
              - Effect: Allow
                Action:
                  - "events:PutTargets"
                  - "events:PutRule"
                  - "events:DescribeRule"
                Resource:
                  - !Sub "arn:${AWS::Partition}:events:${AWS::Region}:${AWS::AccountId}:rule/StepFunctionsGetEventForCodeBuildStartBuildRule"
  CodeBuildServiceRole:
    Type: AWS::IAM::Role
    Properties:
      AssumeRolePolicyDocument:
        Version: "2012-10-17"
        Statement:
          - Effect: Allow
            Action: "sts:AssumeRole"
            Effect: Allow
            Principal:
              Service: codebuild.amazonaws.com
      Path: /
      Policies:
        - PolicyName: CodeBuildServiceRolePolicy
          PolicyDocument:
            Version: "2012-10-17"
            Statement:
              - Effect: Allow
                Action:
                - "logs:CreateLogGroup"
                - "logs:CreateLogStream"
                - "logs:PutLogEvents"
                - "codebuild:CreateReportGroup"
                - "codebuild:CreateReport"
                - "codebuild:UpdateReport"
                - "codebuild:BatchPutTestCases"
                - "codecommit:GitPull"
                Resource: "*"
Outputs:
  StateMachineArn:
    Value: !Ref CodeBuildStateMachine
  ExecutionInput:
    Description: Sample input to StartExecution.
    Value:
      >
        {}

CloudFormation 스택이 생성되었을 때 상태 머신 정의에는 두 개의 CodeBuild 작업이 있습니다.

  • 첫 번째 CodeBuild 작업은 동기식 통합(startBuild.sync)을 사용하여 빌드가 자동으로 종료되기를 기다렸다가 다음 단계를 처리합니다.
"Trigger CodeBuild Build With Tests": {
  "Type": "Task",
  "Resource": "arn:aws:states:::codebuild:startBuild.sync",
  "Parameters": {
    "ProjectName": "CodeBuildProject-HaVamwTeX8kM"
  },
  "Next": "Get Test Results"
}
  • 두 번째 CodeBuild 작업은 BuildspecOverride 파라미터를 사용하여 빌드에서 사용한 기본 buildspec 파일을 테스트를 실행하지 않는 빌드로 재정의합니다.
"Trigger CodeBuild Build Without Tests": {
  "Type": "Task",
  "Resource": "arn:aws:states:::codebuild:startBuild.sync",
  "Parameters": {
    "ProjectName": "CodeBuildProject-HaVamwTeX8kM",
    "BuildspecOverride": "buildspec-notests.yml"
  },
  "Next": "Notify No Tests"
},

첫 번째 단계는 상태 머신 실행 입력값을 살펴보고 테스트를 실행할지 실행하지 않을지 결정하는 Choice입니다. 예를 들어 테스트를 실행하려면 다음과 같이 입력합니다.

{
  "tests": true
}

이는 테스트를 실행하는 시각적 워크플로로, 모든 테스트가 통과되었습니다.

"tests"의 값을 false로 변경하고 다른 분기로 연결되는 새로운 실행을 시작합니다.

이번에는 buildspec이 테스트를 실행하지 않고, 테스트가 실행되지 않았다는 알림이 수신됩니다.

GitHub 또는 CodeCommit에서 활동이 실행된 후 이 워크플로가 자동으로 시작되면 마지막 커밋 메시지에서 특정 패턴을 찾아보고 그에 맞게 빌드 프로세스를 사용자 지정할 수 있습니다. 예를 들어 [skip tests] 문자열이 커밋 메시지에 포함되어 있으면 테스트를 건너뛸 수 있습니다. 마찬가지로 프로덕션 환경에서 [skip static analysis] 메시지가 커밋에 포함되어 있으면 코드 정적 분석을 건너뛰고 변경 사항을 즉시 적용하여 신속히 통합을 생성할 수 있습니다.

컨테이너화된 애플리케이션으로 워크플로 확장
애플리케이션을 여러 다른 환경으로 배포할 때는 Docker 이미지로 패키징하는 것이 좋습니다. 이렇게 하면 빌드 워크플로에 단계를 추가하고 QA(품질 검증) 팀을 위해 Amazon ECS 작업(AWS Fargate에서 실행)에서 컨테이너화된 애플리케이션을 시작할 수 있습니다.

먼저 ECR에서 이미지 리포지토리를 생성하고 CodeBuild 프로젝트에서 사용하는 서비스 역할에 ECR로 업로드하기 위한 권한을 추가합니다. 이와 관련된 설명은 여기에서 확인할 수 있습니다.

그런 다음, 코드 리포지토리에서 이 예시를 따라 다음을 추가합니다.

  • 소프트웨어 빌드로 Docker 컨테이너를 준비하고 애플리케이션을 시작하는 Dockerfile
  • Docker 이미지를 생성하고 업로드하는 명령이 포함된 buildspec-docker.yml 파일

최종 워크플로에서는 다음의 모든 작업을 자동화합니다.

  1. 소스 코드에서 소프트웨어 빌드
  2. Docker 이미지 생성
  3. Docker 이미지를 ECR로 업로드
  4. ECSFargate에서 QA 환경 시작
  5. QA 환경이 준비되었다는 SNS 알림 전송

워크플로와 각각의 단계는 요구 사항에 따라 손쉽게 사용자 지정할 수 있습니다. 예를 들어 몇 가지를 변경하는 것만으로 이미지를 Docker Hub에 푸시하도록 buildspec 파일을 수정할 수 있습니다.

지금 이용 가능
CodeBuild 서비스 통합은 Step Functions 및 CodeBuild 서비스가 제공되는 모든 상업 및 GovCloud 리전에 제공됩니다. 사용 가능한 리전은 AWS 리전 표를 참조하십시오. 자세한 내용은 설명서를 참조하십시오.

AWS 서버리스 히어로, Gojko AdzicAWS DevOps 블로그에서 언급했듯이, CodeBuild는 관리 작업을 실행하는 데도 사용할 수 있습니다. Step Functions를 포함한 통합 덕분에 온갖 새로운 가능성이 열렸습니다.

이 새로운 서비스 통합을 어디에 사용할지 알려주세요!

Danilo