CI/CD 및 Test Bed 환경 도입기(AWS CodeBuild, CodeDeploy, CodePipeline)

Yoochan·2024년 5월 12일
0
post-thumbnail

최근에 기업 홈페이지를 구축하는 프로젝트를 진행했었다. 해당 프로젝트에서 나는 CRM과 더불어 Editor.js를 활용한 블로그 관련 기능을 주로 개발했다.
프로젝트 1차 완성 기간이 끝나고 유지보수 기간으로 들어가게 됐는데, 앞으로 나올 유지보수 주요 사항들이 내가 작업했던 기능들의 고도화와 더불어 프론트단의 일부 수정으로 예정되었다. 따라서 유지보수 업무를 내가 도맡아 진행하게 되었다.

1차 프로젝트 기간과 다르게, 유지보수 업무를 진행함에 있어 CI/CD와 더불어 Test Bed 환경을 구축하여 유지보수 프로세스를 강화하기로 결정했다.

우선 기존에 프로젝트를 진행했던 방식을 살펴보면, 아래와 같은 프로세스로 배포를 진행했다.

front / back 레포가 존재하는 상태. 변경 사항을 master 브랜치에 푸쉬한 뒤, 각각의 인스턴스에 접속해서 명령어를 실행시켜 직접 배포 작업을 진행.
그리고 이런 적용사항들은 별도의 테스트 도메인 없이 작업한 내용이 바로 Publish 도메인에 배포되는 방식.

어떤 단점들이 있을까?

1. 일단 각각의 변경 사항들을 수동으로 명령어를 입력해서 배포시키는 것 자체가 비효율적인 작업임
2. 배포 과정에서 본 서버에 적용시키기 전에 문제가 없는지 확인을 해보겠지만, 사람이 하는일인지라, 혹시라도 빠뜨려서 사이트에 영향이 가능 문제가 생긴다면? → 매우 크리티컬한 문제임. 해당 프로젝트를 진행하며 실제로 겪었던 문제...

CI/CD를 통해 1번 문제를, Test Bed 환경 구축을 통해 2번 문제를 해결해고자 함.

따라서 앞으로 3달 정도 간 진행될 유지보수 업무에서는 테스트 환경을 도입하여 아래와 같은 프로세스를 도입하기로 결정했다.

유지보수 사항 요청(클라이언트) -> 작업 확인 요청(Me) -> 검수 완료(클라이언트) -> 배포 완료(Me)

즉, 내가 처리한 유지보수 사항들을 클라이언트 측이 테스트 환경에서 확인해볼 수 있고, 검수가 완료되었을 때 본 서버에 적용되는 프로세스를 도입해야했다.

위 프로세스를 적용하기 위해 AWS CodePipeline을 활용하였다.

MSA(Micro Service Architecture)과 CI/CD

우선 해당 내용을 적용하기 위해 현재 단계에서 필요한 MSACI/CD의 개념에 대해 간단하게 정리해보았다.

Micro Service Architecture

작은 서비스가 여러 개가 모여서 하나의 시스템을 제공하는 아키텍처.

각 서비스는 작고 독립적이며, 느슨하게 결합되어 있다. 때문에 서비스들을 독립적으로 배포할 수 있으며, 전체 프로그램을 빌드한 뒤에 재배치하지 않고도 기존 서비스들을 업데이트 할 수 있다.

즉, 큰 문제(task, service)들을 작은 문제로 분해하여 해결하는 것이며, 작게 나뉘어진 서비스가 서로에게 영향을 미치지 않고 독립적으로 역할을 수행하게 만드는 것. 따라서 특정 서비스에 에러가 발생해도 다른 서비스에게 영향을 미치지 않게 되며, 서비스가 실패할 경우 롤백 매커니즘을 두어, 이전 버전으로 쉽게 되돌릴 수 있다.

CI/CD

