AWS

Part 15. Code Series를 사용해서 CI/CD 자동화

cwchoiit 2024. 1. 21. 19:29
728x90
반응형
SMALL
728x90
SMALL

 

AWS에서 제공해주는 CodeBuild, CodeDeploy를 이용해서 CI/CD 자동화를 구현해보자.

 

CodePipeline

우선 AWS에서 제공하는 CodePipeline이라는 게 있다. CD를 위한 서비스인데 이 서비스 내 CodeBuild, CodeDeploy 서비스를 사용할 수 있다. 그래서 이 서비스를 이용할건데 이 서비스를 이용해서 CD 파이프라인을 만드려면 buildspec.yaml 파일이란게 필요하다. 그리고 그 yaml파일은 어떤 형식을 가지고 있는데 이 형식에 대한 설명이나 예시는 다음 링크에서 확인할 수 있다.

 

자습서: CodePipeline을 사용한 Amazon ECS 표준 배포 - AWS CodePipeline

CodePipeline의 Amazon ECS 표준 배포 작업은 Amazon ECS 서비스에서 사용하는 수정 버전을 기반으로 작업 정의의 자체 수정 버전을 생성합니다. Amazon ECS 서비스를 업데이트하지 않고 작업 정의에 대한 새

docs.aws.amazon.com

 

그래서 이 YAML파일을 수정해보자. 다음은 예시코드인데 여기서 우리 환경에 맞게 수정을 해줘야한다. 

 

Frontend buildspec.yaml 파일 만들기

우선 프론트 소스에 대한 buildspec.yaml 파일을 만들자. 아래를 그대로 쭉 따라해보자. 

version: 0.2

phases:
  pre_build:
    commands:
      - echo Logging in to Amazon ECR...
      - aws --version
      - aws ecr get-login-password --region ap-northeast-2 | docker login --username AWS --password-stdin 135149110460.dkr.ecr.ap-northeast-2.amazonaws.com
      - REPOSITORY_URI=135149110460.dkr.ecr.ap-northeast-2.amazonaws.com/frontend
      - COMMIT_HASH=$(echo $CODEBUILD_RESOLVED_SOURCE_VERSION | cut -c 1-7)
      - IMAGE_TAG=${COMMIT_HASH:=latest}
  build:
    commands:
      - echo Build started on `date`
      - echo Building the Docker image...
      - docker build -t $REPOSITORY_URI:latest .
      - docker tag $REPOSITORY_URI:latest $REPOSITORY_URI:$IMAGE_TAG
  post_build:
    commands:
      - echo Build completed on `date`
      - echo Pushing the Docker images...
      - docker push $REPOSITORY_URI:latest
      - docker push $REPOSITORY_URI:$IMAGE_TAG
      - echo Writing image definitions file...
      - printf '[{"name":"frontend","imageUri":"%s"}]' $REPOSITORY_URI:$IMAGE_TAG > imagedefinitions.json
artifacts:
  files: 
    - 'image*.json'
    - 'appspec.yaml'
    - 'taskdef.json'
  secondary-artifacts:
    DefinitionArtifact:
      files:
        - appspec.yaml
        - taskdef.json
    ImageArtifact:
      files:
        - imagedefinitions.json
위 buildspec.yaml 파일에서 이미지 태그가 제대로 동작하지 않는 경우가 있다. 그래서 CodeBuild로 Image를 빌드하면 Image는 만들어 지는데 그 이미지를 가져다가 클러스터의 서비스를 만들어서 실행하는게 안되는 경우가 있다. 그 때는 이미지 태그를 다르게 직접 명시 해줘야한다.

 

위 태그 문제에 대한 예시

version: 0.2

phases:
  pre_build:
    commands:
      - echo Logging in to Amazon ECR...
      - aws --version
      - aws ecr get-login-password --region ap-northeast-2 | docker login --username AWS --password-stdin 135149110460.dkr.ecr.ap-northeast-2.amazonaws.com
      - REPOSITORY_URI=135149110460.dkr.ecr.ap-northeast-2.amazonaws.com/frontend
  build:
    commands:
      - echo Build started on `date`
      - echo Building the Docker image...
      - docker build -t frontend .
      - docker tag frontend:latest $REPOSITORY_URI:v1
  post_build:
    commands:
      - echo Build completed on `date`
      - echo Pushing the Docker images...
      - docker push $REPOSITORY_URI:v1
      - echo Writing image definitions file...
      - printf '[{"name":"frontend","imageUri":"%s"}]' $REPOSITORY_URI:$IMAGE_TAG > imagedefinitions.json
