BOOK 2 - 스프링부트와 AWS로 혼자 구현하는 웹 서비스(8)

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

Travis CI 배포 자동화

CI & CD

  • CI (Continuous Integration) : 코드 버전 관리 시스템에 새로운 코드가 push되면 자동으로 테스트와 빌드가 수행되어 안정적인 배포 파일을 만드는 과정
    • 여러 개발자가 동시에 같은 프로젝트를 개발하면서 각자의 코드를 병합하고 빌드를 수행해야 하는 과정이 계속해서 발생하게 된다.
    • 이러한 과정을 수작업으로 진행하는 것은 비효율적이기 때문에 자동으로 코드가 병합되어 테스트 코드와 빌드를 수행하는 환경을 구축하게 되었다.
  • CD (Continuous Deployment) : 프로젝트 빌드 결과를 자동으로 운영 서버에 무중단 배포까지 진행하는 과정
    • 서비스가 여러 대의 서버에서 운영되고 있을 경우 여러 대의 서버에 수동으로 동시에 배포를 진행하기는 매우 어렵기 때문에 무중단 배포를 진행하는 환경을 구축하게 되었다.

CI의 4가지 규칙

  • 모든 소스 코드가 살아 있고(현재 실행되고) 누구든 현재의 소스에 접근할 수 있는 단일 지점을 유지할 것
  • 빌드 프로세스를 자동화해서 누구든 소스로부터 시스템을 빌드하는 단일 명령어를 사용할 수 있게 할 것
  • 테스팅을 자동화해서 단일 명령어로 언제든지 시스템에 대한 건전한 테스트 수트를 실행할 수 있게 할 것
  • 누구나 현재 실행 파일을 얻으면 지금까지 가장 완전한 실행 파일을 얻었다는 확신을 하게 할 것

Travis CI 연동

  • github에서 제공하는 무료 CI 서비스
  • https://travis-ci.org 에서 github 계정으로 로그인하여 프로젝트를 연동할 수 있다.

프로젝트 설정

  • build.gradle 파일과 같은 위치에 .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:
          - hsdeb11@gmail.com
    
    • branches
      • CI를 어느 브랜치가 푸시될 때 수행할 지 지정
      • 오직 master 브랜치에 푸시될 때만 수행하도록 지정했다.
    • cache : gradle을 통해 의존성을 받게 되면 이를 해당 디렉토리에 캐시하여 다음 배포때부터는 같은 의존성은 다시 받지 않도록 설정한다.
    • script
      • master 브랜치에 푸시되었을 때 수행하는 명령어 지정
      • 프로젝트 내부에 둔 gradlew 을 통해 clean & ` build` 를 수행한다.
    • notifications : CI 실행 완료 시 해당 메일로 알람이 가도록 설정
  • 위 변경사항을 master에 push 한 후 travis CI 저장소 페이지를 확인하면 다음과 같이 빌드가 진행중인 것을 확인할 수 있다.

    image

image

Travis CI와 AWS S3 연동

  • S3 : AWS에서 제공하는 일종의 파일 서버

    • 이미지 파일을 비롯한 정적 파일을 관리하거나 배포 파일들을 관리하는 등의 역할을 한다.
  • 다음과 같은 구조로 travis CI와 S3를 연동하여 사용하도록 한다.

    img

    • Travis CI는 build 결과인 jar 파일을 S3에 저장하고, CodeDeploy 서비스에 배포를 요청한다.
    • CodeDeploy 서비스는 S3로부터 jar 파일을 전달받아 EC2에 배포를 진행한다.
  • CodeDeploy는 저장 기능이 없기 때문에 빌드 결과를 저장할 S3를 연동하여 S3에서 jar 파일을 가져오도록 하는 것이다.

AWS Key 발급

  • 일반적으로 AWS 서비스에 외부 서비스가 접근할 수 없다.
    • 접근 가능한 권한을 가진 key를 생성해서 사용해야 한다.
  • AWS에서는 인증과 관련된 기능을 제공하는 서비스로 IAM(Identity and Access Management) 이 있다.
  • IAM을 통해 Travis CI가 AWS의 S3와 CodeDeploy에 접근할 수 있도록 해야 한다.

  • AWS 웹 콘솔에서 IAM 서비스의 사용자 > 사용자 추가 를 클릭하여 사용자를 추가한다.

    image

    image

    image

    image

  • 발급된 key를 Travis CI 설정에서 environment variables로 추가한다.

    image

    • environment variables로 등록된 변수들은 .travis.yml 파일에서 $변수명 으로 사용할 수 있다.