CI/CD는 지속적인 통합, 지속적인 제공, 지속적인 배포에 관한 것이다.

  • 지속적인 통합(Continuous integration)
    • 코드 변경 사항은 보통 main branch에 merge 된다. 자동화된 빌드 및 테스트 프로세스는 main branch에 있는 코드가 항상 높은 생산성 품질을 유지하도록 보장한다.
  • 지속적 제공(Continuous delivery)
    • CI 프로세스를 통과한 모든 코드 변경 사항은 자동적으로 프로덕션과 유사한 환경에 배포된다. 실제 프로덕션 환경에 배포하려면 수동 승인이 필요할 수 있지만, 그렇지 않으면 자동화된다. 목표는 코드가 항상 프로덕션에 배포될 준비가 되어 있어야 한다는 것이다.
  • 지속적 배포(Continuous deployment)
    • 위 두 단계를 거친 코드의 변경 사항은 자동적으로 프로덕션 환경에 배포된다.

MSA와 CI/CD의 개념에 입각하여 해당 프로젝트에서 다음과 같이 활용해볼 수 있겠다고 생각이 들었다.

microservices을 위한 CI/CD 목표

  • 각 팀은 다른 팀에 영향을 주거나 방해하지 않고, 독립적으로 소유한 서비스를 빌드하고 배포할 수 있다 → 각 issue(유지보수 issue 하나에 해당하는) 별로 해결한 문제를 독립적으로 수정하고 배포할 수 있음
  • 서비스의 새로운 버전이 프로덕션에 배포되기 전에, 검증을 위해서 개발/테스트/QA 환경에 배포된다 → 클라이언트 측에서 확인 후 승인 시 배포
  • 서비스의 새로운 버전을 이전 버전과 함께 배포할 수 있다

AWS CodePipeline이란?

AWS에서는 CodePipeline을 통해 CI/CD 환경을 구축할 수 있다.

파이프라인(Pipeline)이란 소스 코드의 관리부터 실제 서비스로의 배포 과정을 연결하는 구조로, 전체 배포 과정을 여러 단계(Stages)로 분리한다. 파이프라인 안에서 순차적으로 실행되며, 각 단계마다 주어진 작업(Actions)들을 수행한다.

파이프라인의 단계는 상황과 필요에 따라 더 세분화되거나 간소화될 수 있지만, 통상적으로는 Source - Build - Deploy의 단계를 구성하며, 해당 파이프라인에도 다음 단계를 구성하고자 한다.

  1. Source 단계: Source 단계에서는 원격 저장소에 관리되고 있는 소스 코드에 변경 사항이 일어날 경우, 이를 감지하고 다음 단계로 전달하는 작업을 수행
  2. Build 단계: Build 단계에서는 Source 단계에서 전달받은 코드를 컴파일, 빌드, 테스트하여 가공. 또한 Build 단계를 거쳐 생성된 결과물을 다음 단계로 전달하는 작업을 수행.
  3. Deploy 단계: Deploy 단계에서는 Build 단계로부터 전달받은 결과물을 실제 서비스에 반영하는 작업을 수행.

AWS에서 제공해주는 개발자 도구를 옵션들을 통해 CodePipeline을 수월하게 구축할 수 있다.

  1. CodeCommit
    1. Source 단계를 구성할 때 CodeCommit 서비스를 이용. CodeCommit은 GitHub과 유사한 서비스를 제공하는 버전 관리 도구
    2. 하지만 파이프라인에서 CodeCommit 말고 Github 레포로 직접 Source 단계를 연결할 수 있기 때문에 따로 만들지는 않아도 됨.
    3. CodeBuild
      1. Build 단계에서는 CodeBuild 서비스를 이용. CodeBuild 서비스를 통해 유닛 테스트, 컴파일, 빌드와 같은 빌드 단계에서 필수적으로 실행되어야 할 작업들을 명령어를 통해 실행
      2. CodeBuild 서비스는 사용자가 작성한 buildspec.yml 파일을 참조하여 작업을 수행
    4. CodeDeploy
      1. Deploy 단계에서 실행되고 있는 서버 애플리케이션에 실시간으로 변경 사항을 전달
      2. 사용자가 작성한 appspec.yml 파일을 참조하여 작업을 수행
        1. appspec.yml은 배포 자동화를 도와주는 CodeDeploy-Agent가 인식하는 파일
      3. S3 서비스를 통해 S3 버킷을 통해 업로드된 정적 웹 사이트에 변경 사항을 실시간으로 전달하고 반영
    5. CodePipeline
      1. 각 단계를 연결하는 파이프라인을 구축할 때 CodePipeline 서비스를 이용