artifacts:
  files:
    - 'image*.json'
    - 'appspec.yaml'
    - 'taskdef.json'
  secondary-artifacts:
    DefinitionArtifact:
      files:
        - appspec.yaml
        - taskdef.json
    ImageArtifact:
      files:
        - imagedefinitions.json

 

위 코드의 이 부분을 보자. 태그(v1)를 직접 명시했다. 

- docker tag frontend:latest $REPOSITORY_URI:v1

 

 

 

ECR 로그인 커맨드

배포 자동화를 위해선 도커를 사용하기 때문에 이미지를 계속 리빌딩 해줘야한다. 그러기 위해 ECR을 사용했고 ECR을 사용하려면 로그인 커맨드가 필요하다. ECR에 가서 로그인 커맨드를 가져오자. 

 

가져와서 위 그림의 8번째 라인(아래 코드)을 복사한 커맨드로 수정한다.

- aws ecr get-login-password --region ap-northeast-2 | docker login --username AWS --password-stdin 135149110460.dkr.ecr.ap-northeast-2.amazonaws.com

 

Repository URI 변경

그 다음 원하는 레포지토리의 URI를 가져와야한다. 이 또한 ECR에서 확인할 수 있다.

 

원하는 레포지토리 URI를 가져와서 위 코드의 9번째 라인(아래 코드)을 수정한다.

- REPOSITORY_URI=135149110460.dkr.ecr.ap-northeast-2.amazonaws.com/frontend

 

그리고 다음 부분에서 "name"의 value값을 본인의 레포지토리 이름으로 바꿔주자.

- printf '[{"name":"frontend","imageUri":"%s"}]' $REPOSITORY_URI:$IMAGE_TAG > imagedefinitions.json

 

이 상태로 두고 본인의 Github에 Push하자. 다 했으면 AWS Console로 가서 CodeBuild로 가서 새 프로젝트를 만들자.

 

CodeBuild 프로젝트 만들기

Project configuration 부분은 다음처럼 작성한다.

- Project name: frontend-build

- Description: frontend-build

 

Source 부분은 다음과 같다.

- Source provider: Github

- Repository: Connect with a Gtihub personal access token

 

그리고 Github personal access token을 발급받아 저 필드에 집어넣어야 한다. 집어 넣고 하단 'Save token' 버튼을 누르면 다음과 같이 화면이 변한다. 저기서 원하는 레포지토리를 선택한다. 

 

다음은 Environment다. 변경 사항은 다음과 같다.

- Environment Image: Managed Image

- Operation system: Amazon Linux

- Runtime(s): Standard

- Image: aws/codebuild/amazonlinux2-x86_64-standard:5.0

- Image version: Always use the latest image for this runtime version

- Service role: New service role

 

그 다음 하단 Additional configuration을 눌러 다음 화면과 같이 Privileged를 체크한다. 이는 도커 이미지를 빌드하기 위함이다.

 

 

그 외 나머지는 기본값으로 설정 후 생성한다. 그럼 다음과 같이 리스트에 잘 노출된다.

 

 

CodeBuild의 Role 수정하기 (ECR을 컨트롤 할 수 있게)

이 다음 이 CodeBuild를 만들면서 위에 Environment 섹션에서 서비스 역할(Service role)을 새로이 만드는 작업(New service role)을 했다. 이 역할을 IAM에 가서 확인해보자.

 

IAM > Access management > Roles 진입 후 'codebuild'라고만 쳐도 나올 것이다.

 

저렇게 잘 나오면 저 역할을 선택한다. 이 역할에게 ECR을 컨트롤 할 수 있는 권한을 추가해줘야 한다. 

 

그런 다음 나오는 화면에서 'ECR'이라고 검색 후 다음 정책을 선택하여 추가한다.

추가를 다 하면 이제 CodeBuild로부터 ECR을 컨트롤 할 수 있게 된다. 그럼 이제 CodeBuild에서 Build를 해보자.

 

CodeBuild로 빌드하기

CodeBuild > Build projects 화면에서 방금 만든 프로젝트를 빌드해보자. 우측 상단 'Start build > Start now'

 

'Start now'를 클릭하면 다음 화면처럼 진행 중인 모습을 확인할 수 있다.

 

