본문 바로가기

DevOps/AWS

[AWS] Travis CI와 AWS CodeDeploy를 사용한 자동배포 구축하기 #2

Spring Boot 기반의 서비스를 Travis CI와 AWS CodeDeploy를 사용해 자동 배포 시스템을 구축할 것입니다.

전체적인 구조는 다음과 같습니다.

 

  전달 과정
1 Travis CI AWS S3 jar 파일 전달
2 Travis CI  AWS CodeDeploy 배포 요청
3 AWS S3  AWS CodeDeploy jar 파일 전달
4 AWS CodeDeploy  AWS EC2 배포

1편 안 보신 분들은 먼저 보고 오길 추천드립니다.

스프링 부트 서비스는 이전 글을 기반으로 하고 있습니다.

 

 

목차

1. CI & CD 소개 (#1)

2. Travis CI 연동하기 (#1)

3. Travis CI와 AWS S3 연동하기 (#1)

4. Travis CI와 AWS S3, CodeDeploy 연동하기 (#2)

5. 배포 자동화 구성 (#2)

6. CodeDeploy 로그 확인 (#2)

 

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

EC2에 IAM 역할 추가하기

배포 대상인 EC2가 CodeDeploy를 연동받을 수 있게 IAM 역할을 하나 생성합니다.

S3와 마찬가지로 IAM을 검색하고, 이번에는 [역할] 탭을 클릭해서 이동합니다.

[역할 => 역할 만들기] 버튼을 차례로 클릭합니다.

 

앞에서 만들었던 IAM의 사용자와 역할의 차이

  • 역할
    • AWS 서비스에서만 할당할 수 있는 권한
    • EC2, CodeDeploy, SQS 등
  • 사용자
    • AWS 서비스 외에 사용할 수 있는 권한
    • 로컬 PC, IDC 서버등

지금 만들 권한은 EC2에서 사용할 것이기 때문에 사용자가 아닌 역할로 처리합니다.

서비스 선택에서는 [AWS 서비스=> EC2]를 차례로 선택합니다.

  • EC2에 IAM 역할 연결

연결 후에는 ec2 인스턴스를 재부팅해줍니다.

 

CodeDeploy 에이전트 설치

  1. CodeDeploy 에이전트 설치
aws s3 cp s3://aws-codedeploy-ap-northeast-2/latest/install . --region ap-northeast-2

CodeDeploy의 요청을 받을 수 있게 에이전트를 설치합니다.

ec2에 접속 후 다음 명령어를 입력합니다.

$ chmod +x ./install    #실행 권한 추가
$ sudo ./install auto   #install 파일로 설치 진행
$ sudo service codedeploy-agent status #agent 정상 실행 확인. 상태 검사

성공하면 다음과 같이 PID가 나온다.

 

/usr/bin/env: ruby: No such file or directory 에러
예상 원인: ruby 미설치
해결 방법: 아래 명령어를 통해 ruby 설치
$ sudo yum install ruby

 

CodeDeploy를 위한 권한 생성

CodeDeploy -> EC2 접근을 위해서는 마찬가지로 권한이 필요하다.

IAM 역할을 생성하자.

역할 이름이나 Name 태그 등은 위와 같이 원하는 대로 지어도 된다.

 

CodeDeploy 생성

애플리케이션 생성

 

배포 그룹 생성

서비스 역할은 4에서 생성한 CodeDeploy용 IAM 역할을 선택하면 된다.

현재 프로젝트의 경우 EC2 하나만 배포하면 되므로 배포 유형- 현재 위치, 로드 밸랜서- 비활성화 설정을 해두었습니다.

CodeDeploayDefault.AllAtOnce는 한 번에 다 배포하는 것을 의미합니다.

 

Travis CI와 AWS S3, CodeDeploy 연동

S3에서 넘겨줄 zip 파일을 저장할 디렉터리부터 생성하겠습니다.

EC2에 접속해 다음 명령어를 입력합니다.

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

 

Travis CI의 Build가 끝나면 S3에 zip 파일이 전송됩니다.

이 zip 파일은 /home/ec2-user/app/step2/zip로 복사되어 압축을 풀 예정입니다.

Travis CI의 설정은 .travis.yml을 수정합니다.

...
deploy:
  - provider: s3
    ...
  - 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: lㅣㅣ-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
...

AWS CodeDeploy의 설정은 appspec.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에 옮긴 파일들로 진행

overwrite

  • 덮어쓰기 가능 여부
  • yes: 덮어쓴다.

Travis CI가 끝나면 CodeDeploy 화면 아래에서 배포가 수행된 것을 확인할 수 있습니다.

배포가 끝났으면 다음 명령어로 파일들이 잘 도착했는지 확인해봅니다.

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

다음과 같이 프로젝트 파일이 잘 도착한 것을 확인할 수 있습니다.

 

5. 배포 자동화 구성

deploy.sh 파일 추가

#!/bin/bash

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

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도 끝나지 않기 때문에 설정.

 

.travis.xml 파일 수정

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 디렉토리 전체 파일을 압축

 

appspec.yml 수정

permissions

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

hooks

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

 

실제 배포 과정 체험

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

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

version '1.0.1-SNAPSHOT'

API 응답 값을 변경해준다.

@GetMapping("/")
public String hello(){
    return "안녕하세요~~~V2";
}

API 등 확인해보면 실제로 배포된 것을 확인할 수 있다.

 

6. CodeDeploy 로그 확인

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

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

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

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

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

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

다음 장에서는 무중단 배포에 대한 글을 작성하겠습니다.