
AWS 시리즈에서 서버에 배포한 프로젝트에 GitHub Actions를 사용해 CI/CD 및 테스트 자동화를 적용하려고 한다.
배포하려고 하는 서버에 접근할 수 있는 IAM 게정을 생성한다.
빌드된 파일을 업로드할 S3 버킷을 생성한다.
공개되면 안되는 정보 (비밀키 등)를 여기에 입력해준다.

2번에서 발급받은 액세스 키와 비밀 액세스 키를 각각 생성해준다.

나는 application.properties 내에 포함되어 있는 민감한 정보를 숨기기 위해
.gitignore 파일에 application.properties 파일을 추가해뒀다.
그래서 CI/CD 작업을 위해 build가 실행되기 전에 application.properties 파일을 생성해서 함께 build 될 수 있게 하기 위해 secrets에 함께 작성해줬다.

프로젝트에 맞는 항목을 찾아서 configure를 클릭한다.


configure를 클릭하면 .github/workflows 디렉토리 하위에 gradle.yml (gradle일 경우) 파일생성 페이지로 넘어가고 기본 구성이 작성되어 있을 것이다.
여기서 이제 필요없는 부분은 지워주고 java 세팅, gradle 세팅, application.properties 파일 생성, 빌드, S3에 업로드 순서로 진행해주면 된다.
name: Java CI/CD with Gradle
on:
push:
branches: [ "develop", "master" ]
pull_request:
branches: [ "develop", "master" ]
jobs:
build:
runs-on: ubuntu-latest
permissions:
contents: read
steps:
- uses: actions/checkout@v4
- name: Set up JDK 17
uses: actions/setup-java@v4
with:
java-version: '17'
distribution: 'temurin'
- name: Setup Gradle
uses: gradle/actions/setup-gradle@417ae3ccd767c252f5661f1ace9f835f9654f2b5 # v3.1.0
# application.properties 파일 생성
- name: Generate Environment Variables File for Production
run: |
echo "$APPLICATION_PROPERTIES" >> ./src/main/resources/application.properties
env:
APPLICATION_PROPERTIES: ${{ secrets.APPLICATION_PROPERTIES }}
# gradlew 실행 권한 부여
- name: Grant execute permission for gradlew
run: chmod +x gradlew
# 테스트 Skip 하고 clean build
- name: Build with Gradle
run: ./gradlew clean build -x test
# 디렉토리 생성
- name: Make Directory
run: mkdir -p deploy
# Jar 파일 복사
- name: Copy Jar
run: cp ./build/libs/*.jar ./deploy
# zip 파일 생성
- name: Make zip file
run: zip -r ./practice.zip ./deploy
shell: bash
# AWS 접근 환경설정
- 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: ap-northeast-2
# Amazon S3에 업로드
- name: Upload to S3
run: aws s3 cp ./practice.zip s3://버킷이름
작성 후 상단의 Commit Changes 버튼을 눌러준다.

그럼 위 파일에 작성해준대로 develop 브랜치에 push 이벤트가 발생하기 때문에 작성한 프로세스가 실행될 것이다.

성공!
CodeDeploy 애플리케이션과 배포그룹을 생성해준다.
프로젝트의 루트디렉토리에 appspec.yml을 작성해준다.
version: 0.0
os: linux
files:
- source: /
destination: /home/practice/app/
overwrite: yes
permissions:
- object: /
pattern: "**"
owner: ubuntu
group: ubuntu
hooks:
ApplicationStart:
- location: deploy.sh
timeout: 60
runas: ubuntu
ValidateService:
- location: healthCheck.sh
timeout: 60
runas: ubuntu
ApplicationStart와 ValidateService에 정의해준 deploy.sh와 healthCheck.sh 파일을 작성해야한다.
루트디렉토리에 scripts 디렉토리를 만들고 안에 파일들을 생성해준다
#!/bin/bash
export NVM_DIR="$HOME/.nvm"
[ -s "$NVM_DIR/nvm.sh" ] && \. "$NVM_DIR/nvm.sh"
REPOSITORY=/home/practice/app
echo "pm2 실행중인 프로세스 종료"
pm2 kill
echo "> 새 애플리케이션 배포"
JAR_NAME=$(ls -tr $REPOSITORY/*SNAPSHOT.jar | tail -n 1)
echo "> JAR NAME: $JAR_NAME"
echo "> $JAR_NAME 에 실행권한 추가"
chmod +x $JAR_NAME
echo "> $JAR_NAME 실행"
cd $REPOSITORY
pm2 start /home/practice/ecosystem.config.js
#!/bin/bash
echo "> Health check 시작"
echo "> curl -s http://localhost:8080/health/check "
for RETRY_COUNT in {1..15}
do
# RESPONSE body 데이터에 'SUCCESS'이라는 글자가 있다면 성공, 없다면 다시 체크
RESPONSE=$(curl -s http://localhost:8080/health/check)
UP_COUNT=$(echo $RESPONSE | grep 'SUCCESS' | wc -l)
if [ $UP_COUNT -ge 1 ]
then # $up_count >= 1 ("UP" 문자열이 있는지 검증)
echo "> Health check 성공"
break
else
echo "> Health check의 응답을 알 수 없거나 혹은 status가 UP이 아닙니다."
echo "> Health check: ${RESPONSE}"
fi
if [ $RETRY_COUNT -eq 10 ]
then
echo "> Health check 실패. "
exit 1
fi
echo "> Health check 연결 실패. 재시도..."
sleep 10
done
exit 0
healthCheck.sh파일에 작성한/health/check엔드포인트는 spring boot 애플리케이션 내에 health check용 엔드포인트를 작성해줬다@Tag(name = "AWS Health 검사를 위한 API") @RequestMapping("/health") @RestController public class HealthController { @Operation(summary = "AWS Health 검사 API", description = "AWS Health 검사 API") @ResponseStatus(HttpStatus.OK) @GetMapping("/check") public String healthCheck() { return "SUCCESS"; } }
위에서 작성했던 gradle.yml파일의 제일 뒤에 아래 코드를 추가해준다.
# CodeDeploy 실행
- name: Execute CodeDeploy
env:
AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }}
AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
run: |
aws deploy create-deployment \
--application-name CodeDeploy_애플리케이션_이름 \
--deployment-group-name CodeDeploy_배포그룹_이름 \
--file-exists-behavior OVERWRITE \
--s3-location bucket=버킷이름,bundleType=zip,key=zip_파일이름.zip \
--region ap-northeast-2