다 끝나면 다음과 같이 상태가 'Succeeded'로 변경된다.

그럼 정말 잘 됐는지 ECR로 가서 이미지 빌드 상태를 보자. ECR > CodeBuild로 적용한 레포지토리(frontend)

 

확인하면 다음과 같이 CodeBuild로부터 빌드된 이미지가 잘 업로드됐다.

 

 

CodeDeploy 생성하기

CodeBuild로부터 이미지를 잘 빌드했으니 이제 CodeDeploy를 이용해서 클러스터에 배포하는것까지 해보자. 그 전에 이 CodeDeploy가 클러스터에 배포를 할 때 서비스 자원을 건드리기 때문에 관련 IAM 역할을 생성해야 한다.

 

CodeDeploy용 역할 생성하기

IAM > Roles 화면에서 우측 상단 'Create role'

 

Select trusted entity 화면이다.

- Trusted entity type: AWS service

- Use case: CodeDeploy

 

Use case를 CodeDeploy로 선택하면 다음 3가지 경우가 나오는데 'ECS'로 선택 후 'Next'

 

기본값으로 세팅된대로 쭉쭉 넘어가고 Role Name만 적어주자.

 

역할 생성하기 버튼을 눌러 생성하면 역할 리스트에서 위에서 설정한 Role name으로 검색하면 잘 나와야한다.

 

CodeDeploy용 대상 그룹 생성하기

이 대상 그룹은 왜 생성하냐면, 배포를 하면 배포 전과 배포 후 새로운 서비스로 호스팅을 해줘야 하니까 그 새로운 서비스에 대한 대상 그룹을 만드는 것이다.

 

그 외 값은 기본값으로 만들고 'Next'  이 대상 등록 화면에서는 아무것도 등록하지 않고 만든다.

대상 그룹을 만들었으면 로드밸런서에서 이 대상 그룹을 사용할 수 있도록 추가해주자. ECS용 로드밸런서를 선택하고 리스너 추가를 한다.

 

Listener details 화면에서는 다음 부분을 수정한다.

- Protocal:Port: HTTPS:8443

- Routing actions: Forward to target groups - 방금 만든 타겟 그룹

 

Secure listener settings에서는 Certificate source를 From ACM으로, Certificate은 만들어진 도메인으로 선택한다.

 

이렇게 설정 후 새로운 리스너 규칙을 만들면 다음과 같이 리스트에 잘 노출된다.

 

 

Deployment Type이 Blue/green deployment인 클러스터의 서비스 만들기

이제 ECS로 가서 클러스터에서 서비스를 새로 만들어야 한다. 이 서비스는 CodeDeploy와 연동하는 서비스가 될 것. 다음과 같이 새 서비스를 만들기 원하는 클러스터에서 서비스 탭의 생성 버튼을 누른다.

 

 

Environment는 동일하게 EC2로 Launch type을 설정한다.

 

Deployment configuration은 다음과 같이 수정이 필요하다.

- Application type: Service

- Family: 프론트 엔드 용 Task Definition

- Service name: frontend

 

하단에 Deployment options에서 다음과 같이 수정한다.

- Deployment type: Blue/green deployment

- Service role for CodeDeploy: 방금 위에서 만든 CodeDeploy용 서비스 역할

 

Load balancing에서 다음과 같이 수정한다.

- Load balancer type: Application Load banalcer

- Load balancer: ECS용 로드밸런서

- Listener: 
Production listener> 443:HTTPS / Test listener > 8443:HTTPS

 

대상 그룹은 1번을 기존 frontend-80으로, 2번을 방금 막 만든 bluegreen 80으로 설정한다.

 

그리고 서비스를 생성한다. 서비스를 생성하면 자동으로 CodeDeploy의 Application이 생성된다. (물론 서비스를 생성하고 정상적으로 Task가 Running 상태로 띄워져야 한다. 그렇게 되면 아래 사진처럼 CodeDeploy의 Application이 자동 생성된다.)

 

appspec.yaml 파일 수정하기

CodeDeploy에서 사용할 appspec.yaml 파일을 손보자. 이 파일 형식 역시 다음 링크를 참조한다.

 

2단계: AppSpec 파일 생성 - AWS CodeDeploy

이 페이지에 작업이 필요하다는 점을 알려 주셔서 감사합니다. 실망시켜 드려 죄송합니다. 잠깐 시간을 내어 설명서를 향상시킬 수 있는 방법에 대해 말씀해 주십시오.