S3 Bucket 생성

  • S3 (Simple Storage Service)는 순수하게 파일들을 저장하고 접근 권한을 관리, 검색 등을 지원하는 파일 서버의 역할을 한다.

  • S3 > 버킷 만들기 를 클릭하여 Travis CI에서 생성된 build 파일을 저장할 저장소를 만든다.

    image

    image-20201005105942299

    • 버킷의 모든 퍼블릭 액세스는 차단한다.
      • 퍼블릭 액세스를 차단하지 않을 경우 Jar 파일을 누구나 내려받을 수 있어 코드나 설정값, 주요 키값들이 탈취될 수 있기 때문이다.

.travis.yml 설정 수정

...
before_deploy:
  - zip -r all-review *
  - mkdir -p deploy
  - mv all-review.zip deploy/all-review.zip

deploy:
  - provider: s3
    access_key_id: $AWS_ACCESS_KEY
    secret_access_key: $AWS_SECRET_KEY
    bucket: all-review-springboot-build
    region: ap-northeast-2
    skip_cleanup: true
    acl: private
    local_dir: deploy
    wait-until-deployed: true
  • before_deploy
    • deploy 명령어가 실행되기 전에 수행된다.
    • CodeDeploy는 Jar 파일은 인식하지 못하므로 jar + 기타 설정 파일 들을 모아 압축한다.
    • zip -r all-review *
      • 현재 위치의 모든 파일을 all-review 이름으로 압축한다.
      • 명령어의 마지막 위치는 프로젝트 이름이어야 한다.
    • mkdir -p deploy : deploy 라는 디렉토리를 Travis CI가 실행중인 위치에서 생성한다.
    • mv all-review.zip deploy/all-review.zip : zip 파일을 deploy 디렉토리로 이동시킨다.
  • deploy : S3로 파일 업로드 혹은 CodeDeploy로 배포 등 외부 서비스와 연동될 행위들을 선언한다.
    • local_dir: deploy
      • 앞에서 생성한 deploy 디렉토리를 지정한다.
      • 해당 위치의 파일들만 S3로 전송한다.
  • 해당 파일 변경 사항을 github에 push하면 Travis CI 빌드가 수행되어 S3 버킷에 zip파일이 업로드된 것을 확인할 수 있다.

    image

CodeDeploy 연동

EC2에 IAM Role 추가

  • EC2가 CodeDeploy를 연동 받을 수 있도록 IAM Role을 추가한다.

    사용자와 역할의 차이점이 무엇일까?

    • 사용자 : AWS 서비스 외에 사용할 수 있는 권한
      • 로컬 PC, IDC 서버 등
    • 역할 : AWS 서비스에만 할당할 수 있는 권한
      • EC2, CodeDeploy 등
  • IAM 서비스의 역할 > 역할 만들기 를 클릭하여 역할을 추가한다.

    image

    image

    image

  • 생성한 역할을 EC2 인스턴스에 IAM Role로 설정한다. EC2 서비스에서 인스턴스 설정 > IAM 역할 수정 에서 설정한다.

    image-20201005130902687

  • IAM Role 설정 후에는 재부팅을 해야 적용되므로 재부팅을 해준다.

CodeDeploy 에이전트 설치

  • EC2 인스턴스에 접속 후 다음 명령어를 실행한다.

    aws s3 cp s3://aws-codedeploy-ap-northeast-2/latest/install . --region ap-northeast-2
    
    chmod +x ./install
    sudo ./install auto
    
  • 설치가 끝났으면 Agent가 정상적으로 실행되고 있는지 상태 검사를 한다.

    sudo service codedeploy-agent status
    

    image

CodeDeploy IAM Role 생성

  • CodeDeploy에서 EC2에 접근하기 위해 IAM Role을 생성한다.

  • IAM 서비스 역할 만들기에서 AWS 서비스 > CodeDeploy 를 선택하여 생성한다.

    image-20201005131813750

CodeDeploy 생성

  • CodeDeploy 서비스의 애플리케이션 > 애플리케이션 생성 을 클릭하여 생성한다.

    image

  • 어플리케이션 생성 후 배포 그룹을 생성한다.

    image

    image

    image

