이제 자동 빌드와 배포를 위한 jar파일을 S3로 보내는 부분이 완성이 됐으니 S3에서 파일을 가져와 자동으로 배포하고 실행하는 부분을 만들기 위해서는 스크립트를 추가해야 한다.
appspec.yml (build.gradle 옆에 생성)
version: 0.0 # 버전 표시
os: linux # 적용 os
files: # 배포 파일 설정
- source: / # 인스턴스에 복사할 디렉터리 경로
destination: /home/ubuntu/app/ # 인스턴스에서 파일이 복사될 경로
overwrite: yes # 복사할 위치에 파일이 있는 경우 덮어쓰기 가능 여부
permissions: # 복사한 파일에 대한 권한 설정
- object: / # 권한이 지정되는 파일 또는 디렉터리
pattern: "**" # 매칭되는 패턴에만 권한 부여
owner: ubuntu # object의 소유자
group: ubuntu # object의 그룹 이름
hooks: # 자동으로 수행할 스크립트 지정
ApplicationStart: # 자동 실행
- location: deploy.sh # 시행할 스크립트 이름
timeout: 60 # 스크립트 실행시 시간 제한 (초과할 시 배포 실패 처리)
runas: ubuntu # 스크립트 실행자
deploy.sh (root에 만들어도 상관없으나 스크립트가 많아질 수 있기에 scripts 디렉터리를 만들고 넣어주자)
#!/usr/bin/env bash
REPOSITORY=/home/ubuntu/app # 실행 경로 설정
echo "> 현재 구동 중인 애플리케이션 pid 확인"
# 자바로 실행중인 프로세스 PID와 이름 검색 | 해당 이름이 들어간 프로세스 선택 | 해당 프로세스의 PID 선택
CURRENT_PID=$(pgrep -fla java | grep SNAPSHOT | awk '{print $1}')
echo "현재 구동 중인 애플리케이션 pid: $CURRENT_PID"
if [ -z "$CURRENT_PID" ]; then # 실행중인 PID가 없다면
echo "현재 구동 중인 애플리케이션이 없으므로 종료하지 않습니다."
else # PID가 있으면 PID를 가진 소프트웨어 강제 종료
echo "> kill -15 $CURRENT_PID"
kill -15 $CURRENT_PID
sleep 5
fi
echo "> 새 애플리케이션 배포"
JAR_NAME=$(ls -tr $REPOSITORY/*SNAPSHOT.jar | tail -n 1) # 배포한 Jar파일 이름 설정
echo "> $JAR_NAME 에 실행권한 추가"
chmod +x $JAR_NAME # Jar파일 실행 권한 추가
echo "> $JAR_NAME 실행"
# Seoul시간대로 설정하여 자동 실행
nohup java -jar -Duser.timezone=Asia/Seoul $JAR_NAME >> $REPOSITORY/nohup.out 2>&1 &
이제 해당 스크립트와 함께 Workflow에 deploy를 추가해주면 완성이 된다.
name: Spring Boot & Gradle CI/CD
# 이벤트 트리거
on:
push:
branches: [ "main" ]
pull_request:
branches: [ "main" ]
env:
S3_BUCKET_NAME: elasticbeanstalk-ap-northeast-2-526437230846
jobs:
build:
# 실행환경
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
# 자바 버전 설정 스프링 3.0 이후부터 java17이 최소 요구사항
- name: Set up JDK 17
uses: actions/setup-java@v3
with:
java-version: '17'
distribution: 'temurin'
# gradlew 실행 권한 부여
- name: Grant execute permission for gradlew
run: chmod +x gradlew
# application.properties 추가
- name: Update application.properties
run: |
echo $PROPERTIES >> src/main/resources/application.properties
env:
PROPERTIES: ${{ secrets.APPLICATION_PROPERTIES_DATA }}
# gradle build
- name: Build with Gradle
uses: gradle/gradle-build-action@bd5760595778326ba7f1441bcf7e88b49de61a25 # v2.6.0
with:
arguments: clean bootJar
# 디렉토리 생성
- name: Make Directory
run: mkdir -p deploy
# Jar 파일 복사
- name: Copy Jar
run: cp ./build/libs/*.jar ./deploy
# appspec.yml 파일 복사
- name: Copy appspec.yml
run: cp appspec.yml ./deploy
# script files 복사
- name: Copy script
run: cp ./scripts/*.sh ./deploy
# deploy 폴더 파일 zip 압축
- name: Make zip file
run: zip -r ./fishingLoad.zip ./deploy
shell: bash
# IAM 인증
- 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
# S3로 zip파일 보내기
- name: Upload to S3
run: aws s3 cp --region ap-northeast-2 ./fishingLoad.zip s3://$S3_BUCKET_NAME/
# Deploy 설정
- name: Deploy
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 fishingLoad \
--deployment-group-name fishingLoad-deployment-group \
--deployment-config-name CodeDeployDefault.AllAtOnce \
--file-exists-behavior OVERWRITE \
--s3-location bucket=$S3_BUCKET_NAME,key=fishingLoad.zip,bundleType=zip \
--region ap-northeast-2
그리고 주의할 점 코드에 이상이 없는데 오류가 발생하는 것처럼 느껴지면 한번 코드 마지막 부분에 공백이 들어가 있는지 확인을 해보자. 3시간 동안 공백을 못 찾아 코드만 계속 수정만 하다가 공백을 제거하고 성공하였다..