(AWS)React 앱 빌드,UI 테스트,배포,완료 처리 자동화 파이프라인 구축하기(1)

Devops의 꽃: 자동화

“The most powerful tool we have as developers is automation.”

Scott Hanselman

AWS는 Devops를 “애플리케이션과 서비스를 빠른 속도로 제공할 수 있도록 조직의 역량을 향상시키는 문화 철학, 방식 및 도구의 조합” 으로 정의합니다. 그리고 이것을 이루기 위한 Devops의 가장 강력한 무기는 바로 자동화입니다.

속도가 느리고 수동으로 실행되는 반복적인 프로세스를 자동화하면 많은 시간을 줄일 수 있고 이는 개발자가 좀더 로직을 구현하는데 집중할 수 있도록 도와주며 이는 제공하는 서비스를 빠르게 제공할 수 있게 도와줄 뿐 만 아니라 서비스의 질적 향상에 엄청난 도움이 되기 때문이죠.

이번 포스팅시리즈 에서는 AWS의 Code시리즈(CodePipeline, CodeBuild, CodeCommit)과 연관된 여러 서비스를 통해 개발된 React앱의 빌드,테스트,마지막 승인,배포까지의 모든 과정을 자동화하고 각 스테이지의 진행상황을 SNS(슬랙,이메일 연동가능)으로 받아볼 수 있는 솔루션을 설명하며 CloudFormation 템플릿도 공유하려 합니다.

아키텍쳐 다이어그램입니다. 클릭하면 크게 볼 수 있습니다.


배경

저희 팀에서 진행중인 프로젝트 중 프론트엔드를 React기반의 Web어플리케이션으로 개발한 프로젝트가 있습니다. AWS의 S3는 정적 호스팅 기능이 있기 때문에 React로 개발된 이 앱을 호스팅 할 수 있어 S3에 호스팅하여 배포하도록 하였고 이 배포를 자동화 하기 위해 다음 순서로 동작하는 파이프라인을 구축하였습니다.(위의 아키텍쳐 다이어그램 순서대로 입니다.)

  1. 개발자가 CodeCommit(AWS의 Git)에 PR이 올리고 관리자가 PR을 머지합니다.
  2. CodeCommit에 코드 변경을 감지해 CodePipeline이 동작을 시작합니다.
  3. 빌드를 위한 Codebuild를 실행합니다.
    3-1. CodeBuild가 AWS-CLI를 사용해서 기존 서비스중인 빌드를 백업합니다.
    3-2. CodeBuild가 소스코드로부터 새로운 아티팩트를 빌드합니다.
    3-3. Codbuild가 최종 QA테스트를 위해서 별도의 S3로 배포합니다.
  4. 람다함수가 실행되어 자동 UI 테스트를 실행합니다.
  5. 자동 테스트를 통과하면 QA팀에서 최종 QA테스트 빌드를 수동 테스트하고 테스트를 통과하면 팀장에게 보고합니다.
  6. 수동 테스트가 종료될때까지 파이프라인은 승인을 기다립니다.(승인 대기중임을 슬랙으로 알려줍니다.)
  7. QA팀에 보고를 받은 팀장이 마지막 체크후 배포를 승인합니다. 파이프라인이 다시 시작됩니다.
  8. CodeBuild가 프로덕션 S3버킷으로 배포합니다.
  9. 빌드가 완료되면 빌드 정보와 함께 팀 슬랙으로 완료 알림을 전달합니다. 또한 Confluence에 빌드 배포 로그를 기록합니다.
  10. 이 과정에서 하나라도 Fail이 되면 SNS로 전달되고 슬랙을 통해 팀 채널에 알려줍니다.
  • 이 프로젝트의 배포는 Blue/Green Deploy를 사용합니다. 따라서 새로 배포한 버전이 불안정할 경우 이전 버전으로 빠르게 롤백하기 위해 Backup 버킷에 현재 서비스중인 빌드를 백업합니다.

위의 작업을 위해서 다음의 AWS 서비스들을 이용합니다.

  • CodeCommit: AWS의 Git Repository
  • CodeBuild: AWS의 CI/CD 서비스
  • S3: React 앱의 호스팅, 백업용도
  • SNS: 이메일, 슬랙 등으로 알림을 전송하기 위해서 사용
  • Lambda: SNS의 메세지 처리, S3 버킷 정리, UI 자동 테스트
  • CodePipeline: 위의 모든 서비스의 오케스트레이션
  • CloudWatch: CodeCommit 변경시 파이프라인 트리거, CodePipeline의 이벤트 캐치 밑 SNS로 전달

