Travis CI 배포 자동화

2023. 5. 14. 22:01Cloud

1. CI & CD 소개

  • CI(Continuous Integration, 지속적인 통합) : Git에 푸시가 되면 자동으로 테스트와 빌드를 하고 배포 파일을 만드는 과정입니다.
  • CD(Continuous Deployment, 지속적인 배포) : 배포 파일을 자동으로 운영 서버에 무중단 배포까지 진행하는 과정입니다.

2. Travis CI 연동하기

  • Travis CI : 깃허브에서 제공하는 무료 CI 서비스입니다.

Travis CI 웹 서비스 설정

  1. Travis-CI 웹사이트 입장
  2. Github 계정으로 로그인
  3. 계정 설정(Settings)으로 들어가서 CI 하고 싶은 저장소를 활성화

 

프로젝트 설정

Travis CI의 상세한 설정은 프로젝트에 존재하는 .travis.yml 파일로 할수 있습니다.

language: java
jdk:
  - openjdk11

branches:
  only:
    - main

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

script: "sudo ./gradlew clean build"


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 파일 이동

deploy:
  # S3로 파일 업로드 혹은 CodeDeploy로 배포 등 외부 서비스와 연동될 행위들을 선언합니다.
  - provider: s3
    access_key_id: $AWS_ACCESS_KEY # Travis repo settings에 설정된 값
    secret_access_key: $AWS_SECRET_KEY # Travis repo settings에 설정된 값
    bucket: {버킷 이름} # S3 버킷
    region: ap-northeast-2
    skip_cleanup: true
    acl: private # zip 파일 접근을 private로 설정
    local_dir: deploy # before_deploy에서 생성한 디렉토리
    # 해당 위치(deploy) 파일들만 S3로 전송합니다.
    wait-until-deployed: true
    on:
      all_branches: true # default 브랜치인 master 브랜치 외에도 다른 브랜치에도 배포 할수 있도록 하기

  - provider: codedeploy
    access_key_id: $AWS_ACCESS_KEY
    secret_access_key: $AWS_SECRET_KEY
    bucket: {버킷 이름} # 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
    on:
      all_branches: true

# CI 실행 완료시 메일로 알람
notifications:
  email:
    recipients:
      - {메일명}
  • branches
    • Travis CI를 어느 브랜치가 푸시될때 수행할지를 지정합니다.
  •  cache
    • 그레이들을 통해 의존성을 받게 되면 이를 해당 디렉토리에 캐시하여, 같은 의존성은 다음 배포 때부터 다시 받지 않도록 설정합니다.
  • script
    • main 브랜치에 푸시될때 수행하는 명령어입니다.
    • 프로젝트 내부에 있는 gradlew를 톻애서 clean & build를 수행합니다.

 

.travis.yml 파일을 커밋 및 푸시하고 Travis CI 저장소 페이지를 확인합니다. 빌드가 성공하면 다음과 같이 passed가 되면 성공입니다.

 

3. Travis CI와 AWS S3 연동하기

  • AWS S3 : AWS에서 제공하는 파일을 저장하는 서버, 여러 파일들을 저장할 수 있으며 배포 파일들 또한 관리할 수 있습니다.

 

S3와 Travis CI와 연동하게 되면 전체 구조는 다음과 같습니다.

  1. Travis CI가 만든 빌드 파일(Jar)을 S3 서버에 전달합니다.
  2. Travis CI는 AWS Code Deploy 서비스를 이용하여 빌드 파일을 배포 요청합니다.
  3. Code Deploy 서비스는 S3 서버에 있는 Jar 파일에 접근합니다.
  4. EC2 인스턴스에 Jar 파일을 배포합니다.
Code Deploy와 S3 서버와 연동이 필요한 이유는 Code Deploy는 저장 기능이 없기 때문에 빌드 파일을 보관할 공간이 필요하기 때문입니다.

 

AWS Key 발급

1. AWS IAM 계정 점속 -> 사용자 -> 사용자 추가-> 사용자 정보 입력

  • 사용자 이름 : freelect-travis-deploy

 

 

2. 사용자 권한 설정

사용자 추가시 권한 설정에서 기존 정책 직접 연결을 선택하여 S3와 CodeDeploy 서비스에 접근할 수 있는 권한을 추가합니다.

  • AmazonS3FullAccess : S3 서비스에 접근할 수 있는 권한
  • AWSCodeDeployFullAccess : CodeDeploy 서비스에 접근할 수 있는 권한

 

