본문 바로가기
스프링부트와 AWS로 혼자 구현하는 웹서비스

Travis CI 배포 자동화 - Travis CI와 AWS S3, CodeDeploy 연동

by 초이사 2023. 4. 13.

이 글은 '스프링부트와 AWS로 혼자 구현하는 웹 서비스 - 이동욱(jojoldu)'을 공부하며 작성한 글로 생략된 내용은 책을 구매해서 확인하는 것을 권장합니다.

참고 소스코드 깃허브 https://github.com/jojoldu/freelec-springboot2-webservice

http://www.yes24.com/Product/Goods/83849117

 

스프링 부트와 AWS로 혼자 구현하는 웹 서비스 - YES24

가장 빠르고 쉽게 웹 서비스의 모든 과정을 경험한다. 경험이 실력이 되는 순간!이 책은 제목 그대로 스프링 부트와 AWS로 웹 서비스를 구현한다. JPA와 JUnit 테스트, 그레이들, 머스테치, 스프링

www.yes24.com

 

이 책의 9장에 해당하는 Travis CI 배포 자동화 과정 중 Travis CI와 AWS S3, CodeDeploy 연동 과정을 정리한 글이다.

 

 

Travis CI와 AWS S3, CodeDeploy 연동

AWS의 배포 시스템인 CodeDeploy 이용하기 전 배포 대상인 EC2가 CodeDeploy를 연동받을 수 있도록 하기 위해 IAM 역할을 생성해줘야 한다.

 

EC2에 IAM 역할 추가

1. IAM 검색 -> 왼쪽에서 역할 -> 오른쪽 상단에 역할 만들기

IAM 사용자와 역할 차이

사용자: AWS 서비스 외에 사용할 수 있는 권한 ex) 로컬 PC, IDC 서버 등

역할: AWS 서비스에서만 할당할 수 있는 권한 ex) EC2, CodeDeploy, SQS 등

-> 지금만드는 역할은 EC2 서비스에서 사용할 것이기 때문에 역할로 생성

 

2. AWS 서비스 선택 -> EC2 선택 -> 다음

신뢰할 수 있는 엔티티 선택

3. AmazonEC2RoleforAWSCodeDeploy 선택 -> 다음

권한 추가

4. 역할 이름 등록 및 태그 추가(선택사항) -> 역할 생성

이름 작성
태그 추가

6. EC2 -> 해당 인스턴스 ID 오른쪽 마우스 클릭 -> 보안 -> IAM 역할 수정 -> 생성한 역할 선택 -> 인스턴스 재부팅

 

 

 

 

Codedeploy 에이전트 설치

Codedeploy의 요청을 받을 수 있도록 에이전트를 설치한다.

1. EC2 접속

아래 명령어 실행

리전은 꼭 맞춰주도록 하자!

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

내려받기 성공

2. install 파일에 실행권한 추가 및 install 파일로 설치

# 실행권한 추가
chmod +x ./install
# 설치
sudo ./install auto

설치를 시도하니

위처럼 /usr/bin/env: No such file or directory라고 뜨면 ruby가 설치가 안된 상태라 발생하는 문제다.

#루비 설치
sudo yum install ruby

루비 설치후 다시 install파일로 설치 진행

설치가 완료되면 Agent가 정상적으로 실행되는 지 상태 검사

sudo service codedeploy-agent status

Agent 정상적으로 실행되고 있는 상태

 

 

 

 

Codedeploy를 위한 권한 생성

Codedeploy에서 EC2 접근하기 위한 권한 필요!

이것도 AWS 서비스이므로 IAM 역할 생성

1. IAM 검색 -> 왼쪽에서 역할 -> 오른쪽 상단에 역할 만들기

2. AWS 서비스 -> CodeDeploy

신뢰할 수 있는 엔티티 유형

3. 권한 추가는 권한이 하나뿐이므로 바로 다음으로

4. 역할 이름 등록 및 태그 추가(선택사항) -> 역할 생성

역할 이름 작성
태그 등록

 

 

 

 

Codedeploy 생성

지금 프로젝트에서 Code Commit의 역할은 깃허브, Code Build는 Travis CI가 하고 있기에 CodeDeploy 서비스 사용만 추가적으로 필요

1. Codedeploy -> 왼쪽에 시작하기 -> 애플리케이션 생성 -> 애플리케이션 이름 및 플랫폼 선택 -> 생성

애플리케이션 생성

2. 배포그룹생성 -> 배포그룹이름 및 서비스 역할 선택 -> 배포유형 현재위치 

배포그룹 생성

  • 서비스역할: 이전에 생성한 CodeDeploy IAM 역할 선택
  • 배포유형: 현재위치, 만약 배포할 서비스가 두대이상이라면 블루/그린 선택

