CheerUp_Cheers

스프링 부트 - (9) 코드가 푸시되면 자동배포(Travis CI 배포 자동화) 본문

서적 공부/스프링부트 - [스프링부트와 AWS로 혼자 구현하는 웹서비스]

스프링 부트 - (9) 코드가 푸시되면 자동배포(Travis CI 배포 자동화)

meorimori 2020. 4. 24. 12:56

#앞에서의 방식의 문제점

- 수동실행되는 Test

  본인의 코드가 다른 개발자 코드에 영향 없는지 전체 테스트 수행.

  현재 상태는 항상 개발자가 작업을 하고, 수동으로 전체 테스트 수행

- 수동 Build

  다른 사람이 작성한 브랜치와 본인이 작성한 브랜치가 합쳐졌을 때(Merge) 이상없는지는 빌드를 수행해야 암.

  이를 매번 개발자가 직접 실행

=> 깃허브에 푸시하면 자동으로 Test & Builde & Deploy를 진행되도록 하자!

 


9.1 CI & CD 소개

- CI(Continuous Intergration - 지속적인 통합)

  코드 버전 관리를 하는 VCS 시스팀(Git, SVN)에 푸시 시에

  테스트와 빌드가 자동수행 되어 안정적인 배포를 만드는 과정

- CD(Continuouse Deployment - 지속적인 배포)

  이 빌드 결과를 자동으로 운영 서버에 무중단 배포까지 진행되는 과정

 

#CI의 4가지 규칙(마틸 파울러)

  - 모든 소스코드가 살아있고, 누구든 현재의 소스에 접근할 수 있는 단일 지점을 유지할것.

  - 빌드 프로세스를 자동화해서 누구든 소스로부터 시스템을 빌드하는 단일 명령어를 사용할 수 있게 할것.

  - 테스팅을 자동화해서 단일 명령어로 언제든지 시스템에 대한 건전한 테스트 수트를 실행할 것.

  - 누구나 현재 실행파일을 얻으면 지금까지 가장 완전한 실행파일을 얻었따는 확인을 하게 할 것.

=> 가장 중요한 것은 테스팅 자동화(완전한 상태임을 보장하기 위한 테스트 코드)

 


9.2 Travis CI 연동하기

[1] Travis CI

 깃허브에서 제공하는 무료 CI서비스(오픈소스 웹 서비스 = 미설치)

 

 - https://travis-ci.org/에서 서 깃허브 계정으로 로그인을 한 뒤, 왼쪽 위에[계정명 -> Settings] 클릭. 

 - 해당 저장소를 활성화.

이걸로 Travis CI 설정 끝

 

[2] 프로젝트 설정

 프로젝트의 YML(야믈)파일로 진행.

 JSON에서 괄호를 제거한 것

 build.gradle과 같은 위치에서 .travis.yml을 생성한후 코드를 추가.

 

 #travis.yml

language: java
jdk:
  - openjdk8

branches:
  only:
    - master

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

script: "./gradlew clean build"

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

- branches

  Travis CI를 어느 브랜치가 푸시될 때 수행할지 지정함.

  현재는 오직 master 브랜치에 푸시 될 때.

 

- cache

  그레이들을 통해 의존성을 받게 되면 이를 해당 디렉토리에 캐시

  같은 의존성은 다음 배포 때부터 다시 받지 않도록 설정.

 

- script

  master 브랜치에 푸시되었을 때 수행하는 명령어.

  여기서는 프로젝트 내부에 둔 gradlew을 통해 clean & build를 수행

 

- notifications

  Travis CI 실행 완료 시, 자동으로 알람이 가도록 설정.

 

#여기 까지 한후, master 브랜치에 커밋과 푸시

  Travis CI 저장소 페이지를 확인.

  https://travis-ci.com/github/wotjd4305/com.jojodu.book/builds

 

(!) 오류가 뜸..

 권한을 주자.

 ./travis.yml 에 추가

before_install:
  - chmod +x gradlew

./gradle이 문제
추가하고 성공.


9.3 Travis CI와 AWS S3 연동하기

#S3

 AWS에서 제공하는 일종의 파일 서버.

 이미지 파일을 비롯한 정적인 파일을 관리.

 ex) 보통 이미지 업로드를 구현,

=> 필요한 이유? Jar파일을 전달하기 위해서

 

Travis CI 연동시 구조

 

 

[1] AWS Key 발급.

 일반적으로 AWS 서비스에 외부 서비스가 접근할 수 없다.

  -> 접근가능한 권한을 가진 Key를 생성해서 사용.(= IAM 서비스)

 

- CodeDeploy

  AWS의 실제 배포 서비스

 

