본문 바로가기
개발/스프링

[혼자 구현하는 웹서비스] 9. 코드가 푸시되면 자동으로 배포해 보자 - Travis CI 배포 자동화 (2)

by 카펀 2022. 2. 26.

*이 글은 '스프링 부트와 AWS로 혼자 구현하는 웹 서비스' (프리렉, 이동욱 저) 를 공부하며 내용을 정리한 글입니다.

*내용을 따라가며 쓴 글이라 책과 흐름이 겹칠 수 있으나, 최대한 내용을 이해한 후 저의 글로 옮겼습니다.

*이 글은 9-1. 코드가 푸시되면 자동으로 배포해 보자 - Travis CI 배포 자동화 (1)에서 이어집니다.

 

4. Travis CI와 AWS S3, CodeDeploy 연동하기

5. 배포 자동화 구성

6. CodeDeploy 로그 확인

 

4. Travis CI와 AWS S3, CodeDeploy 연동하기

CodeDeploy (AWS의 배포 시스템)을 이용하기 전에, EC2가 CodeDeploy를 연동받을 수 있도록 설정해야 한다.

IAM 역할을 하나 생성하자.

 

AWS에서 IAM을 검색한다.

좌측의 '역할' 을 선택한다.

'역할 -> '역할 만들기' 를 선택한다.

 

IAM의 사용자와 역할은 약간의 차이가 있다.

역할은 AWS 내의 서비스에만 할당할 수 있다. EC2, CodeDeploy 등등...

사용자는 AWS 바깥에서 접근하는 권한을 의미한다. 로컬 PC, 외부 서버 등이 존재한다.

우리는 EC2에서 사용할 것이므로 아래와 같이 선택하자.

 

다음으로 정책을 입력하게 된다.

EC2RoleForA를 검색하고, AmazonEC2RoleForAWSCodeDeploy를 선택하자.

이어서, 역할 이름을 지정해 주고 마무리 하자.

이렇게 만든 역할을 EC2에 추가하자.

EC2 인스턴스 목록 -> 우클릭 -> 보안 -> IAM 역할 수정을 선택한다.

 

방금 만든 IAM을 선택해 주면 된다.

이후 EC2 인스턴스를 재부팅 해 주자.

 

다음은 EC2에 접속해서 진행한다.

EC2에 접속해서 아래 명령어를 입력한다.

 

aws s3 cp s3://aws-codedeploy-ap-northeast-2/latest/install . --region ap-northeast-2

 

내려받기가 성공한 모습이다.

 

이어서 아래 명령어를 입력해서, 먼저 실행 권한을 추가해 주고, 설치를 진행한다.

 

chmod +x ./install
sudo ./install auto

 

 

만약 '/usr/bin/env: ruby: No such file or directory' 라는 에러 메세지가 발생한다면, Ruby 언어를 설치해 주면 됩니다.

 

sudo yum install ruby

 

설치가 잘 되어 있는지 확인해 봅시다.

아래 명령어를 입력합니다.

 

sudo service codedeploy-agent status

 

잘 실행되고 있습니다.

 

이어서, CodeDeploy를 위한 권한을 생성해야 합니다. (아까는 EC2를 위한 권한!)

아까와 과정이 유사합니다. 마찬가지로 AWS 서비스이므로, IAM 역할을 선택합니다.

 

CodeDeploy는 권한이 하나 뿐이라서 아까와는 다르게 바로바로 진행할 수 있습니다.

 

생성을 완료하였다.

 

이제, CodeDeploy를 생성할 차례이다.

CodeDeploy는 AWS의 배포 시리즈 3개 중 하나이다.

  • Code Commit - GitHub의 코드 저장소와 같은 역할
  • Code Build - Travis CI와 같은 역할
  • Code Deploy - 다른 대체제가 없다.

AWS 콘솔에서 CodeDeploy를 검색하고, '애플리케이션 생성' 을 누릅니다.

 

이름은 적당히 정하고, 컴퓨팅 플랫폼은 EC2/온프레미스 를 골라 주자.

생성이 완료된다.

 

이어서 '배포 그룹 생성'을 선택하고, 아래와 같이 입력하자.

 

서비스 역할에서, 아까 생성한 IAM을 골라 주면 된다.

 

이어서 위와 같이 설정해 주면 된다.

맨 밑에 로드 밸런서는 체크 해제해 주자. 서버가 1대이기 때문에 부하를 분산할 필요가 없다.

생성을 완료해 주자.

 

이어서 Travis CI, S3, CodeDeploy를 연동해 보자.

EC2에 접속하여 아래 명령어를 입력해 디렉토리를 생성하자.

 

mkdir ~/app/step2 && mkdir ~/app/step2/zip

 

Travis CCCI의 Build가 완료되면 S3에 zip 파일이 전달되는 것 까지는 앞의 글에서 확인했다.

이 zip 파일이 방금 생성한 /app/step2/zip 디렉토리로 복사되어 압축이 풀릴 것이다.

 

Travis CI의 설정은 .travis.yml 파일을 통해 했던 것처럼, AWS CodeDeploy의 설정은 appspec.yml 파일을 생성해서 진행한다.

 

version: 0.0
os: linux
files:
  - source: /
    destination: /home/ec2-user/app/step2/zip/
    overwrite: yes

 

 

이어서 .travis.yml 파일에도 CodeDeploy 관련 내용을 추가해 준다.

 

