Web/CI, CD

Travis CI 배포 자동화

태윤2 2021. 8. 23. 03:54

CI/CD 란?

  • 코드 버전 관리를 하는 VCS 시스템(Git, SVN 등)에 PUSH가 되면 자동으로 테스트와 빌드가 수행되어 안정적인 배포 파일은 만드는 과정을 CI(Continuous Integration - 지속적 통합)한다
  • 이 빌드 결과를 자동으로 운영 서버에 무중단 배포까지 진행되는 과정을 CD(Continuous Deployment - 지속적 배포)라고 한다

 

마틴 파울러의 CI에 대한 4가지 규칙

 

Continuous Integration (original version)

A long-form article entitled: "Continuous Integration (original version)"

martinfowler.com

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

지속적으로 통합하기 위해서는 프로젝트가 완전한 상태임을 보장하기 위한 테스팅자동화가 중요하다

 

  • TDD 강의

 

Travis CI 연동하기

  • Travis CI는 깃허브에서 제공하는 무료 CI 서비스
  • 젠킨스는 설치형이기 때문에 EC2 인스턴스 하나가 더 필요
 

Travis CI - Test and Deploy Your Code with Confidence

 

travis-ci.org

  • 로그인한 뒤 [계정명 -> Settings] 클릭
  • 깃허브 저장소 확인

프로젝트 설정(.travis.yml)

  • build.gradle과 같은 위치에서 .travis.yml 파일 생성
language: java
jdk:
  - openjdk11

branches:
  only:
    - master

# Permission Denied
before_install:
  - chmod +x gradlew

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

script: "./gradlew clean build"

# CI 실행 완료 시 메일로 알람
notifications:
  email:
    recipients:
      - 본인 메일 주소
  1. branches
    • Travis CI를 어느 브랜치가 푸시될 때 수행할지 지정
    • 현재 옵션은 오직 master 브랜치에 push될 때만 수행
  2. cache
    • 그레이들을 통해 의존성을 받게 되면 이를 해당 디렉토리에 캐시하여, 같은 의존성은 다음 배포 때 부터 다시 받지 않도록 설정
  3. script
    • master 브랜치에 푸시되었을 때 수행하는 명령어
    • 프로젝트 내부에 둔 gradlew을 통해 clean & build를 수행
  4. notifications
    • Travis CI 실행 완료 시 자동으로 알람이 가도록 설정

Travis CI와 AWS S3 연동하기

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

  • AWS key 발급

체크
체크
식별 가능한 태그 입력
권한 최종 확인

 

엑세스 키와 비밀 엑세스 키 생성됨

  • Travis CI에 키 등록

Settings 클릭

  • AWS_ACCESS_KEY: 엑세스 키 ID
  • AWS_SECRET_KEY: 비밀엑세스 키 

S3 버킷 생성

  • S3(Simple Storage Service)에 관해 설정을 진행
  • AWS의 S3서비스는 일종의 파일 서버
  • AWS에서 S3를 검색하여 이동하고 버킷을 생성
    • 엑세스 모두 차단할것

 

.travis.yml에 코드 추가

before_deploy:
  - zip -r yoon-springboot2-webservice *
  - mkdir -p deploy
  - mv yoon-springboot2-webservice.zip deploy/yoon-springboot2-webservice.zip
    
deploy: 
  - provider: s3
    access_key_id: $AWS_ACCESS_KEY # Travis repo settings 에 설정된 값
    secret_access_key: $AWS_SECRET_KEY # Travis repo settings 에 설정된 값
    bucket: yoon-springboot-build # S3 버킷
    region: ap-northeast-2
    skip_cleanup: true
    acl: private # zip 파일 접근을 private
    local_dir: deploy # before_deploy 에서 생성한 디렉토리
    wait-until-deployed: true
  1. before_deploy
    • deploy 명령어가 실행되기 전에 수행
    • CodeDeploy는 Jar 파일은 인식하지 못하므로 Jar+기타 설정 파일들을 모아 압축(zip)함
  2. zip -r yoon-springboot2-webservice
    • 현재 위치의 모든 파일을 yoon-springboot2-webservice 이름으로 압축(zip)함
    • 명령어의 마지막 위치는 본인의 프로젝트 이름이어야 함
  3. mkdir -p deploy
    • deploy라는 디렉토리를 Travis CI가 실행 중인 위치에서 생성
  4. mv yoon-springboot2-webservice.zip deploy/yoon-springboot2-webservice.zip
    • mv yoon-springboot2-webservice.zip 파일을 deploy/yoon-springboot2-webservice.zip으로 이동
  5. deploy
    • S3로 파일 업로드 혹은 CodeDeploy로 배포 등 외부 서비스와 연동될 행위들을 선언
  6. local_dir: deploy
    • 앞에서 생성한 deploy 디렉토리를 지정
    • 해당 위치의 파일들만 S3로 전송
  • 깃허브로 푸시!

 

Travis CI와 AWS S3, CodeDeploy 연동하기

  • EC2에 IAM 역할 추가하기

 

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

역할 만들기

 

EC2 -> 인스턴스 -> 우클릭 -> 보안 -> IAM 역할 수정

 

역할 저장후 재부팅!

  • 재부팅 완료 후 CodeDeploy의 요청을 받을 수 있게 에이전트 설치

CodeDeploy 에이전트 설치

  • EC2에 접속
wget https://aws-codedeploy-ap-northeast-2.s3.ap-northeast-2.amazonaws.com/latest/install
  • install 파일에 실행 권한 추가
chmod +x ./install
  • ruby: No such file or directory가 뜨면
sudo yum install ruby
  • install 파일로 설치