- IAM

  AWS에서 제공하는 서비스의 접근 방식과 권환을 관리.

  IAM을 통해 Travis CI가 AWS의 S3와 CodeDeploy에 접근 할수 있도록 해볼것.

  1) AWS 웹콘솔 > IAM 검색 및 이동 > IAM페이지 왼쪽 사이드바[사용자 -> 사용자 추가]

  다음 입력.

 

 2) 권환 설정 방식 > 기존 정책 직접 연결

 

 3) 정책검색

  S3FULLAccess 권한 추가

  CodeDeploy 권한 추가

 

 4) 태그등록

 

  5)Tracvis CI 생성.

 엑세스키 생성.

 비밀 엑세스키 생성.

 

[2] Travis CI 키 등록

 - TravisCI의 설정 화면으로 이동

  https://travis-ci.com/github/wotjd4305

 

 - Setting > Environmet Variables 입력

여기서 등록된 값들은 $AWS_ACCESS_KEY, $AWS_SECRET_KEY로 사용.

이제는 이 키를 관리할 S3 버킷 생성.

 

[3] S3 버킷 생성

S3는 AWS의 일종의 파일 서버.

- S3 검색 후, 버킷 만들기.

- 버킷 버전 관리(걍 넘어가)

- 버킷의 보안과 권한 설정

  모든 차단.

  jar파일이 퍼블릭일 경우, 누구나 내려받아 코드의 설정값과 주요키값들이 탈취.

  퍼블릭이 아니더라도 우리는 IAM 사용자로 발급받은 키를 사용 -> 접근가능

 

[4]travis.yml 추가

.travis.yml에 코드를 더 추가.

before_deploy:
  - mkdir -p deploy # zip에 포함시킬 파일들을 담을 디렉토리 생성
  - zip -r freelec-springboot2-webservice *
  - mv freelec-springboot2-webservice.zip deploy/freelec-springboot2-webservice.zip # deploy로 zip파일 이동


deploy:
  - provider: s3
    access_key_id: $AWS_ACCESS_KEY # Travis repo settings에 설정된 값
    secret_access_key: $AWS_SECRET_KEY # Travis repo settings에 설정된 값
    bucket: freelect-springboot-build # S3 버킷
    region: ap-northeast-2
    skip_cleanup: true
    acl: private # zip 파일 접근을 private으로
    local_dir: deploy # before_deploy에서 생성한 디렉토리
    wait-until-deployed: true

- before_deploy

  deploy 명령어가 실행되기 전에 수행.

  codeDeploy는 jar파일은 인식 못함, JAR+기타 설정 파일들을 모아 압축.

- zip -r freelec-springboot2-webservice

 현재 위치의 모든 파일을 freelec-springboot2-webservice 이름으로 압축.

 명령어의 마지막 위치는 본인의 프로젝트 이름.

- mv~

  freelec-springboot2-webservice.zip파일을 deploy/~으로 이동.

- deploy

 S3로 파일 업로드 혹은 CodeDeploy로 배포 등 외부서비스와 연동될 행위들을 선언

- local_dir:deploy

  앞에서 생성한 디플로이 디렉토리를 지정.

  해당 위치의 파일들만 S3로 전송

 

[5] 푸시

(!) 오류

uploading "freelec-springboot2-webservice.zip" with {:content_type=>"application/zip", :acl=>"private"}

504Oops, It looks like you tried to write to a bucket that isn't yours or doesn't exist yet. Please create the bucket before trying to write to it.

->라는 오류

 bucket: freelec-springboot-build ->  bucket: freelect-springboot-build

 내가 만든 S3버켓은 철자가 달랏다..

성공화면

 


9.4 Travis CI와 AWS S3, CodeDeploy 연동하기

 

[1]EC2에 CodeDeploy 연동

EC2가 CodeDeploy 연동 받을 수 있게 IAM 역할 생성.

 

- 역할

  AWS 서비스에만 할당할 수 있는 권한.

  EC2, CodeDeploy. SQS등

- 사용자

 AWS 서비스 외에 사용할 수 있는 권한

 로컬PC, IDC 서버등

=> EC2에서 권한을 사용하기 떄문에, 역할로 처리

 

#AWS서비스 > EC2 > 다음.

다음을 선택, 태그네임 설정

정책만들기
태그 추가
마지막 검토

 

#역할을 EC2 서비스에 등록하기.

- EC2 인스턴스 목록 > 인스턴스 설정(인스턴스 오른쪽 클릭) > IAM 역할 연결/바꾸기

- 적용하기.

- EC2 재부팅!

 

[2] CodeDeploy 에이전트 설치

EC2 접속해서 다음 명령어 입력

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

 

chmod +x ./install로 실행권한을 추가

install파일로 설치 진행

chmod +x ./install
sudo ./install auto

 

Agent가 정상적으로 실행되는지 상태 검사.

 sudo service codedeploy-agent status

이 메시지면 된것

 

[3] CodeDeploy를 위한 권한 생성

CodeDeploy에서 EC2에 접근하려면 권한 필요.

AWS서비스이니 IAM역할 생성.

 

