
AWS 사양
- Front End : Route53, ACM, S3, CloudFront
- Back End : S3, EC2, AutoScaling Group, CodeDeploy(Blue-Green)
- Machin Learning : S3, EC2, CodeDeploy
1️⃣ GitHub에 코드 commit & push(main 브랜치) → GitHub Actions CI 실행
2️⃣ 빌드 결과물 (.jar, appspec.yml, scripts) 압축 후 S3에 업로드
3️⃣ CodeDeploy가 Auto Scaling Group을 이용해 Green 환경에 배포
4️⃣ Green 인스턴스 Health 체크 완료 시, Blue → Green으로 트래픽 전환
5️⃣ 배포 완료 후 Blue 인스턴스 자동 종료
🗂️ 디렉토리 구조
├─.github
│ ├─ workflows/
│ │ └─ deploy.yaml/
├─ bonbon/
│ ├─ src/
│ │ └─ main/
│ │ └─ resources/
│ ├─ build/
│ ├─ gradlew
│ ├─ build.gradle
├─ scripts/
│ └─start.sh / stop.sh / init-dir.sh (배포 스크립트)
├─ appspec.yml
appspec.yml(CD) - AWS CodeDeploy가 배포 작업 중 어떤 스크립트를 어떤 타이밍에 실행할지 정의하는 설정 파일 start.sh(CD) - 애플리케이션 실행stop.sh(CD) - 기존에 실행중이던 애플리케이션 중지 deploy.yaml(CI) - 배포 전 작업을 수행하는 사용자 정의 스크립트init-dir.sh(CD) - EC2 인스턴스 초기 배포 시 초기 디렉토리 및 권한 설정1️⃣ [ CI 단계 ]
deploy.yamlS3 업로드1️⃣ [ CD 단계 ]
appspec.ymlBeforeInstall: init-dir.sh 실행 (초기 디렉토리 준비)ApplicationStop: stop.sh 실행 (기존 프로세스 종료)ApplicationStart: start.sh 실행 (zip 다운로드, 압축 해제, .jar 실행)🗂️ 디렉토리 구조
bonbon/
├─ 5f6c33d.zip/
│ ├─ build/
│ ├─ appspec.yml
│ └─ scripts/
│ │ ├─ start.sh
│ │ ├─ stop.sh
│ │ ├─ init-dir.sh
│ │ └─ sha.txt
name: BonBon Back-end CI&CD
on:
push:
branches: main
env:
AWS_REGION: ap-northeast-2 #리전
S3_BUCKET_NAME: bonbon-back-bucket #버킷 이름
CODE_DEPLOY_APPLICATION_NAME: bonbon-dev #CodeDeploy 애플리케이션 이름
CODE_DEPLOY_DEPLOYMENT_GROUP_NAME: bonbon-dev #CodeDeploy 배포 그룹 이름
permissions:
contents: read
jobs:
build:
# job이 돌아갈 환경
runs-on: ubuntu-latest
steps:
# 기본 체크아웃
- name: Checkout
uses: actions/checkout@v3
# resources 디렉토리 생성
- name: Create resources directory (for ignored files)
run: mkdir -p ./bonbon/bonbon/src/main/resources
# properties들 생성
- name: Set application.properties
run: |
echo "${{ secrets.APPLICATION_PROPERTIES }}" > ./bonbon/bonbon/src/main/resources/application.properties
echo "${{ secrets.APPLICATION_JWT_PROPERTIES }}" > ./bonbon/bonbon/src/main/resources/application-jwt.properties
# gradlew 권한 추가
- name: Make gradlew executable
run: chmod +x ./bonbon/bonbon/gradlew
# 빌드, 테스트
- name: Build and Test
run: |
cd ./bonbon/bonbon
./gradlew clean build test --info
# GitHub 커밋의 고유한 SHA 값을 scripts/sha.txt 파일에 저장 -> 어떤 커밋 배포했는지 확인할라고
- name: Save SHA to file
run: echo $GITHUB_SHA > ./bonbon/bonbon/scripts/sha.txt
# zip 파일 압축(scripts, appspec.yml, build 포함)
- name: Make deployment zip
run: |
cd ./bonbon/bonbon
echo $GITHUB_SHA > scripts/sha.txt
zip -r $GITHUB_SHA.zip build appspec.yml scripts
mv $GITHUB_SHA.zip ../../
# JDK 23 세팅
- name: Set up JDK 23
uses: actions/setup-java@v3
with:
distribution: 'temurin'
java-version: '23'
# 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_KEY_ID }}
aws-region: ${{ env.AWS_REGION }}
- name: Upload to S3
run: |
aws s3 cp --region ap-northeast-2 ./$GITHUB_SHA.zip s3://${{ env.S3_BUCKET_NAME }}/$GITHUB_SHA.zip
# CodeDeploy로 배포 실행
- name: Deploy to EC2
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=${{ env.S3_BUCKET_NAME }},key=${{ github.sha }}.zip,bundleType=zip
#!/bin/bash
# 프로젝트 루트 디렉토리 경로
PROJECT_ROOT="/home/ec2-user/app"
# 디렉토리 확인 후 생성
if [ ! -d "$PROJECT_ROOT" ]; then
echo "$(date '+%Y-%m-%d %H:%M:%S') > $PROJECT_ROOT 디렉토리 없음. 생성 중..." >> /home/ec2-user/deploy.log
mkdir -p $PROJECT_ROOT
echo "$(date '+%Y-%m-%d %H:%M:%S') > $PROJECT_ROOT 디렉토리 생성 완료." >> /home/ec2-user/deploy.log
else
echo "$(date '+%Y-%m-%d %H:%M:%S') > $PROJECT_ROOT 디렉토리가 이미 존재합니다." >> /home/ec2-user/deploy.log
fi
# 디렉토리 권한 설정 (ec2-user에게 소유권 부여)
echo "$(date '+%Y-%m-%d %H:%M:%S') > $PROJECT_ROOT 디렉토리 권한 변경 중..." >> /home/ec2-user/deploy.log
chown -R ec2-user:ec2-user $PROJECT_ROOT
chmod -R 755 $PROJECT_ROOT
echo "$(date '+%Y-%m-%d %H:%M:%S') > $PROJECT_ROOT 디렉토리 권한 설정 완료." >> /home/ec2-user/deploy.log
start.sh에 stop.sh가 하는 일까지 포함시켰다.. #!/usr/bin/env bash
# 프로젝트 루트 디렉토리 설정
PROJECT_ROOT="/home/ec2-user/app/build/libs"
JAR_NAME="bonbon-0.0.1-SNAPSHOT.jar"
JAR_FILE="$PROJECT_ROOT/$JAR_NAME"
# 로그 파일 경로
APP_LOG="$PROJECT_ROOT/application.log"
ERROR_LOG="$PROJECT_ROOT/error.log"
DEPLOY_LOG="$PROJECT_ROOT/deploy.log"
# 현재 시간 저장
TIME_NOW=$(date '+%Y-%m-%d %H:%M:%S')
# sha.txt에서 SHA 읽기
SHA=$(cat /home/ec2-user/app/scripts/sha.txt)
# 다운로드
aws s3 cp s3://bonbon-back-bucket/${SHA}.zip /home/ec2-user/app/${SHA}.zip
# S3에서 ZIP 파일 다운로드
echo "$TIME_NOW > S3에서 ZIP 파일 다운로드 시작" >> $DEPLOY_LOG
aws s3 cp s3://bonbon-back-bucket/${SHA}.zip $PROJECT_ROOT/${SHA}.zip >> $DEPLOY_LOG 2>&1
if [ $? -ne 0 ]; then
echo "$TIME_NOW > 오류: S3에서 ZIP 파일 다운로드 실패" >> $DEPLOY_LOG
exit 1
fi
echo "$TIME_NOW > ZIP 파일 다운로드 완료" >> $DEPLOY_LOG
# ZIP 파일 압축 해제
echo "$TIME_NOW > ZIP 파일 압축 해제 시작" >> $DEPLOY_LOG
unzip -o $PROJECT_ROOT/${SHA}.zip -d $PROJECT_ROOT >> $DEPLOY_LOG 2>&1
if [ $? -ne 0 ]; then
echo "$TIME_NOW > 오류: ZIP 파일 압축 해제 실패" >> $DEPLOY_LOG
exit 1
fi
echo "$TIME_NOW > ZIP 파일 압축 해제 완료" >> $DEPLOY_LOG
# 기존 프로세스 종료 (있을 경우)
EXISTING_PID=$(pgrep -f "$JAR_FILE")
if [ -n "$EXISTING_PID" ]; then
echo "$TIME_NOW > 기존 프로세스 종료 (PID: $EXISTING_PID)" >> $DEPLOY_LOG
kill -9 $EXISTING_PID
fi
# JAR 파일 실행
echo "$TIME_NOW > $JAR_FILE 파일 실행 시작" >> $DEPLOY_LOG
nohup /opt/jdk-23/bin/java -jar $JAR_FILE > $APP_LOG 2> $ERROR_LOG &
echo "$TIME_NOW > 실행된 프로세스 아이디 $CURRENT_PID 입니다." >> $DEPLOY_LOG
#!/usr/bin/env bash
PROJECT_ROOT="/home/ec2-user/app"
JAR_FILE="$PROJECT_ROOT/spring-webapp.jar"
DEPLOY_LOG="$PROJECT_ROOT/deploy.log"
TIME_NOW=$(date '+%Y-%m-%d %H:%M:%S')
# 현재 구동 중인 애플리케이션 pid 확인
CURRENT_PID=$(pgrep -f $JAR_FILE)
# 프로세스가 켜져 있으면 종료
if [ -z $CURRENT_PID ]; then
echo "$TIME_NOW > 현재 실행중인 애플리케이션이 없습니다" >> $DEPLOY_LOG
else
echo "$TIME_NOW > 실행중인 $CURRENT_PID 애플리케이션 종료 " >> $DEPLOY_LOG
kill -15 $CURRENT_PID
fi
version: 0.0
os: linux
files:
- source: /
destination: /home/ec2-user/app
overwrite: yes
permissions:
- object: /
pattern: "**"
owner: ec2-user
group: ec2-user
hooks:
BeforeInstall:
- location: scripts/init-dir.sh
timeout: 30
runas: ec2-user
AfterInstall:
- location: scripts/stop.sh
timeout: 60
runas: ec2-user
ApplicationStart:
- location: scripts/start.sh
timeout: 60
runas: ec2-user
“bonbon project ver_1.2.0”
main branch로 PR & Merge“bonbon project ver_1.2.2”로 변경한 후 Commit & push
AutoScaling Group + Launch Template에 의해 새로운 EC2 인스턴스 2개 생성 → 대체 인스턴스에 .jar 파일 배포AutoScaling Group 을 통해 배포된 인스턴스들에게 트래픽을 분산시킴
healthy
“bonbon project ver_1.2.0” → “bonbon project ver_1.2.2”
BackEnd도 끝!