deploy:
	...
  - provider: codedeploy
    assess_key_id: $AWS_ACCESS_KEY
    secret_access_key: $AWS_SECRET_KEY
    bucket: kchung1995-springboot-build
    key: springboot-web-practise.zip
    bundle_type: zip
    application: springboot-web-practise
    deployment_group: springboot-web-practise
    region: ap-northeast-2
    wait-until-deployed: true

이제 commit을 하면 예전처럼 Travis CI에 의해 자동으로 빌드가 진행된다.

 

성공한 후 AWS CodeDeploy를 확인해 보면 빌드가 성공한 것을 확인할 수 있다.

 

빌드 진행 중 문제가 한 번 발생하였고, 이 글을 참고하여 해결했다.

 

실제로 파일들이 잘 도착했는지 확인해 보자.

 

 

step2 디렉토리가 생성되었고, step2/zip 폴더 내에 내용이 잘 도착한 것을 확인할 수 있다.

연동이 성공적으로 완료되었다.

 

5. 배포 자동화 구성

앞서 성공적으로 연동을 마쳐서, 프로젝트 파일이 EC2 서버에 도착까지 잘 하는 것을 확인할 수 있었다.

이제 빌드까지 자동으로 구성되어 .jar 파일이 생성되도록 설정해 보자.

 

프로젝트 내에 scripts 디렉토리를 만들고, 안에 deploy.sh 파일을 만들자.

 

#!/bin/bash

REPOSITORY=/home/ec2-user/app/step2
PROJECT_NAME=springboot-web-practise

echo "> Build 파일 복사"

cp $REPOSITORY/ZIP/*.jar $REPOSITORY/

echo "> 현재 구동중인 어플리케이션 pid 확인"

CURRENT_PID=$(pgrep -fl springboot-web-practise | grep jar | awk '{print $1}')

echo "> 현재 구동중인 어플리케이션 pid: $CURRENT_PID"

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 | tail -n 1)

echo "> JAR Name: $JAR_NAME"

echo "> $JAR_NAME 에 실행권한 추가"

chmod +x $JAR_NAME

echo "> $JAR_NAME 실행"

nohup java -jar \
  -Dspring.config.location=classpath:/application.properties,classpath:/application-real.properties,/home/ec2-user/app/application-oauth.properties,/home/ec2-user/app/application-real-db.properties\
  -Dspring.profiles.active=real \
  $JAR_NAME > $REPOSITORY/nohup.out 2>&1 &

 

앞에 step1에서 작성한 deploy.sh 내용과 비슷하지만 약간 다르다.

git pull을 통해 직접 빌드하는 내용을 제거하고, jar을 실행하는 내용이 약간 수정되었다.

 

다음으로 .travis.yml 파일을 수정한다.

실제로 필요한 파일은 jar, appspec.yml, 쉘스크립트 뿐이다. 나머지는 포함하지 않고 압축하도록 하자.

 

before_deploy를 아래와 같이 수정한다.

 

before_deploy:
  - mkdir -p before-deploy
  - cp scripts/*.sh before-deploy/
  - cp appspec.yml before-deploy
  - cp build/libs/*.jar before-deploy/
  - cd before-deploy && zip -r before-deploy *
  - cd ../ && mkdir -p deploy
  - mv before-deploy/before-deploy.zip deploy/springboot-web-practise.zip

 

마지막으로 배포 관련 명령을 담당할 appspec.yml을 수정하자.

아래 내용을 추가한다.

 

version: 0.0
os: linux
files:
  - source: /
    destination: /home/ec2-user/app/step2/zip/
    overwrite: yes

permissions:
  - object: /
    pattern: "**"
    owner: ec2-user
    group: ec2-user

hooks:
  ApplicationStart:
    - location: deploy.sh
      timeout: 60
      runas: ec2-user

 

이후 GitHub에 commit 및 push한다.

Travis CI를 확인하여 빌드 및 배포가 성공적으로 완료되는지 확인해 보자.

 

배포가 잘 되었다!

 

더보기
더보기
더보기
더보기

.yml 파일은 띄어쓰가가 중요하다.

 

hooks:
  ApplicationStart:
    -location: deploy.sh
    timeout: 60
    runas: ec2-user

 

위와 같이 작성하면 배포에 실패하니 주의하자.

 

이제 실제로 배포하듯 해 보자.

 

build.gradle에서 프로젝트 버전을 1.0.1로 올리고, index.mustache에서 제목 뒤에 2.0을 붙여 본 후, commit 및 push 해 보자.

 

정상적으로 배포되어 수정된 사항이 반영된 것을 확인할 수 있다! (기쁨)

 

6. CodeDeploy 로그 확인

기존에 로컬에서 배포를 할 때는 콘솔창에 로그가 출력되었다.

step1에서는 nohup.out 파일을 통해 결과를 확인했다.

step2로 넘어오면서 CodeDeploy의 로그를 확인하고 싶은데, AWS의 서비스이기 때문에 로그를 어디서 확인할 수 있을지 확인이 어렵다.

 

해당 내용은 /opt/codedeploy-agent/deployment-root/ 디렉토리를 확인해 보면 알 수 있다.

 

로그는 deployment-logs 안에 들어 있다.

 

해당 파일을 확인해 보면 된다.

 

이렇게 배포 자동화 환경 구축을 성공했다.

커밋만 하면 간편하게 배포를 할 수 있다.

 

이제 남은 과제는 무중단 배포 구현이다.

현재는 배포를 하는 동안 잠시 서비스가 다운된다. 실제 운영 중인 서비스라면 사용자가 불편할 수밖에 없다.

이를 위한 무중단 배포를 다음 글에서 다루도록 하겠다.

댓글