이 글은 프론트엔트(4) + 디자이너(1) + 백엔드(2) 인원으로 진행한 FeedB 웹 애플리케이션을 진행하다 Infra 구축을 맡게 되어 진행 후 정리하여 기록함으로서 복습의 효과를 얻기 위해 작성한 글입니다.
(전체적인 구조와 흐름 위주로 작성하고 aws 사용설명 부분은 자세하게 기록하지 않습니다. 전체적인 구조를 참고하여 순서대로 해나가는 것이 큰 도움이 된다고 생각합니다.)
☀️ CI/CD Architecture

CI / CD 자동화 무중단 배포 장점
CI / CD 적용 전
- 개발자들이 개발하여 코드를 수정한다.
- 각자의 브랜치에 코드를 push 한다. (에러가 발생해도 알아차리기 어렵다.)
- branch를 main branch에 통합(merge)한다.
- EC2 환경에서 배포하기 위해 build를 진행한다.
- 에러가 발생하고, 코드를 다시 개발 환경으로 가져와서 코드를 수정한다.
- (1) ~ (5)의 과정을 반복한다.
- 에러가 해결되었으면 다시 배포를 시작한다. 개발자가 위의 과정을 수동으로 진행하므로 많은 시간을 소요하게 된다.
CI / CD 적용 후
- 개발자들이 개발하여 브랜치에 코드를 push 한다.
- git pull Request를 통해 Trigger 되어 Github Action에서 알아서 build 및 테스트를 실행하고 결과를 전송한다.
- 개발자들은 결과를 전송받고 에러가 난 부분이 있다면 에러 부분을 수정하고 코드를 main 브랜치에 merge 한다.
- main 브랜치에 코드를 merge 하고 Build, Test가 정상적으로 수행이 되었다면 CI / CD 서버에서 알아서 Deploy(배포) 과정을 수행한다.
CI / CD 자동화 배포에 대한 저의 개인적인 생각으로는 격렬하게 필요하다 생각하는 편입니다. 물론 아직 실무 단계에서 가상 클라우드 환경에서 사용해 볼 기회가 없어서 확실할 순 없지만 토이프로젝트들을 진행하면서 수동 배포로 2 개의 프로젝트를 진행하고 자동화 배포를 적용 해보니 생산성 효율성 측면에서 말도 안되게 상승 했다는 느낌을 체감하고 있습니다.(이 부분은 정말 말로 하는 것보다 직접 수동 -> 자동 배포의 과정을 겪으면 신세계를 경험할 수 있다고 생각합니다!)
물론 , CI / CD Infra를 구축 접근성 부분에서 어려운 부분이 있지만 본격적인 코드 작성 전 CI / CD 환경을 구축하고 프로젝트를 시작하면 프론트분들과의 협업에서도 서버 최신화를 빠르게 대응 할 수 있고 같은 백엔드분과의 코드를 합치는 과정에서도 충돌을 미연에 방지해줍니다.(어서 빨리 AWS Freetier가 아닌 비용을 지불하여 좋은 Infra를 구축해보는 날이 왔으면 좋겠습니다ㅎㅎ.)
☀️ CI / CD 인프라 구축
1. Github Action + S3 + CodeDeploy + EC2 tool을 선택 한 이유
이번 팀 프로젝트 버전 관리를 Github 로 진행하고 팀원들의 PR 을 거쳐 main branch로 push되는 과정에서 build 및 test를 자동화 하는 것이 목적이기 때문에 Github Action을 선택했습니다. 그리고 S3는 이전 프로젝트에서는 Github Action에서 jenkins를 통해 직접 EC2 서버에 build 파일을 복사하는 형태로 사용해봤는데 배포 기록들을 보고 싶을 때 Jenkins로 들어가서 확인하는 방법보다 AWS에서 모든 걸 해결해보고 싶은 생각이 들어 선택하였습니다. 또한 , 배포 파일들이 저장된다는 부분이 큰 장점으로 느껴졌습니다.
CodeDeploy는 AWS 의 여러 기능들을 사용해보는 것이 목표였는데 마침 S3와 EC2와의 시너지가 좋고 간편하며 무료라는 점에서 사용 하지 않을 이유가 없다고 생각했습니다. 그리고 CodeDeploy log 관리를 경험해보고 싶어 선택했습니다. EC2는 저의 목표중에 실무에서도 Infra를 만져보는 것이 목표인데 현재 여러 IT 기업에서 가상 클라우드 중 AWS의 비중이 많다고 생각하여 익숙해지고 싶어 선택하게 되었습니다.
2. 적용 순서
Github Action -> S3 -> CodeDeploy -> EC2 CI/CD 적용을 순서대로 기록 해보겠습니다.
(AWS에서 각 tool들의 사용 과정을 자세히 설명하지 않고 간단히 글로만 설명하고 script 코드 위주로 기록합니다.)
1. CI.yml 작성(Github Action)
Pull Request 요청 시 build 및 Test 과정을 통해 해당 코드에 문제가 없는 지 확인하는 동작을 CI.yml 파일에 작성
# workflow 이름
name: CI
# workflow 실행 트리거 설정
on:
pull_request:
branches: [ main ] # main branch로 pull request 될때만 실행
#CI는 pr 요청시에만 적용하고 merge(push) 시에는 CD만 적용되게 구현(어짜피 CD에 build 로직 있기 때문)
# push:
# branches: [ main ] # main branch에 커밋이 푸시될 때 실행(pr merge시 main 으로 push 되니 동일)
# job - 워크플로우의 업무? 일? 이라고 생각하면 편함 , 각각의 job(업무)는 step(단계별)로 나눌 수 있다.
jobs:
build:
name: CI
runs-on : ubuntu-latest # 해당 설정은 workflows의 가상머신 환경 설정(실제 배포 서버에서의 OS와는 무관)
steps:
- uses: actions/checkout@v2
- name: Set up JDK 21
uses: actions/setup-java@v2
with:
java-version: '21'
distribution: 'zulu'
- name: Grant execute permission for gradlew
run: chmod +x ./gradlew
shell: bash
- name: Build with Gradle
run: ./gradlew build
shell: bash
위와 같이 작성이 완료되면 Pull Request를 통해 정상 작동 하는 지 확인(만약 Build 또는 Test 과정에서 문제가 있으면 하단의 이미지와 같이 개발자들이 인지할 수 있습니다.)

