Github Action: 빌드, 테스트 및 파이프라인을 자동화 할 수 있는 CI/CD 플랫폼으로, repository에서 이벤트가 발생할 때 workflow를 실행
EC2: 서버
S3: 배포할 결과물을 저장
CodeDeploy: 배포를 돕는 도구
변경된 코드가 GitHub에 푸시됨: 개발자가 새로운 코드 변경 사항을 GitHub 리포지토리에 푸시
GitHub Actions 트리거: GitHub 리포지토리에 코드가 푸시되면, 이를 트리거로 GitHub Actions 워크플로우가 시작
프로젝트 빌드: GitHub Actions 워크플로우는 프로젝트를 빌드하고, 결과물인 jar 파일을 .zip으로 압축
S3 업로드: 압축된 .zip 파일을 Amazon S3 버킷에 업로드
CodeDeploy에 배포 요청: GitHub Actions 워크플로우는 AWS CodeDeploy 서비스에 배포를 요청
CodeDeploy Agent에 배포 명령 전달: AWS CodeDeploy 서비스는 배포할 EC2 인스턴스에 설치된 CodeDeploy Agent에게 배포 명령을 전달
Agent가 appspec.yml
참고: CodeDeploy Agent는 EC2 인스턴스에서 배포 명령을 받으면, appspec.yml
파일을 참고하여 배포 프로세스를 진행
배포 실행: CodeDeploy Agent는 appspec.yml
에 정의된 지시사항에 따라 S3에서 jar 파일을 가져오고 배포 스크립트를 따라 진행
S3 버킷 생성: 배포할 jar 파일을 저장하기 위해 새로운 S3 버킷을 생성
퍼블릭 엑세스 차단 설정: 생성한 S3 버킷의 설정에서 '모든 퍼블릭 엑세스 차단' 옵션 선택
객체 소유권 설정에서 'ACL(액세스 제어 목록) 비활성화'를 선택
GitHub Actions가 AWS S3 버킷에 접근할 수 있도록 하기 위해 AWS에서 IAM 사용자를 생성하고 해당 사용자에게 S3 접근 권한을 부여한 뒤, 생성된 자격 증명(액세스 키 ID와 비밀 액세스 키)을 GitHub 리포지토리의 Secrets에 추가
IAM 사용자에게 AWSCodeDeployFullAccess와 AWSCodeDeployRole 권한을 추가
CodeDeploy 역할
EC2용 역할
sudo apt install openjdk-17-jdk
# 시스템 패키지 목록을 업데이트
$ sudo apt update
# AWS CodeDeploy 에이전트는 Ruby 기반
$ sudo apt install ruby-full
# 파일 다운로드를 위해 wget 패키지를 설치
$ sudo apt install wget
# 홈 디렉토리로 이동
$ cd /home/ubuntu
# AWS CodeDeploy 에이전트 설치 스크립트를 다운로드
$ wget https://aws-codedeploy-ap-northeast-2.s3.ap-northeast-2.amazonaws.com/latest/install
# 다운로드한 설치 스크립트에 실행 권한을 부여
$ chmod +x ./install
# 설치 스크립트를 실행하여 CodeDeploy 에이전트를 자동으로 설치
$ sudo ./install auto > /tmp/logfile
# CodeDeploy 에이전트의 상태를 확인
$ sudo service codedeploy-agent status
# Restart
$ sudo service codedeploy-agent restart
AWS Management Console에서 CodeDeploy 서비스로 이동
"배포" 섹션 아래의 "애플리케이션" 메뉴를 선택하고, "애플리케이션 생성" 버튼을 클릭
"EC2/온프레미스" 옵션을 선택
배포 그룹 생성: 앞서 만든 CodeDeploy용 역할을 추가
배포 유형"으로 "현재 위치"를 선택
환경 구성: "환경 구성"에서 "Amazon EC2 인스턴스"를 선택
인스턴스 태그 선택: 배포할 EC2 인스턴스를 식별할 수 있는 태그를 선택
name: Deploy Backend to Amazon EC2
on:
push:
branches: [ "main", "develop" ]
paths:
- 'server/**'
#리전, 버킷 이름, CodeDeploy앱 이름, CodeDeploy배포 그룹 이름
env:
AWS_REGION: ap-northeast-2
S3_BUCKET_NAME: genesis-airport-deploy
CODE_DEPLOY_APPLICATION_NAME: genesis-airport-codedeploy-app
CODE_DEPLOY_DEPLOYMENT_GROUP_NAME: genesis-airport-codedeploy-deployment-group
permissions:
contents: read
jobs:
deploy:
name: Deploy
runs-on: ubuntu-latest
environment: production
# (1)기본 체크아웃
steps:
- name: Checkout
uses: actions/checkout@v4
# (2) JDK 17세팅
- uses: actions/checkout@v4
- name: Set up JDK 17
uses: actions/setup-java@v4
with:
java-version: '17'
distribution: 'temurin'
# not executable 에러 방지를 위한 권한 부여
- name: Run chmod to make gradlew executable
run: chmod +x gradlew
working-directory: ./server
# (3) Gradle build
- name: Build with Gradle without tests
run: ./gradlew build -x test
working-directory: ./server
# (4) AWS인증 (IAM사용자, Access Key, Secret Key)
- 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: ${{ env.AWS_REGION }}
# (5)빌드 결과물을 S3버킷에 업로드
- name: Upload to AWS S3
run: |
aws deploy push \
--application-name ${{ env.CODE_DEPLOY_APPLICATION_NAME }} \
--ignore-hidden-files \
--s3-location s3://$S3_BUCKET_NAME/$GITHUB_SHA.zip \
--source .
# (6) S3버킷에 있는 파일을 대상으로 CodeDeploy실행
- name: Deploy to AWS EC2 from S3
run: |
aws deploy create-deployment \
--application-name ${{ env.CODE_DEPLOY_APPLICATION_NAME }} \
--deployment-config-name CodeDeployDefault.AllAtOnce \
--deployment-group-name ${{ env.CODE_DEPLOY_DEPLOYMENT_GROUP_NAME }} \
--s3-location bucket=$S3_BUCKET_NAME,key=$GITHUB_SHA.zip,bundleType=zip
CodeDeploy에서 배포를 위해 참조할 appsepc.yml 파일 작성
루트 디렉토리에 위치
version: 0.0
os: linux
files:
- source: /server # 배포할 파일 원본 위치
destination: /home/ubuntu/app/server # 파일을 복사할 대상 위치
overwrite: yes # 대상 위치에 이미 파일이 있을 경우 덮어쓰기
permissions:
- object: / #루트('/')로 설정되어 전체 파일 시스템에 적용
pattern: "**"
owner: ubuntu # 파일의 소유자
group: ubuntu
hooks:
ApplicationStart: # 애플리케이션 시작 시 실행될 스크립트 정의
- location: server/script/deploy.sh # 실행할 스크립트의 위치
timeout: 60 # 스크립트 실행 시 제한 시간
runas: ubuntu # 스크립트를 실행할 사용자
# 환경 변수 파일을 소스로 가져오기
source /env.sh
# 프로젝트 루트 디렉토리 경로를 설정
PROJECT_ROOT="/home/ubuntu/app/server"
# 로그 파일의 경로를 설정
APP_LOG="$PROJECT_ROOT/application.log"
ERROR_LOG="$PROJECT_ROOT/error.log"
DEPLOY_LOG="$PROJECT_ROOT/deploy.log"
# 현재 시간
TIME_NOW=$(date +%c)
# build/libs 디렉토리에 있는 JAR 파일의 경로를 가져오기
BUILD_JAR="$PROJECT_ROOT/build/libs/reservation-0.0.1-SNAPSHOT.jar"
# JAR 파일의 이름만 추출
JAR_NAME=$(basename "$BUILD_JAR")
# JAR 파일 복사에 대한 로그
echo "$TIME_NOW > $JAR_NAME 파일 복사" >> "$DEPLOY_LOG"
# build/libs 디렉토리에서 JAR 파일을 프로젝트 루트로 복사
cp "$BUILD_JAR" "$PROJECT_ROOT/"
# 현재 실행중인 애플리케이션의 PID를 확인하고 로그에 기록
echo "> 현재 실행중인 애플리케이션 pid 확인" >> "$DEPLOY_LOG"
CURRENT_PID=$(pgrep -f "$JAR_NAME")
# 실행중인 애플리케이션 PID가 없으면 종료하지 않고, 있으면 종료
if [ -z "$CURRENT_PID" ]
then
echo "> 현재 구동중인 애플리케이션이 없으므로 종료하지 않습니다." >> "$DEPLOY_LOG"
else
echo "> kill -15 $CURRENT_PID" >> "$DEPLOY_LOG"
kill -15 "$CURRENT_PID"
sleep 5
fi
# 배포할 JAR 파일의 경로를 설정
DEPLOY_JAR="$PROJECT_ROOT/$JAR_NAME"
# JAR 파일 실행에 대한 로그
echo "$TIME_NOW > $DEPLOY_JAR 파일 실행" >> "$DEPLOY_LOG"
# 배포할 JAR 파일을 nohup으로 실행
nohup java -DDB_USERNAME="$DB_USERNAME" -DDB_PASSWORD="$DB_PASSWORD" -DREDIS_PASSWORD="$REDIS_PASSWORD" -jar "$DEPLOY_JAR" >> "$DEPLOY_LOG" 2>>"$ERROR_LOG" &