docs.aws.amazon.com

 

이 appspec.yaml 파일은 S3에 업로드해서 사용할 것이다. 그리고 샘플 코드는 다음과 같다.

version: 0.0
Resources:
  - TargetService:
      Type: AWS::ECS::Service
      Properties:
        TaskDefinition: "arn:aws:ecs:ap-northeast-2:466324222409:task-definition/my-ecs-frontend-task-definition:1"
        LoadBalancerInfo:
          ContainerName: "frontend"
          ContainerPort: 80

 

appspec.yaml 파일의 TaskDefinition 수정하기

지금 이 파일에서 수정할 부분은 "어떤 Task Definition을 사용해서 서비스를 띄울건데?" 이다. 그러니까 지금까지 한 내용은 프론트 소스코드에 대해서 CodeBuild와 CodeDeploy를 진행했으니까 역시 Task Definition도 프론트 소스에 대해서 배포해야 한다.

 

ECS > Task definitions > 원하는 Task definition 화면에서 ARN 정보를 복사한다.

 

위 코드의 이 부분을 복사한 값으로 수정.

TaskDefinition: 'arn:aws:ecs:ap-northeast-2:135149110460:task-definition/cwchoiit-ecs-frontend-task-def:1'

 

그리고 이 Task definition의 컨테이너가 있다. 그 컨테이너 이름을 가져오면 된다. 다음 화면 참고.

 

저 이름으로 아래 코드에 적용.

ContainerName: 'frontend'

 

이렇게 appspec.yaml 파일이 다 수정이 끝났으면 이 파일을 S3에 올려야 한다. 

 

 

S3에 appspec.yaml 파일 업로드하기

이미 있는 S3나 새로 만들어서 S3에 appspec.yaml 파일을 넣어보자.

 

 

CodeDeploy Deployment 만들기

S3에 appspec.yaml 파일을 넣었으면 CodeDeploy에 가서 Deployment를 만들자. Application은 위 클러스터의 서비스를 Blue/green deployment type으로 만들 경우 알아서 만들어진다. 

 

CodeDeploy > Applications > 자동으로 만들어진 Application 선택

 

안에 들어가면 다음처럼 Deployment 탭이 있다. 해당 탭에서 "Create deployment" 클릭

 

Deployment settings를 다음과 같이 설정한다.

- Deployment group: 방금 클러스터의 Blue/green 배포 타입의 서비스를 만들면서 자동 생성된 배포 그룹 선택

- Revision type: S3

- Revision location: S3의 appspec.yaml 파일 경로

- Revision file type: .yaml

 

그 외 기본값으로 배포 생성을 하면 다음과 같은 화면이 보인다. 배포가 진행중이다.

그리고 우측을 보면 Original 100% Replacement 0%로 나와있는데 이는 새 버전을 배포하기 전 모든 트래픽을 담당하는 쪽은 아직 구 버전의 배포판이라는 것을 말한다. 즉, 새 버전의 배포가 완료되면 모든 외부와의 트래픽 담당은 새 버전의 배포판이 담당하게 된다. (당연히 그래야 한다)

 

모든 배포가 끝났고 이제 Replacement가 모든 트래픽을 담당한다. 여기서 Step 4, Step 5가 아직 남았는데 이는 뭐냐면 구 버전의 배포판을 종료하는데 1시간 정도 대기 시간을 두는것이다. 1시간의 대기시간이 끝나면 구 버전의 배포판은 완전히 Terminated된다. 만약 이걸 기다리기 싫다면 우측 상단 "Terminate original task set"을 누르면 곧바로 구 버전의 배포 인스턴스가 종료된다.

 

 

중간 정리

이렇게 하면 소스 업데이트 후 새로운 배포를 CodeBuild, CodeDeploy를 통해 반 자동화 할 수 있다. 당연히 아직 완전하지 않다. 이 모든 과정을 자동화하고 싶다. 그것을 CodePipeline에서 할 수 있다.

 

 

CodePipeline을 사용해서 Build - Deploy 자동화

이제 CodeBuild, CodeDeploy를 각각 따로 실행해 보았으니 이 둘을 한번에 진행해보자. 이를 위해 CodePipeline 서비스를 사용하면 된다. 근데 그 전에 우리의 buildspec.yaml, appspec.yaml 파일이 있는 프로젝트의 taskdef.json 이라는 파일을 수정해야한다.

 

 