위 목록의 서비스들을 차례로 구축하고 마지막에 CodePipeLine을 통해 구축한 모든 서비스를 하나로 모아 솔루션을 완성하겠습니다.

마치 여러 부품을 모아 하나로 조립하는 느낌입니다.

1화-CodeCommit, CodeBuild
2화-S3,SNS
3화-UITest Lambda
4화-Codepipeline, CloudWatch Rule, CF 템플릿 공유

보통은 각 AWS 서비스를 콘솔로 생성하는 법을 알려드리지만, 이번 솔루션의 경우 서비스간 연계가 복잡하고 프로젝트 별로 여러번 재활용 할 가능성이 높아 CloudFormation 템플릿 을 만드는 과정으로 설명하도록 하겠습니다.

CodeCommit 생성하기

CodeCommit은 AWS에서 제공하는 Git 서비스입니다. 즉 AWS 버전의 GitHub입니다. AWS CodeCommit은 가격도 저렴하고(사람당 $1) AWS의 CloudTrail과 같은 여러 서비스와도 연동되어있어 편리하기 때문에 프로젝트의 매인 레포지토리로 사용하고 있습니다. 자세한 사용법은 링크 를 참조해주세요.

CodeCommit의 경우 CloudFormation 템플릿 안에 포함시킬 수도 있지만 하나의 레포에 여러 브렌치를 사용해 독립적인 파이프라인을 구축하는 아키텍쳐를 위해서는 따로 생성하는 것이 더 편하기 때문에 콘솔로 생성하려 합니다.

  • 우선 CodeCommit 서비스로 진입하여 우측의 레포지토리 생성을 누릅니다.

우측의 레포지토리 생성을 누릅니다.

  • 이후 적당한 레포지토리 이름을 입력합니다. (예제로 CodePipeline-test)

프로젝트의 이름을 입력합니다.

  • 우선

CodeCommit 접근 권한 생성

CodeCommit는 HTTPS와 SSH 두 가지 프로토콜로 접근 가능합니다. 우선은 HTTP로 접근하는 권한을 생성하겠습니다.

  • IAM으로 이동해 권한을 부여할 유저 정보로 이동합니다.

유저 정보로 이동합니다

  • 탭 중에 “보안 자격 증명” 탭을 눌러 밑으로 스크롤 하여 “AWS CodeCommit에 대한 HTTPS Git 자격 증명” 이 보일때 까지 이동합니다. 이후 자격증명 생성을 누릅니다.

자격 증명 생성을 누릅니다.

  • 증명이 생성되면 정보를 다운로드 받을 수 있습니다. 비밀번호는 다운로드 받지 않으면 다시 볼 수 없으니 주의하시기 바랍니다.

IAM key와 마찬가지로 다시보기는 안되고 잃어버리면 삭제후 다시 생서앻야 합니다.


샘플 코드 CodeCommit으로 업로드 하기

예제의 진행을 돕기 위해 React 기반의 샘플 앱을 만들었습니다. 링크

우선 적당한 디렉토리에 샘플 Git프로젝트를 다운로드 합니다.

git clone --mirror https://github.com/awslabs/aws-demo-php-simple-app.git codepipeline-sample

그리고 방금 생성한 코드커밋에 푸시합니다.

git push https://git-codecommit.us-east-2.amazonaws.com/v1/repos/{코드커밋 이름} --all

이때 아까 만들어둔 자격증명 정보(userid/password)를 사용하면 됩니다.


CodeBuild 수행 내용 만들기

CodeBuild는 AWS에서 제공하는 CI/CD를 위한 서비스입니다. CodeBuild를 통해 정해진 환경에서 주어진 순서로 빌드/테스트 과정을 수행할 수 있고 여기서 생성된 빌드버전을 직접 배포할 수도 있습니다. 사실 개념 자체는 가상화된 인스턴스를 잠깐 빌려 무엇인가를 수행하는 개념이라 실제로는 빌드/테스트가 아니라도 원하는건 대부분 수행할 수 있지요.

주어진 과정을 수행한 결과물을 아티펙트라고 하며 이 아티펙트를 파이프라인의 다음 스테이지로 전달해 필요한 내용을 수행합니다. 이 아티펙트는 빌드 결과물일수도 있고 테스트라면 테스트 결과일수도 있으며 빌드와 기타 설정파일 및 메타데이터를 포함한 파일일 수도 있습니다.

