본문 바로가기
Clone Coding/스프링 부트와 AWS

[CodeDeploy] 배포 자동화

by 연로그 2021. 3. 16.
반응형

yeonyeon.tistory.com/72 에서 이어지는 글 입니다.

연동, 단순 빌드 및 배포 과정은 위 글에서, 배포 자동화 과정은 본 글에서 확인해주세요.

 

이제 Jar를 배포해 실행까지 해보자.


배포 자동화하기

 

1. deploy.sh 파일 생성

프로젝트에 scripts 폴더를 만든 후 그 안에 deploy.sh 파일을 생성한다.

#!/bin/bash

REPOSITORY=/home/ec2-user/app/step2
PROJECT_NAME=freelec-springboot2-webservice

echo "> Build 파일 복사"
cp $REPOSITORY/zip/*.jar $REPOSITORY/

echo "> 현재 구동중인 애플리케이션 pid 확인"
CURRENT_PID=$(pgrep -fl $PROJECT_NAME | 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|grep 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 &

코드 설명 ▼

더보기

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

  • 현재 수행 중인 스프링 부트 애플리케이션의 프로세스 ID 찾기
  • 실행 중이면 종료하기 위해 찾는다.
  • 스프링 부트 애플리케이션 이름으로 된 다른 프로그램들이 있을 수 있으니 $PROJECT_NAME으로 된 jar 프로세스를 찾은 뒤, ID를 찾는다.

 

$JAR_NAME > $REPOSITORY/nohup.out 2>&1 &

  • nohup 실행 시 CodeDeploy는 무한 대기한다.
  • 이 이슈를 해결하기 위해 nohup.out 파일을 표준 입출력용으로 별도 사용
  • 별도 사용하지 않으면 nohup.out 파일이 생기지 않고, CodeDeploy 로그에 표준 입출력이 출력
  • nohup이 끝나지 않으면 CodeDeploy도 끝나지 않기 때문에 설정.

 

step1에서 작성했던 파일과 달리 git pull을 통해 직접 빌드하던 부분을 제거하고, jar를 실행하는 단계에서 몇 가지 코드가 추가되었다.

 

2. .travis.yml 파일 수정

before_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 폴더로 이동 후 전체 압축
  - cd ../ && mkdir -p deploy #상위 디렉토리로 이동 후 deploy 폴더 생성
  - mv before-deploy/before-deploy.zip deploy/freelec-springboot2-webservice.zip #deploy로 zip 파일 

코드 설명 ▼

더보기
  • Travis CI는 S3로 특정 파일만 업로드가 불가능
  • 디렉토리 단위로만 업로드 가능하기에 before-deploy 디렉토리는 항상 생성한다.
  • before-deploy에는 zip 파일에 포함시킬 파일들 저장
  • zip -r 명령어를 통해 before-deploy 디렉토리 전체 파일을 압축

 

.traivs.yml 파일 전문 ▼

더보기
language: java
jdk:
  - openjdk8

branches:
  only:
    - master

# Travis CI 서버의 Home
cache:
  directories:
    - '$HOME/.m2/repository'
    - '$HOME/.gradle'

script: "./gradlew clean build"

before_install:
  - chmod +x gradlew

before_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 폴더로 이동 후 전체 압축
  - cd ../ && mkdir -p deploy #상위 디렉토리로 이동 후 deploy 폴더 생성
  - mv before-deploy/before-deploy.zip deploy/freelec-springboot2-webservice.zip #deploy로 zip 파일 
  
deploy:
  - provider: s3
    access_key_id: $AWS_ACCESS_KEY
    secret_access_key: $AWS_SECRET_KEY
    bucket: freelec-travis-build
    region: ap-northeast-2
    skip_cleanup: true
    acl: private
    local_dir: deploy
    wait-until-deployed: true
    
  - provider: codedeploy
    access_key_id: $AWS_ACCESS_KEY # Travis repo settings에 설정된 값
    secret_access_key: $AWS_SECRET_KEY # Travis repo settings에 설정된 값
    bucket: freelec-travis-build # S3 버킷
    key: freelec-springboot2-webservice.zip # 빌드 파일을 압축해서 전달
    bundle_type: zip
    application: freelec-springboot2-webservice # 웹 콘솔에서 등록한 CodeDeploy 어플리케이션
    deployment_group: freelec-springboot2-webservice-group # 웹 콘솔에서 등록한 CodeDeploy 배포 그룹
    region: ap-northeast-2
    wait-until-deployed: true

# CI 실행 완료시 메일로 알람
notifications:
  email:
    recipients:
      - white_la@naver.com

 

2-1. zip error: Nothing to do 에러

zip 명령어를 제대로 입력하지 않아서 발생하는 에러다

zip -r 폴더명 * 까지 제대로 입력했는지 확인하자.

 

3. appspec.yml 파일 수정

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: 100
      runas: ec2-user

코드 설명 ▼

더보기

permissions

  • CodeDeploy에서 EC2 서버로 넘겨준 파일들 모두 ec2-user 권한을 갖게 한다.

 

hooks

  • CodeDeploy 배포 단계에서 실행할 명령어 지정
  • ApplicationStart 단계에서 deploy.sh를 ec2-user 권한으로 실행
  • 스크립트 실행 100초 이상이면 실패가 된다. (무한정 기다릴 수 없도록 timeout 지정)

 

4. 배포 결과 확인

결과는 Travis CI나 CodeDeploy의 배포 그룹 배포 내역 등에서 확인 가능하다.

배포 과정을 체험하고 싶다면 build.gradle에 version을 추가하고,

version '1.0.1-SNAPSHOT'

index.mustache에서 제목에 ver2를 붙여서 변경 내역을 푸시하면 된다.

 

4-1. 빌드, 배포는 성공 했는데 변경 사항이 변경되지 않는 경우

CodeDeploy 로그를 확인해도 빌드, 배포 과정중 오류난 부분은 없다 (확인 방법은 아래에서)

 

try 1: nohup.out 파일을 보니 org.apache.catalina.LifecycleException: Protocol handler start failed 에러가 난 것을 발견할 수 있었다. (포트 충돌..)

$ netstat -ntlp | grep: 8080 #8080 포트 사용하는 PID 확인
$ kill -9 PID번호            #강제 종료

grep에 대해 (Not all processes could be identified, non-owned process info will not be shown, you would have to be root to see it all.) 에러가 뜬다면 다음 명령어로 입력해볼 것.

netstat -ntlp 2>/dev/null | grep :8080

 

try 2: 이전에 실행되던 애플리케이션이 제대로 종료되지 않아서 새로 실행할 수 없는 것 같다.

deploy.sh 파일을 보면 다음 명령어를 통해 현재 구동중인 어플리케이션을 확인한다.

CURRENT_PID=$(pgrep -fl freelec-springboot2-webservice | grep jar | awk '{print $1}')

이걸 cmd 명령어에 입력해보니 포트 번호를 찾지 못하니까 다음 명령어인 kill도 수행하지 못하는 것 같다.

try 1에서 찾아둔 8080 포트를 사용하고 있는 pid를 찾아놓고, 해당 pid를 출력할 수 있게 명령어를 찾아보았다.

deploy.sh를 내가 찾은 명령어에 맞춰 변경하고 push하니 kill 작업이 무사히 일어난다.

 

try 3: 수정한 페이지 확인

try 2를 거쳐도 페이지 수정이 안되어서 페이지 수정을 로컬에만 하고 git에 푸시를 안했나? 라는 의심까지 들어서 확인한 결과.......

어처구니 없는 실수를 발견했다. src폴더에 있는 페이지가 아닌 bin에 있는 페이지가 수정됐었다.

bin 폴더가 왜 push 되어있는건지는 둘째치고 엄청난... 삽질만 했다...

 

+ CodeDeploy 로그 확인하기

AWS가 지원하는 서비스에서는 오류 발생 시 로그 찾는 방법을 모르면 해결하기 어렵다.

배포 실패 시 어느 로그를 봐야 할지 판단하는 방법이다.

CodeDeploy에 관한 대부분의 내용은 /opt/codedeploy-agent/deployment-root에 있다.

  • CodeDeploy ID 디렉토리: 배포한 단위별로 배포 파일 존재
  • CodeDeploy 로그 파일: 작성한 echo 내용 및 배포 중 표준 입출력 내용 모두 담겨있음

 

이제 프로젝트의 배포를 자동화하는 작업까지 마쳤다.

문제가 하나 있다면, 배포 중에는 서비스를 이용할 수 없다는 것이다.

다음 장에서는 무중단 배포에 대해 배우겠다.

 


해당 게시글은 [ 스프링 부트와 AWS로 혼자 구현하는 웹 서비스 / 이동욱 ] 책을 따라한 것을 정리하기 위한 게시글입니다. 요약, 생략한 부분이 많으니 보다 자세한 설명은 책 구매를 권장합니다.

 

4-1 에러 참고: github.com/jojoldu/freelec-springboot2-webservice/issues/201

반응형