Spring 서버를 EC2에 올리는데 Github Action, S3, CodeDeploy를 활용해서 EC2에 무중단 배포를 해볼 것이다.
appspec.yml
)을 S3에 압축해서 업로드,appspec.yml
에 따라 배포 처리-- nginx 설치
sudo yum install nginx
-- nginx 시작
sudo service nginx start
-- 엔진엑스 설정이 모여있는 폴더에 service-url.inc 파일 생성
sudo vim /etc/nginx/conf.d/service-url.inc
# service-url.inc 파일 내용
set $service_url http://127.0.0.1:8080;
-- nginx 설정 변경 = 스프링 연동
sudo vim /etc/nginx/nginx.conf
# nginx.conf 파일 내용
include /etc/nginx/conf.d/service-url.inc;
location / {
proxy_pass $service_url;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header Host $http_host;
}
-- nginx 재시작
sudo service nginx restart
ec2 IP의 80포트로 요청을 보내서 spring 서버에서 응답이 오는지 확인하고 정상적인 응답이 오면 성공
sudo yum update
sudo yum install ruby -y
sudo yum install wget -y
# 홈 디렉터리 이동
cd /home/ec2-user
wget https://aws-codedeploy-ap-northeast-2.s3.ap-northeast-2.amazonaws.com/latest/install
sudo ./install auto > /tmp/logfile
# 설치 확인
sudo systemctl status codedeploy-agent
만약 CodeDeploy로 배포 중 오류가 발생했다면
EC2에서 /opt/codedeploy-agent/deployment-root/deployment-logs
이 경로에 있는 로그들을 확인해보길 바란다.
CodeDepoly와 Github-Action 생성 밑 설정부분은 다음 링크를 참고하자.
링크한 글은 무중단 배포가 아닌 배포 자동화를 다루기 때문에 appspec.yml이나 workflow, 스크립트 들은 다르다.
그러니 CodeDeploy, IAM, S3, Github-Action 키 설정 정도만 따라하면 된다.
velog/juhyeon1114/실전! Github actions, AWS Code deploy로 Spring boot 배포 자동화하기
Spring 프로젝트에 배포를 위한 코드들을 추가해주자.
@RestController
@RequiredArgsConstructor
public class ProfileController {
private static final List<String> realProfiles = List.of("real1", "real2");
private final Environment env;
@GetMapping("profile")
public String profile() {
List<String> profiles = Arrays.asList(env.getActiveProfiles());
return profiles.stream()
.filter(realProfiles::contains)
.findAny()
.orElseGet(() -> profiles.isEmpty() ? "default" : profiles.getFirst());
}
}
8081포트와 8082 포트로 서버를 올릴 것이다.
application-real1.yml
과 application-real2.yml
파일을 다음과 같이 생성한다.
# real1
server.port: 8081
spring.profiles.include: "prod"
# real2
server.port: 8081
spring.profiles.include: "prod"
/home/ec2-user/app
에 application-prod.yml
이 있어야 한다.
(include 부분과 파일은 프로젝트에 맞게 변경하면 된다.)
https://github.com/yeon-06/springboot-aws/tree/master/scripts
여기서 deploy.sh빼고 전부 가져오자.
프로젝트 환경에 따라서 start.sh
에 다음 항목만 수정해주면 될 것이다.
만약 실행이 됐는데 특정 스크립트들이 의도한 대로 동작하지 않는다면 배포를 시도한 EC2에서 /opt/codedeploy-agent/deployment-root/deployment-logs/codedeploy-agent-deploy.log
로그를 확인해보자.
start.sh
를 그대로 사용하면 이전 로그들이 사라진다.
나는 logs라는 디렉토리를 만들어서 이전 로그들을 보관하기로 했다.
디렉토리 구조
/home/ec2-user/app/project
-application-prod.yml
존재
/zip
: S3에 올라온 배포 파일을 전송 디렉토리/logs
: 로그 보관 디렉토리
#!/usr/bin/env bash
# 배포할 신규버전 스프링 부트 프로젝트를 stop.sh로 종료한 'profile'로 실행
# 현재 실행중이지 않은 Profile을 찾아서 실행
ABSPATH=$(readlink -f $0)
ABSDIR=$(dirname $ABSPATH) # start.sh 경로 찾기
source ${ABSDIR}/profile.sh # import 구문
REPOSITORY=/home/ec2-user/app/project
PROJECT_NAME=bookspot
echo "> Build 파일 복사"
echo "> cp $REPOSITORY/zip/*.jar $REPOSITORY/"
cp $REPOSITORY/zip/*.jar $REPOSITORY/
echo "> 새 어플리케이션 배포"
JAR_NAME=$(ls -tr $REPOSITORY/*.jar | grep -v 'plain' | tail -n 1)
echo "> JAR Name: $JAR_NAME"
echo "> $JAR_NAME 에 실행권한 추가"
chmod +x $JAR_NAME
echo "> $JAR_NAME 실행"
IDLE_PROFILE=$(find_idle_profile)
# 이전 로그 보관
CURRENT_TIME=$(date +%Y%m%d-%H%M%S)
LOG_FILENAME=deployed-at-${CURRENT_TIME}-${PROJECT_NAME}-app.log
LOG_FILE=$REPOSITORY/${LOG_FILENAME}
LOG_DIR=$REPOSITORY/logs
PREVIOUS_LOGS=$(find $REPOSITORY -maxdepth 1 -name "*-${PROJECT_NAME}-app.log")
if [ -n "$PREVIOUS_LOGS" ]; then
echo "> 기존 로그 파일을 $LOG_DIR/ 로 이동합니다:"
for file in $PREVIOUS_LOGS; do
filename=$(basename $file)
echo " - $filename"
mv "$file" "$LOG_DIR/"
done
else
echo "> 이전 로그 파일이 없습니다."
fi
echo "> $JAR_NAME 를 profile=$IDLE_PROFILE 로 실행합니다."
nohup java -jar \
-Dspring.config.location=classpath:/application-$IDLE_PROFILE.yml,/home/ec2-user/app/project/application-prod.yml \
-Dspring.profiles.active=$IDLE_PROFILE \
$JAR_NAME > ${LOG_FILE} 2>&1 &
프로젝트 루트에 다음 appspec.yml
을 추가하자.
version: 0.0
os: linux
files:
- source: /
destination: /home/ec2-user/app/project/zip
overwrite: yes
hooks:
AfterInstall:
- location: stop.sh
timeout: 60
runas: ec2-user
ApplicationStart:
- location: start.sh
timeout: 60
runas: ec2-user
ValidateService:
- location: health.sh
timeout: 60
runas: ec2-user
destination
도 프로젝트에 맞게 설정하면 된다.
다음 .gihhub/workflow/main.yml
을 추가해주자.
name: CICD BookSpot
run-name: Running
on:
push:
branches:
- main
env:
AWS_REGION: ap-northeast-2
AWS_S3_BUCKET: my-bookspot-bucket
AWS_CODE_DEPLOY_APPLICATION: bookspot-CD
AWS_CODE_DEPLOY_GROUP: bookspot-CD-group
AWS_S3_OBJECT_NAME: bookspot-deploy
jobs:
build-with-gradle:
runs-on: ubuntu-latest
steps:
- name: main 브랜치로 이동
uses: actions/checkout@v3
with:
ref: main
- name: JDK 21 설치
uses: actions/setup-java@v3
with:
java-version: '21'
distribution: 'corretto'
- name: gradlew에 실행 권한 부여
run: chmod +x ./gradlew
- name: 프로젝트 빌드
run: ./gradlew clean build -x test
- name: deploy 디렉토리 생성
run: |
mkdir deploy/
cp build/libs/*.jar deploy/
cp script/*.sh deploy/
cp appspec.yml deploy/
- name: AWS credential 설정
uses: aws-actions/configure-aws-credentials@v1
with:
aws-region: ${{ env.AWS_REGION }}
aws-access-key-id: ${{ secrets.CICD_ACCESS_KEY }}
aws-secret-access-key: ${{ secrets.CICD_SECRET_KEY }}
- name: deploy 디렉토리를 S3 업로드
run: |
aws deploy push --application-name ${{ env.AWS_CODE_DEPLOY_APPLICATION }} --ignore-hidden-files \
--s3-location s3://$AWS_S3_BUCKET/$AWS_S3_OBJECT_NAME/$GITHUB_SHA.zip \
--source deploy
- name: EC2 배포
run: |
aws deploy create-deployment --application-name ${{ env.AWS_CODE_DEPLOY_APPLICATION }} \
--deployment-config-name CodeDeployDefault.AllAtOnce --deployment-group-name ${{ env.AWS_CODE_DEPLOY_GROUP }} \
--s3-location bucket=$AWS_S3_BUCKET,key=$AWS_S3_OBJECT_NAME/$GITHUB_SHA.zip,bundleType=zip
이 설정도 프로젝트에 맞게 설정해주면 된다.
나는 main 브랜치에 푸시가 일어나면 적용되도록 설정했다.
env
부분만 설정해주면 될 것이다.
이제 브라우저로 ec2-IP:80
이나 ec2-IP
로 요청을 보내면 서버가 잘 동작하는 것을 확인할 수 있다.
kill
명령어 실행$service_url
을 사용하지 않는 포트로 변경 후 reload첫 배포에는 하나의 Spring 서버가 올라가지만, 두번째 배포부터 8081과 8082 두 포트에 모두 Spring 서버가 올라가 있는 것이 정상이다.