이번 포스팅에서 CodeBuild는 React의 빌드와 배포 두 가지 목적으로 사용합니다. CodeDeploy를 사용하여 S3로 배포할 수도 있으나, CodeDeploy의 경우 S3로 배포시 이전 파일을 삭제하는 기능, 즉 –delete에 해당하는 기능이 없어 배포가 반복되면 이전 파일이 쌓이는 문제가 있습니다. 파이프라인을 구축하는 목적이 최대한 많은 빌드를 배포하는 것이기 때문에 CodeBuild를 사용하여 깔끔하게 배포하도록 하겠습니다.

CodeBuild가 수행하는 내용에 대해서는 프로젝트에 포함된 buildSpec.yml(기본이름:변경가능) 파일에 정의되어 있습니다. 이 프로젝트에서는 빌드, 배포 두 가지를 각각의 CodeBuild에서 수행하기 때문에 빌드를 위한 buildSpec.yml과 배포를 위한 buildSpec_deploy.yml 두 파일을 프로젝트에서 사용합니다.

우선 빌드를 위한 buildSpec.yml 입니다.

  1. 기존에 배포되어 있는 버킷의 빌드를 백업 빌드로 복사
  2. 이 리액트 앱이 프로덕션 스테이지라는 세팅을 앱에서 사용하기 위해 .env파일을 생성
  3. 프로덕션 스테이지이기 때문에 소스맵을 감추기 위해 GENERATE_SOURCEMAP=false 세팅(링크 참조)
  4. 앱을 빌드
  5. 이후 s3로 배포하기 위한 buildSpec_deploy.yml파일을 아티펙트에 포함
  6. 아티펙트 생성

하는 내용을 포함하고 있습니다.

재활용성을 높히기 위해 버킷명 등 변수는 모두 CodeBuild에 환경변수를 사용하였습니다.
buildSpec.yml
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36

version: 0.2
phases:
install:
runtime-versions:
# 런타임은 node12로 명시하면 자동으로 환경을 세팅해줍니다.
nodejs: 12
commands:
# yarn을 사용
- "npm install yarn -g"
pre_build:
commands:
# 기존 배포 빌드 백업
- "echo Copying Old File..."
- aws s3 sync s3://"$bucket_name"/ s3://"$backup_bucket_name"/ --delete --source-region ap-northeast-2 --region ap-northeast-2
# 환경파일 .env 파일 생성
- echo -e "\nSTAGE="$build_stage"" >> .env
- echo -e "\nGENERATE_SOURCEMAP=false" >> .env
- cat .env
- yarn
build:
commands:
- yarn build
post_build:
commands:
# s3에 배포하기 위한 수행내용을 위한 buildspec_deploy.yml을 아티팩트에 포함
- cp buildspec_deploy.yml build/
- cd build
- ls
#아티펙트 디렉토리 지정. react 기본으로 빌드되면 build 디렉토리로 빌드됨
artifacts:
files:
- "**/*"
base-directory: build


다음은 배포를 위한 buildSpec_deploy.yml입니다. 아주 간단하게 지정한 버킷으로 파일을 복사하며 –delete flag로 이름이 겹치지 않는 기존 파일을 삭제합니다.

buildSpec_deploy.yml
1
2
3
4
5
6
7
8
9
10
11
12
13
14

version: 0.2
phases:
install:
runtime-versions:
nodejs: 12
build:
commands:
- aws s3 sync ./ s3://"$bucket_name"/ --delete --region ap-northeast-2
artifacts:
files:
- "**/*"
base-directory: ./


CodeBuild 환경설정

CodeBuild의 빌드 환경 및 위에서 사용한 환경 변수를 설정하는 과정입니다. CloudFormation 템플릿에서는 우선 CodeBuiid가 사용할 IAM역할(role)과 빌드와 배포를 위한 두 CodeBuild를 생성하며 이 때 buildSpec에서 활용할 환경변수 (버킷명, 스테이지 등)을 같이 설정합니다.

CodeBuild에 환경은 다음으로 구성되어 있습니다.

  • OS: Amazon Linux2 혹은 Ubuntu나 커스텀 OS Image 사용 가능. 프로젝트에서는 AmazonLinux를 사용합니다.
  • ComputeType: 빌드 머신의 크기입니다. BUILD_GENERAL1_SMALL, BUILD_GENERAL1_MEDIUM, BUILD_GENERAL1_LARGE등이 있으며 빌드로는 BUILD_GENERAL1_LARGE등이, 배포용은 BUILD_GENERAL1_SMALL을 사용합니다.
  • 환경변수: 빌드 환경에서 사용할 환경변수입니다. 프로젝트에서는 버킷명과 빌드 스테이지를 사용합니다.
  • 타임아웃: 일정기간 이상 빌드 작업이 지속된다면 Fail 처리를 하기 위한 시간입니다. 프로젝트에서는 10분으로 잡았습니다.
  • BuildSpec 파일 이름: CodeBuild가 사용할 BuildSpec 이름입니다. 위에서 설명한대로 빌드용은 buildSpec.yml, 배포용은 buildSpec_deploy.yml을 사용합니다.