#IAM 역할 생성

IAM > AWS 서비스 > CodeDeploy

아까 처럼 역할 생성.

 

[4] CodeDeploy 생성

 

#배포의 삼형제..

- Git Hub

 커밋용 서비스.

- Travis CI

 빌드용 서비스

- CodeDeploy

 배포용 서비스.

 

#CodeDeploy 만들기

- CodeDeply서비스 > 시작 > 애플리케이션 생성.

입력.

입력 후 ,생성

# 배포 그룹 생성.

- 배포 유형

 현재 위치 : 한대의 EC2(우리는 한대의 EC2를 사용.)

 블루/그린 : 2대이상의 EC2

 

- 배포 설정

 배포 구성 : 한번 배포할 때 몇 대의 서버에 배포할지를 결정.

    2대이상이라면 1대씩 배포할 지, 30% 혹은 50%로 나눠서 배포할지

    CodeDeployDefault.AllAtoOnce는 한번에 다 배포 한다는 의미.

 

[5] Travis CI, S3, CodeDeploy 연동

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

 이 디렉토리는 Travis CI가 빌드를 끝내고 만든 zip파일을 풀 장소.

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

 

# Travis CI 설정

.travis.yml로 진행.

코드 추가.

  - provider: codedeploy
    access_key_id: $AWS_ACCESS_KEY # Travis repo settings에 설정된 값
    secret_access_key: $AWS_SECRET_KEY # Travis repo settings에 설정된 값
    bucket: freelect-springboot-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

 

# AWS CodeDeploy 설정

appspec.yml로 진행

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

- version

 CodeDeploy 버전을 이야기.

 프로젝트 버전이 아님으로 다른버전을 사용하면 오류.

- source

 CodeDeploy에서 전달 해 준 파일 중 destination으로 이동시킬 대상을 지정.

 루트경로(/)를 사용시 전체 파일 이야기.

 

#Commit 후, EC2에 파일 확인.

cd ~/app/step3/zip
ll

성공

 


9.5 배포 자동화 구성

이번에는 Jar를 배포하여 실행까지 해보자!

 

[1] deploy.sh 파일 추가

step2 환경에서 실행될 deploy.sh 생성.

script 디렉토리를 생성 후, 스크립트 생성.

#!/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 freelec-springboot2-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 찾기.

 실행 중이면, 종료

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

  pgrep : freelec~이라는 이름의 프로그램 찾고

  awk : 프로세스를 찾은 뒤, 아이디를 찾는다

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

 nohup 실행 시, CodeDeploy는 무한대기 -> nohup 파일을 표준 입출력 용도로 사용

 이렇게 안하면, nohup파일도 생기지 않고, CodeDeploy로그에 표준 입출력이 출력.

 nohup이 끝나기전에 CodeDeploy도 끝나지 않으니 이렇게 해야함.

 

=>직접 빌드 했던 부분을 제거하고, Jar를 실행하는 단계에서 몇가지 코드가 추가.

 

[2].travis.yml 파일 수정

zip파일을 만드는데 필요한 것은 Jar, appsepc.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로 특정파일만 업로드가 안됨.

 디렉토리 단위로만 업로드할 수 있기 떄문에 deploy 디렉토리를 항상 생성.

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

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

 

[3]appspec.yml 파일 수정

CodeDeploy의 명령을 담당할 appsepc.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: 60
      runas: ec2-user

- permissions

 CodeDeploy에서 EC2 서버로 넘겾둔 파일들을 모두 ec2-user 권한을 갖도록 함.

- hooks

 CodeDeploy 배포 단계에서 실행할 명령어를 지정.

 Application 실행된 단계에서 deploy.sh를 ec2-user 권한으로 실행하게 합니다.

 

[4] 실제 배포 과정 체험

 

#build.gradle 프로젝트 버전 변경

version '1.0.1-SNAPSHOT'

 

#index.nbs내용 변경

 


9.6 CodeDeploy 로그 확인

AWS 서비스에서 제공하는 오류는 확인이 어려움.

CodeDeploy의 대부분 로그는 /opt/codedeploy-agent/deployment-root에 있음.

해당 디렉토리로 이동 (cd/opt/codedeploy-agent/deployment-root)

cd/opt/codedeploy-agent/deployment-root
ll

 

- ef3fe~ef3fe2a1-4b3d-4ad9-9546-5fc4c2450a5f

 CodeDeploy ID임.

 해당 디렉토리에는 배포한 단위별로 배포 파일들이 있음.

 본인의 배포파일이 정상적으로 왔는지 확인.

들어온 파일

- deployment-logs

 CodeDeploy 로그 파일.

 CodeDeploy로 이루어지는 배포 내용 중 표준 입/출력은 모두 여기 담김

 작성한 echo도 표기.

로그들..

 

=> Master 브랜치에 푸시만 하면 자동으로 EC2에 배포되는 작업이 완료