프로젝트 코드의 통합 및 배포를 자동화하는 과정에 예전부터 관심 있었는데 이번 기회에 직접 처음부터 모든 작업을 다 해보면서 겪었던 과정을 적어보려고 한다!
본 글에서는 Java(Gradle) + Spring 프로젝트에 CI/CD를 적용해볼것이다
전체적인 파이프라인은 다음 그림이랑 거의 같다

그림 출처: aws blog - Integrating with GitHub Actions – CI/CD pipeline to deploy a Web App to Amazon EC2
텍스트로 설명을 하자면
1. Github Repository에 push가 되면 (trigger 조건)
2. Github action 이 작동하면서 해당 프로젝트를 빌드한 뒤
3. AWS IAM을 통해 AWS 권한 인증을 처리하고
4. 빌드 파일 (.jar)을 압축하여 S3에 업로드한다
5. S3에 압축파일이 업로드되면 aws CodeDeploy 애플리케이션이 실행되고
6. 최종적으로 aws EC2에서 빌드 파일을 백그라운드로 실행하게 한다
(그림에 나와있는 EC2 auto scaling 작업은 진행하지 않는다)
Github action 탭에 들어가면 기존에 작성되어있는 workflow를 가져다 쓸 수도 있고 본인이 직접 만들 수도 있다
workflow를 만들게 되면 레포지토리의 .github/workflows 폴더안에 yml파일이 생기게 된다
다음은 현재 본인이 사용중인 workflow yml 파일을 가져온 것이다
name: Execute CD
on:
push:
branches: [ "develop" ]
permissions:
contents: read
env:
BUCKET_NAME: space-club-be-bucket
DIRECTORY_NAME: space-club-be-directory
CODE_DEPLOY_APP_NAME: space-club-be-deploy
DEPLOYMENT_GROUP_NAME: space-club-be-deploy-group
jobs:
build:
runs-on: ubuntu-latest
steps:
- name: Check Repo code With Submodules
uses: actions/checkout@v3
with:
submodules: 'true'
token: ${{secrets.GH_ACCESS_TOKEN}}
- name: Setup JDK 17
uses: actions/setup-java@v3
with:
java-version: '17'
distribution: 'temurin'
- name: Set encrypt Secret key
run: echo ${{ secrets.encrypt }} > scripts/properties.sh
shell: bash
- name: Permission for gradlew
run: chmod +x ./gradlew
shell: bash
- name: Gradle Build Action
uses: gradle/gradle-build-action@bd5760595778326ba7f1441bcf7e88b49de61a25 # 2.6.0
with:
arguments: bootJar
- name: Make Zip File
run: zip -qq -r ./$GITHUB_SHA.zip .
shell: bash
- name: Configure AWS credentials
uses: aws-actions/configure-aws-credentials@v3
with:
aws-access-key-id: ${{ secrets.ACCESS_KEY }}
aws-secret-access-key: ${{ secrets.PRIVATE_KEY }}
aws-region: ap-northeast-2
- name: Upload to S3
run: aws s3 cp --region ap-northeast-2 ./$GITHUB_SHA.zip s3://$BUCKET_NAME/$DIRECTORY_NAME/$GITHUB_SHA.zip
- name: Deploy to EC2 Instance
run: aws deploy create-deployment --application-name $CODE_DEPLOY_APP_NAME --deployment-group-name $DEPLOYMENT_GROUP_NAME --deployment-config-name CodeDeployDefault.OneAtATime --s3-location bucket=$BUCKET_NAME,bundleType=zip,key=$DIRECTORY_NAME/$GITHUB_SHA.zip

이 프로젝트에서는 서브모듈을 사용하여 기밀 정보를 관리했는데 자세한 사항은 인터넷에 검색을 추천한다
위 workflow는 서브모듈이 적용된 깃허브 레포의 workflow 이므로 서브모듈을 적용하지 않은 경우 첫번째 step의with부분을 지우면 된다
${{ secrets.xxx }}에 해당하는 변수들은 깃허브 레포에서 따로 입력해줘야 한다이 부분에서는 AWS 전반적인 내용보다는 CodeDeploy와 그에 필요한 내용들 위주로 작성해보겠다
우선, IAM 역할부터 생성해보도록 하자
IAM 접속 후 역할 -> 역할 생성