정리하자면, CodePipeline은 CodeCommit, CodeBuild, CodeDeploy를 하나의 프로세스로 통합시켜주는 CI/CD 도구이다.

CodePipeline을 적용시키 전에, 기존에 배포했던 과정을 요약해보고 어떻게 적용시킬지 점검해보자.

기존

  • 프론트엔드: master 브랜치의 변경사항 pull → build → build된 폴더를 기반으로 pm2 start “yarn preivew” 명령어 실행
  • 백엔드: master 브랜치의 변경사항 pull → apache2 restart 명령어 실행

위 기존의 과정을 살펴봤을 때, 프론트엔드는 Source → Build → Deploy의 과정을, 백엔드는 Source → Deploy의 과정만을 적용시키면 된다. 이에 맞게 Pipeline을 구축해보자.

우선 CodePipline을 적용시키기 위해, CodeBuild 빌드 프로젝트와 CodeDeploy 애플리케이션을 각각 생성해줘야한다.

CodeBuild 빌드 프로젝트 생성



해당 프로젝트의 Github repository 기반으로 빌드 작업을 수행해야하므로 다음과 같이 소스 공급자를 선택해준다.

환경에 맞는 이미지 설정을 해준다.

중요한 부분이 다음 Buildspec 관련이다. 해당 설정으로 빌드 명령을 구성할 수 있다. github master 브랜치에 buildspec.yml 파일을 만들고 다음과 같이 작성했다.

version: 0.2

phases:
  build:
    commands:
      - BASE_URL=$BASE_URL yarn install
      - yarn build
artifacts:
  files:
    - .output/**/*
    - scripts/**/*
    - appspec.yml
    - package.json

yarn install을 통해 필요한 모듈들을 다운받고 build 명령어를 실행한다. 여기서 BASE_URL은 api 주소가 들어가게 된다. api도 dev/publish 버전이 각각 존재하기 때문에, 그에 맞는 BASE_URL을 넣어주는 것이다. Code Pipeline에서 build 프로젝트를 등록할 때 환경변수로 넘겨줄 값을 설정해줄 수 있다. 뒤의 과정에서 나오겠지만, $BASE_URL에 맞는 값을 넣어줄 예정이다.

artifacts files에 정의한 파일들을 빌드 결과물로 넘겨주게 된다. 해당 프로젝트는 Nuxt.js를 통해 개발을 진행했는데, Nuxt는 프로덕션용 애플리케이션을 구축할 때 .output/ 디렉토리를 생성하므로 해당 파일의 경로를 지정했다.

넘겨준 아티팩트의 결과물을 저장할 버킷 정보를 작성한다. 기존에 미리 생성한 S3 버킷 정보를 입력했다.

위 정보들을 입력하고 프로젝트 생성을 누르면 빌드 프로젝트가 생성된다.

Pipeline에 적용시키 전에 상세 페이지에서 빌드가 잘 되는지도 테스트 해볼 수 있다.

CodeDeploy 애플리케이션 생성


배포할 플랫폼이 EC2이므로 선택 후 애플리케이션을 생성한다.

애플리케이션 생성 후 배포 그룹을 생성해줘야 하는데, 사전 작업으로 IAM에 CodeDeploy 관련 정책이 부여된 역할을 생성해야한다.

그러고 나서 배포 그룹을 생성 과정을 수행하면 된다.



front-dev 버전의 해당 인스턴스를 배포 대상으로 설정해준다.

그러고 배포 그룹을 생성하면 된다.

배포 그룹 설정 뒤에 추가적으로 두 가지 작업을 진행해야 한다.

1. AppSpec File (EC2/온프레미스 컴퓨팅 플랫폼을 사용하는 경우) 작성