2. CD.yml 작성(Github Action)
CD.yml 을 작성하기 전에 AWS에서 S3를 생성하고 S3 접근 키 필요
name: CD
#CD 워크플로우 실행 트리거 설정
on:
push:
branches: [ main ]
env:
S3_BUCKET_NAME: 본인 S3 이름
PROJECT_NAME: 본인 S3 Project 이름
jobs:
build:
name: CD
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: Set up JDK 21
uses: actions/setup-java@v2
with:
java-version: '21'
distribution: 'zulu'
- name: Grant execute permission for gradlew
run: chmod +x ./gradlew
shell: bash
- name: Build with Gradle
run: ./gradlew build
shell: bash
- name: Make zip file
run: zip -r ./$GITHUB_SHA.zip .
shell: bash
- name: Configure AWS credentials
uses: aws-actions/configure-aws-credentials@v1
with:
aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }}
aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
aws-region: ${{ secrets.AWS_REGION }}
# script file 복사(지금은 ./deploy폴더에 복사하는 이유를 뚜렷이 모르겠다. 추후에 생 해도 문제 없으면 주석)
- name: Copy script
run : cp ./scripts/*.sh ./deploy
#S3에 업로드
- name: Upload to S3
run: aws s3 cp --region ap-northeast-2 ./$GITHUB_SHA.zip s3://$S3_BUCKET_NAME/$PROJECT_NAME/$GITHUB_SHA.zip
위와 같이 작성 후 main branch에 pust하고 AWS S3 대시보드에서 파일이 업로드 되었는 지 확인.

3. CodeDeploy 생성 및 CD.yml 에 실행 코드 추가
AWS CodeDeploy에서 애플리케이션을 생성하고 애플리케이션을 실행할 EC2 서버와 연동해줍니다.
그리고 CD.yml 에 CodeDeploy 실행하는 코드 작성합니다.(하단 이미지 참고)
# Deploy
- name: Deploy
run: |
aws deploy create-deployment \
--application-name 본인 codeDeploy 애플리케이션 이름 \
--deployment-config-name CodeDeployDefault.AllAtOnce \
--deployment-group-name 본인 group \
--file-exists-behavior OVERWRITE \
--s3-location bucket=feedb-bucket,bundleType=zip,key=본인 키/$GITHUB_SHA.zip \
4. appspec 작성(codeDeploy 동작 파일)
CodeDeploy 공식 문서를 보면 appspec 파일을 이용해 동작 하기 때문에 S3에게 받은 zip 파일을 EC2에 복사해주고 EC2 서버에서 배포 스크립트(deploy.sh)를 실행하는 부분을 작성합니다.
(appspec 파일 경로는 내 프로젝트의 /(루트) 경로에 작성합니다)
# codedeploy 동작 정의
version: 0.0
os: linux
#S3에 있는 zip 파일이 EC2에 배포될 위치를 지정
files:
- source: / # CodeDeploy에서 전달해 준 파일 중 destination으로 이동시킬 대상을 루트로 지정(전체파일)
destination: /본인 경로 # <- 이후 jar를 실행하는 등은 destination에서 옮긴 파일들로 진행
overwrite: yes
permissions:
- object: /
pattern: "**"
owner: ec2-user
group: ec2-user
#ApplicationStart 단계에서 deploy.sh를 실행시키도록 함
hooks: # CodeDeploy배포 단계에서 실행할 명령어를 지정합니다.
ApplicationStart: # deploy.sh를 ec2-user권한으로 실행합니다.
- location: scripts/deploy.sh
timeout: 60 # 스크립트 실행 60초 이상 수행되면 실패
runas: ec2-user
추가적으로 CodeDeploy 가 EC2에서 배포를 진행할 수 있게 배포 스크립트(deploy.sh)를 작성합니다.
BUILD_JAR=$(ls /본인 jar파일 경로 | grep -i 'SNAPSHOT.jar$')
JAR_NAME=$(basename $BUILD_JAR)
echo "> build 파일명: $JAR_NAME" >> /본인경로/deploy2.log #deploy.log에 기록 남기기
echo "> build 파일 복사" >> /본인경로/deploy2.log
DEPLOY_PATH=/home/ec2-user/
cp $BUILD_JAR $DEPLOY_PATH
echo "> 현재 실행중인 애플리케이션 pid 확인" >> /본인경로/deploy2.log
CURRENT_PID=$(pgrep -f $JAR_NAME)
if [ -z $CURRENT_PID ]
then
echo "> 현재 구동중인 애플리케이션이 없으므로 종료하지 않습니다." >> /본인경로/deploy2.log
else
echo "> kill -15 $CURRENT_PID"
kill -15 $CURRENT_PID
sleep 5
fi
DEPLOY_JAR=$DEPLOY_PATH$JAR_NAME
echo "> DEPLOY_JAT 배포" >> /본인경로/deploy2.log
nohup java -jar $DEPLOY_JAR >> /home/ec2-user/deploy2.log 2>/home/ec2-user/deploy2_err.log &
마지막으로 main branch에 push를 통해 codeDeploy까지 작동하고 EC2 서버에 애플리케이션이 배포 되는 지 확인합니다.
여기까지가 Github Action -> S3 -> CodeDeploy -> EC2 CI/CD 까지의 배포 자동화 적용하는 순서입니다.
그 다음 글로 Nginx 무중단 배포를 적용하는 과정을 글로 작성 할 생각입니다.