Github Actions, AWS CodeDeploy, S3, ECR, EC2 를 이용한 CI/CD 구축 및 배포하기

이번글은 Docker로 빌드한 이미지를 AWS ECR에 올리고 CdoeDeploy를 통해 EC2에 자동배포를 하기 위해CI/CD 환경을 구축하는 글을 작성해보고자 합니다.
우선 간단하게 정리하면 아래 과정을 거치게 되며 모두 자동으로 실행되도록 github action을 사용합니다.
notion image
  • docker 빌드 스크립트 작성 및 이미지 생성
  • 빌드된 이미지 ecr에 배포 및 이미지 정보를 image.txt 내 저장
  • S3에 image.txt , appspec.yml 그리고 appspec.yml 의 hooks 내 실행시킬 스크립트를 .zip 으로 압축하여 업로드
  • CodeDeploy를 통해 ec2에 배포
모든 과정을 자동화하기 위해 작성한 github action 스크립트는 일단 다음과 같습니다:
이제 하나 하나 알아보겠습니다.

Docker 이미지 빌드

배포하고자 하는 프로젝트를 docker 이미지로 만들기 위한 작업을 진행합니다. 프로젝트 루트내 .docker 폴더를 생성하고 빌드 스크립트를 작성할 DockerFile 을 생성한 뒤 다음과 같이 작성을 하였습니다
  • 저의 경우 nextJS를 사용했으며 next.config.js내 output 옵션을 standlone 으로 설정한 뒤 이미지로 빌드하였습니다. - standalone 장점
작성된 스크립트를 보면 크게 4가지 스텝 작업이 진행이 됩니다.
  • base (베이스 스테이지) - FROM node:18.17.0 AS base
    • nodejs 18.17.0 버전을 기반으로 하는 이미지로 설정을 합니다.
  • deps (의존성 스테이지) - FROM base AS deps
    • WORKDIR 을 통해 작업 폴더를 /app 으로 설정합니다.
    • package.json , pnpm.lock.yaml 을 작업 폴더에 복사합니다.
    • pnpm 패키지 매니저를 글로벌로 설치하고, pnpm install 를 통해 프로젝트 의존성을 설치합니다.
  • builder (빌더 스테이지) - FROM base as builder
    • 작업 폴더를 /app 으로 설정합니다.
    • deps 스테이지의 node_modules 를 복사하여 의존성 설치를 다시 진행하지 안도록 합니다.
    • 프로젝트 전체를 작업 폴더로 복사합니다.
    • yarn build 를 실행하여 프로젝트 빌드를 실행합니다.
  • runner (런너 스테이지) - FROM base as runner
    • 프로젝트를 실행하기 위한 최종 이미지를 설정하는 단계 입니다
    • 동일하게 작업 경로를 /app 으로 설정합니다.
    • nextjsnodejs 를 각 사용자 및 그룹으로 생성합니다..
    • builder 스테이지로 부터 public 폴더를 복사해옵니다.
    • 동일하게 builder 스테이지로 부터 ./next/standalone./next/static 을 복사해옵니다.
    • 포트 3000을 노출시키고, PORT 환경 변수를 3000으로 설정합니다.
    • nodejs를 통해 server.js 을 실행합니다.
이제 해당 프로젝트 내에서 docker 명령문을 실행하면 실제 이미지 빌드를 진행하게 됩니다.
이후 모든 과정이 완료되면 빌드가 아래와 같이 정상적으로 완료된것을 확인 할 수 있습니다.
notion image
notion image

ECR 설정

ECR의 경우 AWS에 지원하는 컨테이너 docker 이미지 레지스트리 서비스로 docker hub와 비슷한 서비스라고 이해하면 될 거 같습니다.

Repository 생성

개인의 상황에 따라 public 혹은 private 을 선택하여 리포지토리를 생성버튼을 클릭합니다
notion image
이후 페이지 내에서 사용할 리포지토리 이름을 원하는것으로 입력한 뒤 생성 버튼을 클릭합니다.
notion image
생성 이후 푸시 명령 을 클릭하면 어떤 명령들을 실행하면 docker 이미지를 repository로 push 할 수 있는지 알려줍니다.
notion image
 
위에 작성했던 github action script의 일부인 아래 스크립트를 확인해보면 aws-actions/amazon-ecr-login@v2 를 활용해 ecr에 로그인을 한 뒤 빌드한 docker 이미지를 ecr에 올리는 작업을 수행합니다.
그리고 이후 codeploy를 통해 배포에 활용하기 위한 이미지 정보를 image.txt 에 저장합니다.
빌드 이미지 이름의 경우 $REGISTRY/$REPOSITORY:$IMAGE_TAG 형식으로 ecr에 올리게 됩니다.
여기서 REGISTRY 의 경우 이미지를 푸시할 ecr 저장소를 가리키는것이며, REPOSITORY 는 ecr에서 생성한 repository 이름, IMAGE_TAG는 github.sha (커밋 식별자)로 지정되게 됩니다.

S3 설정