3. 환경 구성 Amazon EC2 인스턴스 선택 -> 태그 추가(선택사항)

환경 구성

4. 배포 구성 CodeDeployDefault.AllAtOnce -> 로드밸런싱 체크 해제 -> 배포그룹생성

배포설정 및 로드밸런서

CodeDeploy 설정 완료!

 

 

 

 

Travis CI, S3, CodeDeploy 연동

지금 프로젝트에서 Code Commit의 역할은 깃허브, Code Build는 Travis CI가 하고 있기에 CodeDeploy 서비스 사용만 추가적으로 필요

1. S3에서 넘겨줄 zip 파일을 저장할 디렉토리 생성

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

디렉토리 생성

2. Travis CI, S3, CodeDeploy 연동위한 설정 작성

Travis CI의 Build가 끝나면 S3 zip 파일이 전송되고 전송된 zip파일이 home/ec2-user/app/step2/zip로 복사되어 압축이 풀리도록 한다.

2-1. appspec.yml를 생성해 AWS CodeDeploy 설정 작성

파일 생성 위치는 .travis.yml과 동일하다.

version: 0.0
os: linux
files:
  - source:  /
    destination: /home/ec2-user/app/step2/zip/
    overwrite: yes
  • version: 0.0 
    • CodeDeploy 버전을 의미, 프로젝트 버전이 아니기에 0.0 외에 다른 버전을 사용하면 오류 발샐
  • source: CodeDeploy에서 전달해준 파일 중 destination으로 이동시킬 대상을 지정한다. 위처럼 루트 경로(/)는 전체파일을 의미한다.
  • destination:  source에서 지정한 파일을 받을 위치, Jar를 실행하는 것 등은 destination에서 옮긴 파일들로 진행
  • overtime: 기존에 파일들이 있다면 덮어쓸지 결정

2-2. .travis.yml로 Travis CI 설정 작성

...
deploy:
  - provider: s3
  ...
  - provider: codedeploy
    access_key_id: $AWS_ACCESS_KEY # Travis repo settings에 설정된 값
    secret_access_key: $AWS_SECRET_KEY # Travis repo settings에 설정된 값
    bucket: springbootweb-build # S3 버킷
    key: springboot-webservice.zip # 빌드 파일을 압축해서 전달
    bundle_type: zip
    application: springboot-webservice # 웹 콘솔에서 등록한 CodeDeploy 어플리케이션
    deployment_group: springboot-webservice-group # 웹 콘솔에서 등록한 CodeDeploy 배포 그룹
    region: ap-northeast-2
    wait-until-deployed: true
    
    #처음에 아래 코드 작성안해서 배포내역이 기록안되었다.
    on:
    branch: main # main branch 가능하도록 추가
  ...

 

3. 작성한 파일 깃허브로 푸시

Travis CI 시작

Travis CI 완료

완료가 된후 CodeDeploy를 보면 배포내역이 나와야 하는데 나오지 않았다..

 

s3에 zip파일은 잘 올라와있어 codedeploy 전달에서 오류가 생긴 것으로 추정

s3에만 올라오는 이유가 뭘까보니까 이전 작업에서 travis CI 기본 설정으로 master branch만 받아서 main도 허용하도록 따로 코드를 작성했었는데,
그 부분이 codedeploy 사용을 위해 추가로 작성한 부분에는 적혀있지 않았다.

#처음에 아래 코드 작성안해서 배포내역이 기록안되었다.
    on:
    branch: main # main branch 가능하도록 추가

코드 추가해서 작성하니 

main branch도 허용하도록 작성
CodeDeploy 배포 내역

배포 내역에 배포 성공이 기록되었다.

 

4. 만든 폴더에 프로젝트 파일이 잘 도착했는지 확인

cd /home/ec2-user/app/step2/zip
ll

도착한 프로젝트 파일 목록

Travis CI, S3, CodeDeploy 연동완료!

 

 

 

배포 자동화 구성

Travis CI, S3, CodeDeploy 연동을 기반으로 Jar 배포 및 실행하기

1. deploy.sh 파일 추가

프로젝트의 scripts 디렉토리를 생성해 step2 환경에서 실행될 deploy.sh를 생성한다. 

deploy.sh 위치

#!/bin/bash

REPOSITORY=/home/ec2-user/app/step2
PROJECT_NAME=springboot-webservice

echo "> Build 파일 복사"