sudo ./install auto
  • Agent가 정상 실행 되는지 검사
sudo service codedeploy-agent status
  • The AWS CodeDeploy agent is running as PID XXXX 뜨면 정상

CodeDeploy를 위한 권한 생성

  • CodeDeploy에서 EC2에 접근하려면 권한이 필요
  • IAM 역할을 생성 서비스는 [AWS 서비스 -> CodeDeploy]를 선택

권한 정책은 AWSCodeDeployRole 한개뿐

 

 

CodeDeploy 생성

CodeDeploy는 AWS의 배포 삼형제다

  • Code Commit
    • 깃허브와 같은 코드 저장소의 역할
    • 프라이빗 기능을 지원한다는게 강점이지만, 현재 깃허브에서 무료로 프라이빗 지원을 하고 있어서 거의 사용 X
  • Code Build
    • Travis Ci와 마찬가지로 빌드용 서비스
    • 멀티 모듈을 배포해야 하는 경우 사용해 볼만, 규모가 있는 서비스에는 대부분 젠킨스/팀시티 등을 이용
  • CodeDeploy
    • AWS의 배포 서비스
    • 앞의 다른 서비스들은 대체재가 있지만 CodeDeploy는 대체재가 없다
    • 오토 스케일링 그룹 배포, 블루 그린 배포, 롤링 배포, EC2 단독 배포 등 많은 기능을 지원

애플리케이션 생성
애플리케이션 이름/ 컴퓨팅 선택
배포 그룹 생성

 

CodeDeploy 설정이 끝났다면 TravisCi와 CodeDeploy를 연동 하자

 

Travis CI, S3, CodeDeploy 연동

  • EC2 서버에서 디렉토리 생성
mkdir ~/app/step2 && mkdir ~/app/step2/zip
  • AWS CodeDeploy 설정은 appspec.yml로 진행
version: 0.0
os: linux
files:
  - source: /
    destination: /home/ec2-user/app/step2/zip/
    overwrite: yes
  1. version: 0.0
    • CodeDeploy 버전을 이야기
    • 프로젝트 버전이 아니므로 0.0 외에 다른 버전을 사용하면 오류가 발생
  2. source
    • CodeDeploy에서 전달해 준 파일 중 detination으로 이동시킬 대상을 지정
    • 루트 경로(/)를지정하면 전체 파일을 이야기
  3. destination
    • source에서 지정된 파일을 받을 위치
    • 이후 Jar를 실행하는 등은 destination에서 옮긴 파일들로 진행
  4. overwrite
    • 기존에 파일들이 있으면 덮어쓸지를 결정
    • 현재 yes라고 했으니 파일을 덮어씀
  • .travis.yml에도 CodeDeploy 내용을 추가

배포 자동화 구성

  • step2 환경에서 실행될 deploy.sh를 생성
#!/bin/bash

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

echo "> Build 파일 복사"

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

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

CURRENT_PID=$(pgrep -fl $PROJECT_NAME | grep java | 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.yml,classpath:/application-real.yml,/home/ec2-user/app/application-oauth.yml,/home/ec2-user/app/application-real-db.yml \
    -Dspring.profiles.active=real \
    $JAR_NAME > $REPOSITORY/nohup.out 2>&1 &
  1. CURRENT_PID
    • 현재 수행 중인 스프링 부트 애플리케이션의 프로세스 ID를 찾음
    • 실행 중이면 종료하기 위해서
  2. chmod +x $JAR_NAME
    • Jar 파일은 실행 권한이 없는 상태
    • nohup으로 실행할 수 있게 실행 권한을 부여
  3. $JAR_NAME > $REPOSITORY/nohup.out 2>&1 &
    1. nohup 실행 시 CodeDeploy는 무한 대기
    2. 이 이슈를 해결하기 위해 nohup.out 파일을 표준 입출력용으로 별도로 사용
    3. 이렇게 하지 않으면 nohup.ou 파일이 생기지 않고, CodeDeploy 로그 표준 입출력이 출력
    4. nohup이 끝나기 전까지 CodeDeploy도 끝나지 않으니 꼭 이렇게 해야함

 

 

.travis.yml 파일 수정

  • zip 파일로 만드는데 실제로 필요한 파일은 Jar, appspec.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/yoon-springboot2-webservice.zip # deploy 로 zip 파일 이동
  • Travis CI는 S3로 특정 파일만 업로드가 안됨
    • 디렉토리 단위로만 업로드 할 수 있기 때문에 before-deploy 디렉토리는 항상 생성
  • before-deploy에는 zip파일에 포함시킬 파일들을 저장
  • zip -r 명령어를 통해 before-deploy 디렉토리 전체 파일을 압축

appsepc.yml 파일 수정

permission:
  - object: /
    pattern: "**"
    owner: ec2-user
    group: ec2-user
    
hooks:
  ApplicationStart:
    - location: deploy.sh
      timeou: 60
      runas: ec2-user
  • permissions
    • CodeDeploy에서 EC2 서버로 넘겨준 파일들을 모두 ec2-user 권한을 갖도록 함
  • hooks
    • CodeDeploy 배포 단계에서 실행할 명령어를 지정
    • ApplicationStart라는 단계에서 deploy.sh를 ec2-user 권한으로 실행
    • timeout: 60으로 스크립트 실행 60초 이상 수행되면 실패가 됨(무한정 기다릴 수 없으니 시간 제한을 둬야만 함)

Github로 푸시하자

  • codedeploy 로그는 /opt/codedeploy-agent/deployment-root/deployment-logs/codedeploy-agent-deployments.log 에서 확인가능!

 

  • reference

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