위 사진과 같이 taskdef.json 파일이 있는데 이 파일은 어떤 값이 들어가면 되냐? ECS - Task Definition의 Blue/green으로 배포하는 Task Definition의 JSON 값을 넣어주면 된다.

JSON 탭의 JSON을 복사해서 taskdef.json 파일에 넣어주자. 

 

그리고 한가지 더 확인할 것은 buildspec.yaml 파일에서 다음 코드와 같이 artifacts 부분이다.

buildspec.yaml

version: 0.2

phases:
  pre_build:
    commands:
      - echo Logging in to Amazon ECR...
      - aws --version
      - aws ecr get-login-password --region ap-northeast-2 | docker login --username AWS --password-stdin 135149110460.dkr.ecr.ap-northeast-2.amazonaws.com
      - REPOSITORY_URI=135149110460.dkr.ecr.ap-northeast-2.amazonaws.com/frontend
  build:
    commands:
      - echo Build started on `date`
      - echo Building the Docker image...
      - docker build -t frontend .
      - docker tag frontend:latest $REPOSITORY_URI:v1
  post_build:
    commands:
      - echo Build completed on `date`
      - echo Pushing the Docker images...
      - docker push $REPOSITORY_URI:v1
      - echo Writing image definitions file...
      - printf '[{"name":"frontend","imageUri":"%s"}]' $REPOSITORY_URI:$IMAGE_TAG > imagedefinitions.json
artifacts:
  files:
    - 'image*.json'
    - 'appspec.yaml'
    - 'taskdef.json'
  secondary-artifacts:
    DefinitionArtifact:
      files:
        - appspec.yaml
        - taskdef.json
    ImageArtifact:
      files:
        - imagedefinitions.json

 

artifacts 부분에서 files, secondary-artifacts에 appspec.yaml, taskdef.json이 잘 들어가 있는지 확인해야 한다.

이 확인이 다 끝났으면 AWS Console에서 CodePipeline을 검색해서 들어간다.

 

CodePipeline > Pipelines > Create pipeline 클릭 

 

Pipeline settings 부분은 다음처럼 설정한다.

- Pipeline name: cwchoiit-codepipeline

- Pipeline type: V2

- Service role: New service role

- Role name: 자동 세팅 값

 

그 외 값은 기본값 설정으로 둔 채 'Next'

 

Source 부분은 다음과 같이 설정한다.

- Source provider: Github (Version 2)

- Connection: 여기는 우측 Connect to GitHub 버튼 클릭해서 하라는대로 하면 된다. 그럼 Connection이 생성된다. 하라는대로 하다가 중간에 Install App이 있는데 그것도 해줘야한다. 

- Repository name: aws-frontend

- Pipeline trigger: Push in a branch

- Branch name: master

- Output artifact format: CodePipeline default

 

그리고 'Next'

Build 부분은 다음과 같이 설정한다.

- Build provider: AWS CodeBuild

- Region: Asia Pacific (Seoul)

- Project name: 앞전에 만든 CodeBuild 프로젝트

- Built type: Single build

 

그리고 'Next'

Deploy 부분은 다음과 같이 설정한다.

- Deploy provider: Amazon ECS (Blue/Green)

- Region: Asia Pacific (Seoul)

- AWS CodeDeploy application name: 앞에서 (자동으로) 만든 CodeDeploy Application

- AWS CodeDeploy deployment group: 앞에서 (자동으로) 만든 CodeDeploy Deployment Group

- Amazon ECS task definition: BuildArtifact[taskdef.json]

- AWS CodeDeploy AppSpec file: BuildArtifact[appspec.yaml]

그리고 'Next' 

 

Review 부분을 빠르게 보고 최종 생성을 한다.

 

 

최종 생성을 하면 아래처럼 CodePipeline이 바로 진행이 된다. 즉, 위에서 순서대로 Source, Build, Deploy를 구성한 것을 토대로 하나씩 진행이 된다. Github로부터 소스를 받아오고, CodeBuild로부터 Build가 진행되고, CodeDeploy로부터 Deploy가 진행된다.

 

 

근데 진행중에 마지막 Deploy 단계에서 이런 에러가 난다면 .. 
"Tags can not be empty"

 

taskdef.json 파일에서 "tags"를 지워주면 된다. 본인의 경우 이런 빈 값으로 들어가 있었다.

"tags": []

 

728x90
반응형
LIST