Travis CI, S3, CodeDeploy 연동

  • EC2 서버에 S3에서 받은 zip 파일을 저장할 디렉토리를 생성한다.

    mkdir ~/app/step2 && mkdir ~/app/step2/zip
    
    • Travis CI의 build가 끝나면 S3에 zip 파일이 전송되고, 이 zip 파일은 home/ec2-user/app/step2/zip 디렉토리로 복사되어 압축을 풀 예정이다.
appspec.yml
  • CodeDeploy 설정 파일인 appspec.yml 파일을 추가한다.

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

    • source

      • CodeDeploy에서 전달해준 파일 중 destination 으로 이동시킬 대상을 지정한다.
      • / 로 지정하면 전체 파일을 지정하는 것이다.
    • destination

      • source 에서 지정된 파일을 받을 위치를 지정한다.
      • 이후 jar 파일을 실행하는 등은 destination 에서 옮긴 파일들로 진행된다.
    • overwrite : 기존에 파일들이 존재할 때 덮어쓸지를 결정한다.

.travis.yml
  • 기존 .travis.yml 파일에 CodeDeploy 내용을 추가한다.

    ...
    deploy:
    	...
    	- provider: codedeploy
        access_key_id: $AWS_ACCESS_KEY
        secret_access_key: $AWS_SECRET_KEY
        bucket: all-review-springboot-build
        key: all-review.zip
        bundle_type: zip
        application: all-review
        deployment_group: all-review-group
        region: ap-northeast-2
        wait-until-deployed: true
    

배포 자동화 구성

  • 연동된 Travis CI, S3, CodeDeploy를 기반으로 실제로 jar 파일을 배포하여 실행하는 것까지 구현한다.

deploy.sh 파일 추가

  • step2 환경에서 실행될 deploy.sh 를 생성한다.

    #! /bin/bash
      
    REPOSITORY=/home/ec2-user/app/step2
    PROJECT_NAME=all-review
      
    echo "> Copy Build File"
      
    cp $REPOSITORY/zip/*.jar $REPOSITORY/
      
    echo "> Check Running Application PID"
      
    CURRENT_PID=$(pgrep -fl all-review | grep jar | awk '{print $1}')
      
    echo "Running Application PID : $CURRENT_PID"
      
    if [ -z "$CURRENT_PID" ]; then
    	echo "> There's no running application."
    else
    	echo "> Kill $CURRENT_PID"
    	kill -15 $CURRENT_PID
    	sleep 5
    fi
      
    echo "> New Application Deployment"
      
    JAR_NAME=$(ls -tr $REPOSITORY/*.jar | tail -n 1)
      
    echo "> Jar Name : $JAR_NAME"
      
    chmod +x $JAR_NAME
      
    echo "> Execute $JAR_NAME"
      
    nohup java -jar \-Dspring.config.location=classpath:/application.properties,/home/ec2-user/app/application-oauth.properties,/home/ec2-user/app/application-prod-db.properties \-Dspring.profiles.active=production \ $JAR_NAME > $REPOSITORY/nohup.out 2>&1 &
    
    • $JAR_NAME > $REPOSITORY/nohup.out 2>&1 &
      • nohup 실행시 CodeDeploy는 무한 대기한다.
      • 이 이슈를 해결하기 위해 nohup.out 파일을 표준 입출력용으로 별도로 사용한다.

.travis.yml 파일 수정

  • 실제 배포에 필요한 파일들만 zip 하기 위해서 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/all-review.zip
    
    • 배포에 필요한 jar 파일들(라이브러리 파일 포함), appspec.yml , 배포 스크립트 파일들을 before-deploy 디렉토리에 복사하여 before-deploy 디렉토리 전체를 압축한다.
    • 압축한 파일은 deploy 디렉토리에 all-review.zip 이라는 이름으로 복사하여 이동시켜 놓는다.

appspec.yml 파일 추가

...
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 배포 단게에서 실행할 명령어를 지정한다.
    • ApplicationStart 라는 단계에서 deploy.shec2-user 권한으로 실행하게 한다.

해당 변경 사항을 github에 push하면 Travis CI를 통해 Build 되고, build된 파일은 압축되어 S3를 통해 CodeDeploy로 EC2에 배포되게 된다.