전 글로 Github Action으로 자동배포를 성공했다. 하지만 문제점이 하나 존재할 때, 이 deploy.yml을 사용하면 지금 현재 모든 파일을 EC2에 올려버린다. 그러면 submodule로 만든 보안을 유지해야 하는 정보들 조차 EC2에 올라가 버린다. 이러면 안 되기 때문에 다른 방법을 찾아봤다.
목표
목표는 EC2에는 appspec.yml과 .jar파일, start.sh, stop.sh 4개만 올릴 것 이다.
CodeDeploy의 행동
- CodeDeploy는 Zip파일을 받는다. jar 파일은 받지 않는다.
- EC2에 올리기전 자동으로 zip파일을 압축 해제하고 appspec.yml을 실행한다.
- 이제 appspec.yml의 코드대로 코드가 수행된다.
흐름 이해
- github reposiotry에 push를 한다.
- githubAction에 workflow가 실행된다.
- Workflow는 서브 모듈을 확인한다.
- 빌드 한다. (jar 파일 생성)
- 위의 4개 파일을 zip 한다.
- AWS를 인증한다.
- S3에 zip 파일 올리기
- CodeDeploy에 보내기
- CodeDeploy에서 zip파일 압축풀기
- appspec.yml 실행
- 순서대로 stop.sh, start.sh 실행!!
실제 코드
이제 흐름을 이해 했으므로 직접 deploy.yml을 작성해 가면서 구현을 해봤다.
deploy.yml
name: Deploy to Amazon EC2
on:
push:
branches: [ "main" ]
env:
AWS_REGION: ap-northeast-2
S3_BUCKET_NAME: todaysgym-github-actios-s3-bucket
CODE_DEPLOY_APPLICATION_NAME: todaysgym-codedeploy-app
CODE_DEPLOY_DEPLOYMENT_GROUP_NAME: todaysgym-codedeploy-deployment-group
permissions:
contents: read
jobs:
deploy:
name: Deploy
runs-on: ubuntu-latest
environment: production
steps:
# (1) 기본 체크아웃
- name: Checkout
uses: actions/checkout@v3
# (2) JDK 11 세팅
- name: Set up JDK 11
uses: actions/setup-java@v3
with:
distribution: 'temurin'
java-version: '11'
# (3) 서브 모듈 접근하기
- name: Checkout repo
uses: actions/checkout@v3
with:
token: ${{ secrets.TOKEN }}
submodules: true
# (4) 서브 모듈 변경 점 있으면 update
- name: Git Sumbodule Update
run: |
git pull --recurse-submodules
git submodule update --remote --recursive
# (5) gradle 권한 변경
- name: Run chmod to make gradlew executable
run: chmod +x ./gradlew
# (6) Gradle build (Test 제외)
- name: Build with Gradle
uses: gradle/gradle-build-action@0d13054264b0bb894ded474f08ebb30921341cee
with:
arguments: clean build -x test
# (7) 원하는 파일 Zip 하기
- name: Zip build file and deploy sh
run: |
mv ./build/libs/*.jar ./
zip buildFile.zip ./appspec.yml ./*.jar ./scripts/start.sh ./scripts/stop.sh
# (8) 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 }}
# (9) 빌드 결과물을 S3 버킷에 업로드
- name: Upload to AWS S3
run: |
aws s3 cp \
--region ap-northeast-2 \
buildFile.zip s3://$S3_BUCKET_NAME/community-build/
# (10) 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=community-build/buildFile.zip,bundleType=zip
저번 글의 코드와 거의 비슷하지만 달라진 것은 zip을 하는 부분과 S3에 업로드 하는 부분이다.
s3는 cp를 사용해 복사해서 이동시켰다.
이렇게 하면 성공적으로 코드가 실행 된다.
stop.sh
#!/usr/bin/env bash
PROJECT_ROOT="/home/ubuntu/app"
JAR_FILE="$PROJECT_ROOT/todaysgym-0.0.1-SNAPSHOT.jar"
DEPLOY_LOG="$PROJECT_ROOT/deploy.log"
TIME_NOW=$(date +%c)
# 현재 구동 중인 애플리케이션 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
start.sh
#!/usr/bin/env bash
PROJECT_ROOT="/home/ubuntu/app"
JAR_FILE="$PROJECT_ROOT/todaysgym-0.0.1-SNAPSHOT.jar"
APP_LOG="$PROJECT_ROOT/application.log"
ERROR_LOG="$PROJECT_ROOT/error.log"
DEPLOY_LOG="$PROJECT_ROOT/deploy.log"
TIME_NOW=$(date +%c)
# jar 파일 실행
echo "$TIME_NOW > $JAR_FILE 파일 실행" >> $DEPLOY_LOG
nohup java -jar \
-Dspring.profiles.active=main \
$JAR_FILE > $APP_LOG 2> $ERROR_LOG &
CURRENT_PID=$(pgrep -f $JAR_FILE)
echo "$TIME_NOW > 실행된 프로세스 아이디 $CURRENT_PID 입니다." >> $DEPLOY_LOG
여기서 주목해야 할 점은 -Dspring.profiles.active=main이다 자동 배포를 할 때 무조건 main으로 머지 될 겨우에만 EC2에 자동배포된다. 그러므로 profiles를 main으로 설정해줘야 한다. 그 이유는 서브 모듈에서 2개의 yml 파일을 만들 었다. dev 브랜치에서 사용할 yml파일 한개, main 브랜치에서 사용할 yml파일 한개이다. 그러므로 EC2 인스턴스에는 main yml을 받아서 실행해줘야 한다.
트러블 슈팅
이렇게 실행을 해도 계속 application-main.yml에 접근을 하지 못했다. 분명 빌드를 하면 config 폴더에 있는 2개의 yml이 src/main/resources에 복사되어야 한다.
이 역할을 해주는 부분이
task copyGitSubmodule(type: Copy) {
from './config'
include '*.yml'
into 'src/main/resources'
}
build.gradle에 추가한 이 부분이다. 하지만 이 부분이 실행이 안되고 있었다. 구글링을 해보니 한개의 코드가 빠져 있었다.
processResources.dependsOn('copyGitSubmodule')
task copyGitSubmodule(type: Copy) {
from './config'
include '*.yml'
into 'src/main/resources'
}
위의 processResources.dependsOn('copyGitSubmodule')을 추가해줘야 한다.
processResources.dependsOn('copyGitSubmodule')란?
processResources는 빌드 프로세스 중에 실행되는 작업 중 하나이다. 이 작업은 src/main/resources
디렉토리에 있는 파일을 클래스 경로에 복사한다.
dependsOn은 Gradle에서 작업 간의 의존성을 정의하는 방법 중 하나이다.
dependsOn은 processResources작업이 copyGitSubmodule작업에 의존한다는 것을 나타낸다. 즉, processResources 작업이 실행되기 전에 copyGitSubmodule 작업이 선행되어야 다.
결국 main/src/resources/application.yml을 읽으러 가기전에 config에 있는 2개의 yml에 먼저 접근을 안해서 이런문제가 생긴것이다 ㅜㅜ
그래도 하나더 배웠다.
'토이프로젝트 > 스프링 부트로 구현한 웹' 카테고리의 다른 글
Github Actios를 사용하여 자동배포하기 (0) | 2023.03.14 |
---|---|
서브모듈을 이용해 application관리하기 (1) | 2023.03.12 |
EC2 서버에 프로젝트 배포하기 (0) | 2023.03.12 |
스프링 시큐리티와 OAuth2.0으로 로그인 기능 구현하기 - 네이버 (0) | 2023.03.09 |
세션 저장소로 데이터베이스 사용하기 (0) | 2023.03.09 |