3. 태그 작성

사용자를 식별하기 위한 이름을 설정합니다.

  • Name : Name
  • Value : freelect-travis-deploy

 

4. 액세스키 생성

새로 생성한 사용자를 대상으로 보안 자격 증명 탭으로 이동하여 액세스 키를 생성합니다. 액세스키를 생성하면 csv 파일을 다운로드하여 별도로 보관합니다. 이 액세스 키는 외부에 공개되면 안되는 중요한 정보입니다.

  • 액세스 키 모범 사례 및 대안 : Command Line Interface(CLI)

 

Travis CI에 키 등록

1. Travis CI의 활성화된 저장소의 설정 화면으로 이동하여 액세스키와 시크릿키를 등록합니다.

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

위와 같이 Travis CI 저장소 설저에 액세스 키와 시크릿 키를 등록함으로써 .travis.yml 파일에서 $AWS_ACCESS_KEY, $AWS_SECRET_KEY란 이름으로 값을 사용할 수 있습니다.

 

 

S3 버킷 생성

사용자를 추가하고 S3와 Code Deploy 권한을 가진 사용자의 액세스키를 Travis CI의 저장소에 액세스 키를 등록하였습니다. 이제 액세스 키를 사용해서 Jar 파일을 저장할 S3 버킷을 생성해야 합니다.

 

1. S3 -> 버킷 만들기

  • 버킷 이름 : freelect-springboot-bucket
    • 버킷 이름은 전세계에서 고유해야 하기 때문에 위 이름은 예시이고 식별할 수 있는 이름으로 생성하면 됩니다.
  • 리전 : 아시아 태평양(서울)
  • 객체 소유권 : ACL 비활성화됨
  • 모든 퍼블릭 액세스 차단 : 차단 활성화 체크
    • Jar 파일이 퍼블릭일 경우 누구나 다운로드하여 코드, 설정값, 주요 키값들이 탈취당할 수 있습니다.

2. 버킷 생성 완료

 

.travis.yml 추가

Travis CI에서 빌드하여 만든 Jar 파일을 S3에 업로드할 수 있도록 .travis.yml에 코드를 추가합니다.

 

...

deploy:
  # S3로 파일 업로드 혹은 CodeDeploy로 배포 등 외부 서비스와 연동될 행위들을 선언합니다.
  - provider: s3
    access_key_id: $AWS_ACCESS_KEY # Travis repo settings에 설정된 값
    secret_access_key: $AWS_SECRET_KEY # Travis repo settings에 설정된 값
    bucket: {버킷 이름} # S3 버킷

...

  - provider: codedeploy
    access_key_id: $AWS_ACCESS_KEY
    secret_access_key: $AWS_SECRET_KEY
    bucket: {버킷 이름} # S3 버킷

...

위와 같이 S3와 Code Deploy 설정에 액세스 키와 시크릿키, 버킷 이름을 작성합니다. .travis.yml 파일의 전체 내용은 위쪽에 있습니다.

 

.travis.yml 파일에 설정을 작성하고 깃허브에 푸시하게 되면 Travis CI에서 빌드하여 만든 Jar 파일을 S3 서버에 업로드하게 됩니다. S3 버킷에 압축한 zip 파일이 업로드 되면 성공입니다.

 

4. Travis CI와 S3, Code Deploy 연동하기

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

 

EC2에 IAM 역할 추가하기

1. IAM -> 역할 -> 역할 만들기

  • 신뢰할 수 있는 엔티티 유형 : AWS 서비스
  • 사용 사례 : EC2
  • 정책 : AmazonEC2RoleforAWSCodeDeploy

 

2. 태그 등록 및 역할 정보 입력

  • Name : ec2-codedeploy-role
  • 역할 이름 : ec2-codedeploy-role

 

3. EC2 인스턴스에 ec2-codedeploy-role 역할 등록

인스턴스 선택 -> 작업 -> 보안 -> IAM 역할 수정을 클릭합니다.

IAM 역할에서 ec2-codedeploy-role 역할을 선택합니다. 그리고 인스턴스를 재부팅합니다.

 

 

CodeDeploy 에이전트 설치

