이 포스팅은 "스프링 부트와 AWS로 혼자 구현하는 웹 서비스" 책을 보고 정리한 내용입니다. 이번 포스팅에서는 깃허브에 코드가 푸시되면 자동으로 빌드 및 배포가 이루어지는 환경을 구축해보겠습니다. 이전 포스팅에서 구축한 EC2 서버, RDS, 깃허브를 이용합니다.
Travis CI는 웹 기반의 CI 서비스 이다. 깃허브와 연동하여 사용한다.
language: java
jdk:
- openjdk11
# master 브랜치가 푸시될 때 CI 수행
branches:
only:
- master
# 의존성을 해당 디렉토리에 캐시
cache:
directories:
- '$HOME/.m2/repository'
- '$HOME/.gradle'
# 푸시될 때 수행하는 명령어
script: "./gradlew clean build"
# CI 실행 완료시 메일 알림
notifications:
email:
recipients:
- 이메일 주소
AWS S3는 일종의 파일 서버이다. AWS S3와 Travis CI를 연동하는 이유는 Travis CI가 빌드한 결과물인 Jar파일을 S3에 저장하기 위해서이다. S3에 저장된 Jar파일을 CodeDeploy 서비스가 EC2에 배포하게된다.
일반적으로 AWS 서비스에 외부 서비스가 접근할 수 없다. 접근하기 위해서 접근 권한을 가진 Key를 발급받아 사용해야 한다. AWS에서는 IAM(Identity and Access Management)이라는 서비스를 통해 접근 권한 및 인증을 관리한다.
AWS S3는 일종의 파일 서버이다. Travis CI에서 생성된 배포 파일을 저장하도록 설정한다. AWS CodeDeploy 에서 S3에 저장된 배포 파일을 가져가도록 설정한다.
Travis CI에서 빌드된 배포 파일을 S3에 업로드 하도록 .travis.yml 파일에 설정을 추가한다.
before_deploy: # deploy 명령어가 실행되기 전에 수행
- zip -r todo-app-backend * # 현재 경로의 모든 파일을 todo-app-backend.zip 파일로 압축
- mkdir -p deploy
- mv todo-app-backend.zip deploy/todo-app-backend.zip
deploy: # 외부 서비스와 연동될 행위들을 선언
- provider: s3
access_key_id: $AWS_ACCESS_KEY # Travis CI의 Repository Environment Variable 설정 값
secret_access_key: $AWS_SECRET_KEY # Travis CI의 Repository Environment Variable 설정 값
bucket: todo-app-backend-build # S3 버킷 이름
region: ap-northeast-2
skip_cleanup: true
acl: private # zip 파일 접근 private
local_dir: deploy # before_deploy에서 생성한 디렉토리
wait-until-deployed: true
EC2가 CodeDeploy와 연동할 수 있도록 IAM 역할을 생성해야한다. 이전의 Travis CI가 AWS S3, CodeDeploy에 접근할 수 있도록 IAM에서 사용자를 추가하였다. 이번에는 IAM에서 역할을 추가해야한다.
# 아래 명령어를 입력한다.
aws s3 cp s3://aws-codedeploy-ap-northeast-2/latest/install . --region ap-northeast-2
download: s3://aws-codedeploy-ap-northeast-2/latest/install to ./install
chmod +x ./install
sudo ./install auto
sudo yum install ruby
sudo service codedeploy-agent status
The AWS CodeDeploy agent is running as PID xxx
CodeDeploy가 EC2에 연동할 수 있도록 IAM 역할을 생성해야한다. EC2와 마찬가지로 IAM 역할을 추가해야한다.
CodeDeploy를 생성해야한다. CodeDeploy는 깃허브, Travis CI, AWS S3와 연동하여 AWS EC2에 배포하는 역할을 한다.
다시한번 간단하게 정리하면 다음과 같다.
mkdir ~/app/step2 && mkdir ~/app/step2/zip
Travis CI에서 빌드가 완료되면 S3에 zip 파일이 전송되고, zip 파일이 EC2의 ~/app/step2/zip 경로에 복사되도록 설정한다. Travis CI의 설정은 .travis.yml에 AWS CodeDeploy의 설정은 appspec.yml에 작성한다.
먼저 .travis.yml과 같은 경로에 appspec.yml을 생성하고 아래 내용을 작성한다.
version: 0.0
os: linux
files:
- source: / # CodeDeploy에서 destination으로 이동시킬 대상을 지정. 루트를 지정하면 전체파일을 이동
destination: /home/ec2-user/app/step2/zip # source에서 지정한 파일을 받을 위치 지정
overwrite: yes # 덮어쓰기 설정
deploy: # 외부 서비스와 연동될 행위들을 선언
- provider: s3
access_key_id: $AWS_ACCESS_KEY # Travis CI의 Repository Environment Variable 설정 값
secret_access_key: $AWS_SECRET_KEY # Travis CI의 Repository Environment Variable 설정 값
bucket: todo-app-backend-build # S3 버킷 이름
region: ap-northeast-2
skip_cleanup: true
acl: private # zip 파일 접근 private
local_dir: deploy # before_deploy에서 생성한 디렉토리
wait-until-deployed: true
- provider: codedeploy
access_key_id: $AWS_ACCESS_KEY # Travis CI의 Repository Environment Variable 설정 값
secret_access_key: $AWS_SECRET_KEY # Travis CI의 Repository Environment Variable 설정 값
bucket: todo-app-backend-build # S3 버킷 이름
key: todo-app-backend.zip # 빌드 파일
bundle_type: zip
application: todo-app-backend # CodeDeploy 애플리케이션 이름
deployment_group: todo-app-backend-group # CodeDeploy 배포 그룹 이름
region: ap-northeast-2
wait-until-deployed: true
cd /home/ec2-user/app/step2/zip
ll
지금까지 Travis CI, S3, CodeDeploy를 연동하였다. 이제 Jar 파일 배포를 자동화 하도록 설정한다.
기존에 step1 디렉토리에서 작성한 deploy.sh를 기반으로 step2 디렉토리에서 실행될 deploy.sh를 작성해야한다. 먼저 스프링 프로젝트 루트경로에 scripts 디렉토리를 만들고 delply.sh 파일을 생성한다.
#!/bin/bash
# 변수 선언
REPOSITORY=~/app/step2
EXECUTABLE_NAME=todo
echo "> Build 파일 복사"
cp $REPOSITORY/zip/*.jar $REPOSITORY
echo "> 현재 구동중인 애플리케이션 pid 확인"
# 명령어 실행결과를 변수에 할당 $()
CURRENT_PID=$(pgrep -fl ${EXECUTABLE_NAME} | grep java | awk '{print $1}')
echo "> 현재 구동중인 애플리케이션 pid: $CURRENT_PID"
# [ -z ] 는 문자열의 길이가 0이면 참
if [ -z "$CURRENT_PID" ]; then
echo "> 현재 구동중인 애플리케이션이 없으므로 종료하지 않습니다."
else
echo "> kill -15 $CURRENT_PID"
kill -15 $CURRENT_PID
sleep 5
fi
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 실행"
# nohup은 터미널이 끊겨도 실행한 프로세스는 계속 동작하게 함. 실행 중 생기는 메시지를 nohup.out 파일을 만들어 이 파일에 출력
# 2>&1는 표준오류(stderr)를 표준출력(stdout)이 전달되는 곳으로 보냄. 따라서 nohup.out에 표준오류도 같이 씀
# 마지막 &는 명령을 백그라운드 작업으로 실행함
nohup java -jar $JAR_NAME --spring.profiles.active=dev > $REPOSITORY/nohup.out 2>&1 &
이전의 .travis.yml에서는 모든 파일을 zip파일로 만들었다. 하지만 실제로 필요한 파일은 jar, appspec.yml, deploy.sh 파일이다. 따라서 실제로 필요한 파일만 선택하여 zip파일로 만들도록 수정한다.
before_deploy: # deploy 명령어가 실행되기 전에 수행
- mkdir -p before-deploy # zip에 포함시킬 파일들을 담을 디렉토리 생성
- cp scripts/*.sh before-deploy
- cp appspec.yml before-deploy
- cp build/libs/*.jar before-deploy
- cd before-deploy && zip -r before-deploy * # before-deploy로 이동 후 모든 파일을 before-deploy.zip 파일로 압축
- cd .. && mkdir -p deploy # 상위 디렉토리로 이동 후 deploy 디렉토리 생성
- mv before-deploy/before-deploy.zip deploy/todo-app-backend.zip # deploy로 zip파일 이동
appspec.yml 파일에 아래 코드를 추가한다.
permissions:
- object: /
pattern: "**"
owner: ec2-user
group: ec2-user
hooks:
ApplicationStart:
- location: deploy.sh
timeout: 60
runas: ec2-user
지금까지 깃허브에 푸시하면 테스트, 빌드, 배포까지 자동화를 구축하였다. 다음에는 배포하는 동안 스프링 부트 프로젝트가 종료되는데 배포하는 동안에도 서비스를 유지하는 무중단 배포를 구축해본다.