CodeDeploy 선택CodeDeploy선택 (우리는 EC2로 배포를 할 거니깐)이름 입력칸 뜰 때까지 다음 클릭

AWS CodeDeploy 에 접속하여 애플리케이션 생성 클릭

애플리케이션 이름은 위에 CODE_DEPLOY_APP_NAME과 동일하게 작성
그 다음 배포 그룹 생성 클릭

배포 그룹 이름엔 위에 DEPLOYMENT_GROUP_NAME과 동일하게 작성
서비스 역할은 앞에서 만든 codedeploy IAM 역할 설정

환경 구성 ~ 로드 밸런서 설정은 아래와 같게 두기

AWS S3에 접속하여 버킷 만들기 클릭

버킷 이름엔 위에 BUCKET_NAME과 동일하게 작성
나머지 옵션은 기본 설정 사용
다시 IAM에 접속 -> 역할 생성 클릭

이번엔 사용사례에 EC2 선택
권한은 아래 2개 권한 체크해주기 (권한이 너무 많으니 검색해서 찾기 추천)

sudo apt update
sudo apt install ruby-full
sudo apt install wget
cd /home/ubuntu
wget https://aws-codedeploy-ap-northeast-2.s3.ap-northeast-2.amazonaws.com/latest/install
chmod +x ./install
sudo ./install auto
sudo service codedeploy-agent status

sudo service codedeploy-agent start
깃허브 리포지토리 최상단에 appspec.yml 이름으로 파일 생성
내용은 아래와 같이 작성 (os: ubuntu 기준, amazon-linux로 생성시 내용 수정 필요)
version: 0.0
os: linux
files:
- source: /
destination: /home/ubuntu/action
overwrite: yes
permissions:
- object: /
pattern: "**"
owner: ubuntu
group: ubuntu
hooks:
ApplicationStart:
- location: scripts/deploy.sh
timeout: 60
runas: ubuntu
깃허브 리포지토리 최상단에 scripts 폴더 생성, 그 내부에 deploy.sh 파일 생성
내용은 아래와 같이 작성
(본인은 shell 언어를 잘 모름, 따라서 잘못됐거나 불필요한 부분이 있을 수 있음)
source /home/... 이 줄 지워야 함PROJECT_NAME은 본인 프로젝트의 이름으로 변경#!/usr/bin/env bash
source /home/ubuntu/action/scripts/properties.sh
PROJECT_NAME=space-club-backend
REPOSITORY=/home/ubuntu/action
PACKAGE=$REPOSITORY/build/libs/
JAR_NAME=$(ls -tr $PACKAGE | grep 'SNAPSHOT.jar' | tail -n 1)
JAR_PATH=$PACKAGE$JAR_NAME
echo $JAR_NAME
echo $JAR_PATH
cd $REPOSITORY
CURRENT_PID=$(pgrep -f $PROJECT_NAME)
if [ -z $CURRENT_PID ]
then
echo "> 종료할 애플리케이션이 없습니다"
else
echo "> 실행 중인 애플리케이션 종료 $CURRENT_PID"
kill -15 $CURRENT_PID
sleep 5
fi
echo "> 배포 - $JAR_PATH"
chmod +x $JAR_PATH
sudo nohup java -jar $JAR_PATH --spring.profiles.active=develop --jasypt.encryptor.password=${encrypt} > /home/ubuntu/log/nohup_log.out 2> /home/ubuntu/log/nohup_error.out &
본인이 설정한 브랜치로 push 를 날린 후 ec2에서 서버가 정상적으로 뜰 때까지 1~2분 정도 걸릴 수 있다
만약 예상치 못한 문제사항 발생 시 댓글로 남겨주시면 해당 사항 확인 후 아는 선에서 최대한 도움을 드려보겠습니다
ec2 인스턴스에서 해줘야 하는 작업: https://docs.aws.amazon.com/ko_kr/codedeploy/latest/userguide/codedeploy-agent-operations-install-ubuntu.html
위 작업에서 리전의 식별자 안내: https://docs.aws.amazon.com/ko_kr/codedeploy/latest/userguide/resource-kit.html#resource-kit-bucket-names