0. 루바 언어 설치

$ sudo yum install ruby

 

1. EC2에 접속하여 CodeDeploy 에이전트 설치파일 내려받기

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

 

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

$ chmod +x ./install
$ sudo ./install auto

 

3. CodeDeploy 에이전트 설치 확인

$ sudo service codedeploy-agent status
The AWS CodeDeploy agent is running as PID xxx

 

CodeDeploy를 위한 권한 생성

CodeDeploy에서 S3에 접근하기 위해서도 권한이 필요합니다.

 

1. IAM -> 역할 -> 역할 만들기

  • 신뢰할 수 있는 엔티티 유형 : AWS 서비스
  • 사용 사례 : CodeDeploy 선택

2. 권한 추가에서 AWSCodeDeployRole 권한 그대로 선택합니다.

3. 역할 이름을 입력합니다.

  • 역할 이름 : codedeploy-role

 

CodeDeploy 생성

1. CodeDeploy 서비스 이동 -> 애플리케이션 생성

 

2. CodeDeploy 애플리케이션에 대한 배포 그룹 생성

  • 배포할 서비스가 2대 이상이면 블루/그린을 선택하고 1대라면 현재 위치를 선택합니다.

 

3. CodeDeploy 환경 선택

 

4. 배포 구성 선택 및 로드밸런싱 해제

  • 배포 구성 : 한번 배포할때 몇 대의 서버에 배포할지를 결정합니다. 1대씩 배포할지 30% 혹은 50%로 나눠서 배포할지 지 선택합니다.
  • CodeDeployDefault.AllAtOnce : 한번에 다 배포합니다.

 

Travis CI, S3, CodeDeploy 연동

S3에서 전달하는 zip 파일을 저장할 디렉토리를 생성합니다.

 

1. EC2 인스턴스에 디렉토리 생성

$ mkdir ~/app/step2 && mkdir ~/app/step2/zip
  • Travis CI의 빌드가 끝나면 S3에 zip 파일이 전송되고, zip 파일은 /home/ec20user/app/step2/zip로 복사되어 압축을 풀 예정입니다.

 

2. 스프링 부트 프로젝트 최상단에 appspec.yml 파일 작성

appspec.yml 파일은 AWS CodeDeploy 빌드 파일 배포시 설정 파일입니다.

 

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

 

3. .travis.yml 파일에도 CodeDeploy 내용을 추가합니다.

deploy:
  ...

  - provider: codedeploy
    access_key_id: $AWS_ACCESS_KEY
    secret_access_key: $AWS_SECRET_KEY
    bucket: yonghwan-freelec-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
    on:
      all_branches: true

 

4. 프로젝트에 yml 파일을 커밋 및 푸시하고 배포가 잘되었는지 확인합니다.

EC2 인스턴스에 다음 경로를 확인하여 프로젝트 파일들이 잘 도착했는지 확인합니다.

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

 

5. 배포 자동화 구성

deploy.sh 파일 추가

스프링 부트 프로젝트 최상단에 scripts/deploy.sh 파일을 추가합니다.

 

#!/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}')
# pgrep : ps + grep, -f=프로세스 이름과 완전히 일치하는, -l=리스트이름,프로세스이름 리스트
# awk : 텍스트 처리 및 명령어 추출을 위한 명령어, 첫번째 필드를 출력하라는 의미

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)
# ls -t= 파일 및 디렉토리를 수정 시간을 기준으로 정렬, -r=정렬 결과를 역순으로 출력
# 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 &

 

.travis.yml 파일 수정

.travis.yml 파일을 수정하여 실제 배포에 필요한 파일만 zip 파일로 만듭니다.

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 파일 이동

 

.appspec.yml 파일 수정

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

hooks:
  ApplicationStart:
    - location: deploy.sh # nginx와 연결되어 있지 않은 Port로 새 버전의 스프링 부트를 시작합니다.
      timeout: 60
      runas: ec2-user

 

위와 같이 수정하고 커밋 및 푸시하여 빌드 파일을 배포합니다.

 

6. CodeDeploy 로그 확인

$ cat /opt/codedeploy-agent/deployment-root/deployment-logs/codedeploy-agent-deployments.log

 

References

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

'Cloud' 카테고리의 다른 글

AWS EC2 인스턴스에 서버 배포  (0) 2023.04.16