S3 에서 Bucket을 버킷 만들기 클릭한 뒤 다음 페이지 내에서 버킷 이름 및 상황에 맞는 설정을 진행합니다.
notion image
이후 버킷 내 배포에 사용할 압축 파일을 업로드할 codedeploy 폴더를 생성합니다.
notion image

CodeDeploy 설정

codedeploy는 애플리케이션을 여러 인스턴스에 자동배포 할 수 있는 서비스로 해당 서비스를 통해 배포 프로세스를 자동화하고 배포중 문제 발생시 롤백을 수행 할 수 있습니다.
codedeploy 사용을 위해 애플리케이션 생성 을 클릭하고 이름 및 컴퓨팅 플랫폼을 선택합니다.
  • 저의 경우 EC2에 배포를 할 것이기 때문에 EC2/온프레미스 를 선택했습니다
notion image
생성 이후 배포 그룹을 생성합니다. 배포그룹은 배포 프로세스를 관리하고, 배포 대상이 되는 인스턴스 집합을 나타냅니다
notion image
notion image
  • 서비스 역할의 경우 AWSCodeDeployRole 역할을 가지고 있는 IAM 역할로 지정합니다. 없는 경우 새로 생성합니다.
    • notion image
  • 추가적으로 EC2 인스턴스에서 CodeDeploy Agent 를 통해 S3와 ECR에 접근하기 위한 IAM 역할도 생성한 뒤 ec2에 IAM Role을 부여해줍니다.
    • notion image
      notion image

appspec.yml 파일 작성

CodeDeploy에서 어떻게 애플리케이션을 배포할지 정의하는 appspec.yml 파일을 프로젝트 루트 경로내 생성하고 scripts 폴더를 생성하여 실행시킬 스크립트들을 내부에 정의해놓습니다.
CodeDeploy의 경우 아래과 같은 Event LifeCycle을 가지고 있습니다.
notion image
  • Start: codedeploy 에이전트를 자동으로 실행하고 인스턴스 배포가 시작된다.
  • Application Stop: 이전 프로그램을 중지하는 스크립트를 실행하는 단계
    • 새 버전을 배포할 경우 해당 이벤트를 통해 구 버전을 사용하지 않도록 설정하고 새 버전을 수신 하도록 설정하고 새 버전을 수신하도록 인스턴스를 준비한다.
  • DownloadBundle: codedeploy 에이전트는 새 버전을 인스턴스로 가져온다
  • BeforeInstall: 구버전의 설치 구성을 저장하고, 파일을 복호화 한 뒤, 현재 버전의 백업 생성
  • Install: DownloadBundle을 통해 가져온 Bundle을 압축 해제하고 appspec.yml 에 정의된대로 파일을 지정한 경로로 복사
  • AfterInstall: 프로그램이 시작되기전 프로그램의 구성을 변경 할 수 있다.
  • ApplicationStart: 애플리케이션을 구 버전 대신 새 버전으로 설정
  • ValidateService: 배포가 성공했는지 확인 할 수 있는 검증 로직 실행
  • End: lifecycle의 마지막 이벤트로, 인스턴스의 배포 성공 유무를 중앙서비스에 알린다.
이러한 lifecycle 에 맞춰 내부 내용은 다음과 같은 형식을 가지고 있습니다.
저의 경우 ApplicationStart 이벤트 만을 활용하였으며 해당 이벤트에 실행될 deploy.sh 를 작성해주었습니다.
appspec.xml:
deploy.sh:

배포

배포를 진행하기 전에 ec2에서 codedeploy를 사용하기 위해 codedeploy agent를 설치해주겠습니다.
설치가 완료되면 아래 명령어를 통해 정상적으로 설치가 완료 되었는지 확인합니다.
추가적으로 ec2가 재시작될 경우 매번 켜주기 귀찮기에 codedeploy agent도 자동으로 실행되게끔 설정합니다.
마지막으로 ec2에 IAM 역할이 설정되어 있는지 각 IAM Role에 권한이 잘 들어가 있는지 확인합니다.
그리고 드디어 이제 main 브랜치에 push 를 하면 Github Action이 실행됩니다.
notion image
배포가 완료되면 CodeDeploy에 배포내역이 기록되는것을 확인 할 수 있습니다.
notion image
  • 만약 CodeDeploy에서 배포가 실패한 경우 ec2내 /var/log/aws/codedeploy-agent 경로에 있는 log 기록된 내용을 확인해보면 됩니다.
    • Aws::CodeDeployCommand::Errors::AccessDeniedException 에러
    • 1. 해당 에러의 경우 IAM Role에 CodeDeploy 권한이 없어서 발생하는 것일 수도 있습니다
    • 2. root 계정으로 aws cli 를 실행 한 적이 있을 경우 .awscredential 파일이 존재 할 수 있습니다. 이로 인해 발생한 에러일 가능성도 있습니다. credential 파일을 제거한 뒤 codedeploy agent를 재시작 합니다.
CodeDeploy를 통해 성공적으로 배포가 완료되면 ec2에 docker 이미지가 실행되고 있는것을 확인 할 수 있습니다!ㅎ
notion image
 
 
 

© 2024 dan.dev.log, All right reserved.

Built with NextJS