AppSpec file은 appspec.yml이라는 YAML 형식의 파일이어야 하며, 애플리케이션 소스 코드의 루트에 위치 시켜야한다. 난 아래와 같이 appspect.yml 파일을 작성했다.

version: 0.0
os: linux
files:
  - source:  /
    destination: /home/ubuntu/build/
    overwrite: yes
file_exists_behavior: OVERWRITE
permissions:
  - object: /
    pattern: "**"
    owner: ubuntu
    group: ubuntu

hooks:
  ApplicationStart:
    - location: scripts/deploy.sh
      runas: ubuntu
  • source : 인스턴스에 복사할 업데이트 파일 또는 디렉터리를 식별한다.
  • destination : 인스턴스에서 파일이 복사되어야 하는 위치를 식별한다.
  • file_exists_behavior: OVERWRITE → 기존에 같은 파일이 존재하면 overwrite 한다.

그리고 hooks를 통해 원하는 deploy 단계에서 특정 명령을 실행시킬 수 있다. 나는 Application이 시작 됐을 때 실행시킬 deploy.sh를 정의했다. 해당 파일에서는 변경된 빌드 파일을 기반으로 한 preview을 작동시키는 코드를 작성했다.

#!/bin/bash deploy.sh
REPOSITORY=/home/ubuntu/build
HOME=/home/ubuntu

pm2 start "yarn preview"

2. EC2에 CodeDeploy Agent 설치
아래 명령어들을 차례대로 실행시키면 CodeDeploy Agent를 설치할 수 있다.

# apt 저장소 업데이트
sudo apt-get update

# ruby 설치
sudo apt-get install ruby

# wget 설치
sudo apt-get install wget

# Agent 설치 (region-identifier를 적절히 적어줘야함. 아래 명령어는 Seoul Region)
wget https://aws-codedeploy-ap-northeast-2.s3.ap-northeast-2.amazonaws.com/latest/install

chmod +x ./install
sudo ./install auto

# codedeploy-agent 상태 확인
sudo service codedeploy-agent status

# codedeploy-agent 서비스 시작
sudo service codedeploy-agent start

해당 과정까지 마치면 CodeDeploy를 위한 모든 작업을 완료한 것!

이제 CodePipeline에 만든 CodeBuild 빌드 프로젝트와 CodeDeploy 프로젝트를 적용시켜주면 된다.

CodePipeline 생성

1. 소스 스테이지 추가


위에서 언급했듯이, 레포지토리의 master 변경사항을 기반으로 파이프라인을 실행시킬 것이다. 따라서 소스 공급자를 Github로 하고 해당 레포지토리를 연결해주면 된다.

그리고 트리거에 master 브랜치를 명시해준다.

2. 빌드 스테이지 추가


기존에 생성했던 CodeBuild 빌드 프로젝트를 선택해주면 된다. 그리고 아까 언급했던 환경변수를 여기서 설정해줄 수 있다.

3. 배포 스테이지 추가


마찬가지로 기존에 생성했던 CodeDeploy 애플케이션의 배포 그룹을 지정해주면 된다.

그럼 이렇게 파이프라인이 성공적으로 작동하는 것을 확인할 수 있다.

백엔드에 해당하는 파이프라인도 위의 과정과 모두 동일하다. 다른 점은 Github 레포지토리만 back에 해당하는 코드로 지정해주면 되는 것, build 과정이 빠지는 것, deploy.sh 코드가 백엔드에 맞게 바뀌는 것 뿐이다.

이렇게 CI/CD를 적용하여 dev 환경을 구축했다.

추가로 승인 후 본 도메인에 변경사항이 배포되는 Pipeline을 추가로 달아줬다.

이제 각 pr에서 처리된 유지보수 사항들이 master branch에 merge되면, 자동으로 테스트 도메인에 변경사항이 적용된다. 이것을 클라이언트 측에서 검수하고, 문제가 없으면 승인을 하고 본 도메인에 변경사항이 배포된다.

이렇게 앞으로 진행될 유지보수 프로세스를 성공적으로 구축했다.

0개의 댓글