cp $REPOSITORY/zip/*.jar $REPOSITORY/

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

CURRENT_PID=$(pgrep -fl springboot-webservice | 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 &
  • CURRENT_PID: 현재 수행중인 스프링 부트 어플리케이션의 프로세스 ID 찾는다. 실행중일 경우 종료시키기 위해서
    • 스프링부트 애플리케이션 이름인 springboot-webservice로 된 다른 프로그램들이 있을 수 있어 springboot-webservice로 된 jar 프로세스를 찾은 뒤에 ID를 찾는다.
  • chmod +x $JAR_NAME: jar파일에게 nohup으로 실행할 수 있는 권한 부여
  • $JAR_NAME > $REPOSITORY/nohup.out 2>&1 &
    • nohup 실행 시 CodeDeploy는 무한대기 -> 이 이슈 해결을 위해 nohup.out 파일을 표준 입출력용으로 별도로 사용
      • 이렇게 안하면 nohup.out 파일이 생기지 않고 CodeDeploy 로그에 표준 입출력이 출력
      • nohup이 끝나기전까지 CodeDeploy도 끝나지 않기 때문에 설정 필요

2. .travis.yml 파일 수정

지금 작성한 설정은 모든 파일을 압축하고 있는데, 이 중 실제로 필요한 파일은 Jar, appspec.yml, 배포를 위한 스크립트들이다. 나머지는 zip에 포함하지 않도록 수정한다.

.travis.yml 파일의 before_deploy 수정

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/springboot-webservice.zip # deploy로 zip파일 이동

변경 전
변경 후

- Travis CI는 s3로 특정 파일만 업로드 불가능 -> 디렉토리 단위로 업로드해야하기 때문에 before-deploy 폴더 항상 생성

- before-deploy에는 zip 파일에 포함시킬 파일들을 저장

- zip -r 명령어를 통해 before-deploy 디렉토리 전체 파일 압축

 

3. appspec.yml 파일 수정

아래 코드 추가

version: 0.0
...

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

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

 

4. 작성한 파일 깃허브로 커밋, 푸시

푸시할 때, deploy.sh만 올리지 말고 scripts 폴더 채로 푸시해야한다.

Travis CI 시작

Travis.yml 성공
CodeDeploy 배포 성공

codedeploy로 배포후 EC2 도메인(퍼블릭 주소)으로 접속을 시도하니 로딩이 걸리거나 접속이 거부되었다.

codedeploy 로그를 확인하니

codedeploy 로그

pid를 제대로 못 찾는것 같다.

구글링해보니

https://github.com/jojoldu/freelec-springboot2-webservice/issues/586

 

[오류] p.358 deploy.sh 프로세스 ID 조회 · Issue #586 · jojoldu/freelec-springboot2-webservice

기존에 올라온 질문이 아닌지 먼저 검색해주세요! 가장 자주 나온 제보 P.105 @PutMapping("/api/v1/posts") P.111 Posts.update 어떤 @오류인가요? 오류설명: p.358 ~359 deploy.sh 내 프로세스 아이디(CURRENT_PID)를 조

github.com

책에서는 Amazon Linux1 AMI를 사용했는데, 현재는 Amazon Linux2 AMI만 있기 때문에 이를 선택했는데, 이 차이로 발생하는 문제일수도 있다고 한다.

책에서는 애플리케이션 프로세스를 조회할 때 jar로 검색했는데, java로 검색해야 조회가 된다! 그래서 log에서 PID가 제대로 나오지 않는 거였다..!!

scripts/deploy.sh 수정

jar -> java로 변경

scripts/deploy.sh 수정

변경하고 푸시하면 접속이 아주 잘된다.

 

 

5. 배포자동화 테스트

확인을 위해 일부 코드 수정

build.gradle에서 프로젝트 버전 변경

build.gradle

index.mustache 수정

index.mustache

 

변경 전
변경 후

변경하고 Travis CI 로그에서 메인페이지_로딩 테스트가 실패한다고 떴다.

그 테스트에서 어떤 문장이 포함되어있는지를 테스트했는데 그 문장이 배포자동화 테스트를 위해 바뀌면서 포함되지 않는 문장이 되어서 발생한 문제였다.

페이지에 있는 문장으로 바꿔주니 통과했다.

 

 

 

 

CodeDeploy 로그 확인

서버에 접속해서 아래 명령어 실행

cd /opt/codedeploy-agent/deployment-root/deployment-logs
sudo vi codedeploy-agent-deployments.log

 

 

 

현재 문제

이제 main 브랜치에 푸시만 하면 자동으로 EC2에 배포가 된다.

하지만 배포하는 동안 스프링 부트 프로젝트는 종료 상태가 되어 서비스를 이용할 수 없다.

이를 해결하기 위해 무중단 배포를 구현할 필요가 있다.

댓글