실제 콘솔에서는 더 많은 환경세팅이 가능합니다.

우선 CodeBuild가 사용할 IAM 역할입니다. 간단하게 로그 생성권한과 S3에 대한 권한만 부여했습니다.(편의상 fullAccess이기 때문에 실제로는 필요한 버킷에 대해서만 권한을 부여하시면 됩니다.)

codeBuild 역할
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
CodeBuildServiceRole:
Type: AWS::IAM::Role
Properties:
Tags:
- Key: "Stage"
Value: !Ref BuildStage
AssumeRolePolicyDocument:
Version: 2012-10-17
Statement:
- Effect: Allow
Action:
- sts:AssumeRole
Principal:
Service:
- codebuild.amazonaws.com
Policies:
- PolicyName: codebuild
PolicyDocument:
Version: 2012-10-17
Statement:
- Effect: Allow
Action:
- logs:CreateLogGroup
- logs:CreateLogStream
- logs:PutLogEvents
Resource: "arn:aws:logs:*:*:*"
- Effect: Allow
Action:
- s3:*

다음은 CodeBuild를 생성하기 위한 CloudFormation 템플릿 블럭입니다. 빌드를 위한 CodeBuild와 배포를 위한 CodeBuild 두 개를 생성합니다.위에서 만든 IAM 역할을 부여하고 템플릿에서 파라메터로 받은 배포 버킷명과 백업 버킷명을 환경변수로 넣어주어 buildSpec에서 참조할 수 있도록 해주려 합니다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
CodeBuildProject:
Type: AWS::CodeBuild::Project
Properties:
Tags:
- Key: "Stage"
Value: !Ref BuildStage
Name: !Join ["-", [!Ref ProjectName, !Ref BranchName]]
ServiceRole: !GetAtt CodeBuildServiceRole.Arn
Artifacts:
Type: CODEPIPELINE
Environment:
Type: linuxContainer
# ComputeType options: BUILD_GENERAL1_SMALL, BUILD_GENERAL1_MEDIUM, BUILD_GENERAL1_LARGE
ComputeType: BUILD_GENERAL1_LARGE
# Run `aws codebuild list-curated-environment-images` for a complete list of images provided.
Image: aws/codebuild/amazonlinux2-x86_64-standard:3.0
EnvironmentVariables:
- Name: build_stage
Value: !Ref BuildStage
- Name: bucket_name
Value: !Ref DeploymentBucket
- Name: backup_bucket_name
Value: !Ref BackupBucket
Source:
Type: CODEPIPELINE
BuildSpec: buildSpec.yml
TimeoutInMinutes: 10

CodeBuildToDeploy:
Type: AWS::CodeBuild::Project
Properties:
Tags:
- Key: "Stage"
Value: !Ref BuildStage
Name: !Join ["-", [!Ref ProjectName, !Ref BranchName,Deploy]]
ServiceRole: !GetAtt CodeBuildServiceRole.Arn
Artifacts:
Type: CODEPIPELINE
Environment:
Type: linuxContainer
# ComputeType options: BUILD_GENERAL1_SMALL, BUILD_GENERAL1_MEDIUM, BUILD_GENERAL1_LARGE
ComputeType: BUILD_GENERAL1_SMALL
# Run `aws codebuild list-curated-environment-images` for a complete list of images provided.
Image: aws/codebuild/amazonlinux2-x86_64-standard:3.0
Source:
Type: CODEPIPELINE
BuildSpec: buildSpec_deploy.yml
TimeoutInMinutes: 10

마치며

이번 포스팅에서는 파이프라인의 구성요소중 레포지토리인 CodeCommit과 빌드/배포를 위한 CodeBuild를 살펴보았습니다. 다음 파이프라인에 필요한 S3버킷과 React앱을 호스팅하기 위한 S3버킷, SNS, 람다 함수등을 생성해 보겠습니다.

WRITTEN BY
Dev Lead | Certified Professional AWS Solutions Architect/Devops Engineer

댓글