깃허브, Travis CI, AWS로 CI/CD 구축하기

jjunhwan.kim·2022년 2월 3일
1

AWS 사용하기

목록 보기
4/4
post-thumbnail

이 포스팅은 "스프링 부트와 AWS로 혼자 구현하는 웹 서비스" 책을 보고 정리한 내용입니다. 이번 포스팅에서는 깃허브에 코드가 푸시되면 자동으로 빌드 및 배포가 이루어지는 환경을 구축해보겠습니다. 이전 포스팅에서 구축한 EC2 서버, RDS, 깃허브를 이용합니다.

CI/CD란?

  • 코드가 업데이트되면 자동으로 테스트와 빌드를 수행하고 배포 파일을 만드는 것을 CI(Continuous Integration)이라고 한다.
  • 배포파일을 자동으로 운영 서버에 배포하는 것을 CD(Continuous Deployment)이라고 한다.
  • 깃허브, Travis CI, AWS S3, AWS CodeDeploy, AWS EC2등의 서비스를 사용하여 CI/CD를 구축한다.
  • 깃허브는 코드를 저장하는 저장소 역할을 한다.
  • Travis CI는 깃허브 프로젝트를 테스트와 빌드를 수행하여 배포 파일을 만들고 AWS S3에 저장한다.
  • AWS S3는 파일 서버 역할을한다.
  • AWS CodeDeploy는 S3에 저장된 배포 파일을 EC2 서버에 배포한다.

1. Travis CI와 깃허브 연동

Travis CI는 웹 기반의 CI 서비스 이다. 깃허브와 연동하여 사용한다.

  1. https://www.travis-ci.com 에서 깃허브 계정으로 로그인한다.
  2. Settings 메뉴의 Repository 탭에서 연동할 깃허브 리포지토리를 설정한다. 모든 리포지토리를 연동하거나 특정한 리포지토리를 선택할 수 있다.

  3. Travis CI에 깃허브의 리포지토리가 추가되었는지 확인한다.
  4. 깃허브 리포지토리의 Travis CI 설정은 .travis.yml 파일로 설정한다. build.gradle 파일과 같은 위치에 .travis.yml파일을 생성하고 다음의 코드를 작성한다.
language: java
jdk:
  - openjdk11

# master 브랜치가 푸시될 때 CI 수행
branches:
  only:
    - master

# 의존성을 해당 디렉토리에 캐시
cache:
  directories:
    - '$HOME/.m2/repository'
    - '$HOME/.gradle'

# 푸시될 때 수행하는 명령어
script: "./gradlew clean build"

# CI 실행 완료시 메일 알림
notifications:
  email:
    recipients:
      - 이메일 주소
  1. .travis.yml 파일을 master 브랜치에 푸시하면 CI가 수행되고 메일로 결과를 알려준다. Travis CI의 저장소 빌드 내역을 확인한다.

2. Travis CI와 AWS S3 연동

AWS S3는 일종의 파일 서버이다. AWS S3와 Travis CI를 연동하는 이유는 Travis CI가 빌드한 결과물인 Jar파일을 S3에 저장하기 위해서이다. S3에 저장된 Jar파일을 CodeDeploy 서비스가 EC2에 배포하게된다.

1) AWS Key 발급

일반적으로 AWS 서비스에 외부 서비스가 접근할 수 없다. 접근하기 위해서 접근 권한을 가진 Key를 발급받아 사용해야 한다. AWS에서는 IAM(Identity and Access Management)이라는 서비스를 통해 접근 권한 및 인증을 관리한다.

  1. Travis CI가 AWS S3와 CodeDeploy에 접근할 수 있도록 IAM 사용자 Key를 발급한다. AWS 사이트에서 IAM을 검색하여 이동한다. 액세스 관리에서 사용자를 선택하고 사용자 추가를 선택한다.
  2. 사용자 이름을 입력하고, 액세스 유형으로 프로그래밍 방식 액세스를 선택한다.
  3. 기존 정책 직접 연결로 AmazonS3FullAccess정책과 AWSCodeDeployFullAccess정책을 선택한다.

  4. 태그를 설정한다. 키 이름은 Name으로 값은 적당한 이름으로 작성한다.
  5. 설정한 항목들을 검토하고 사용자 만들기를 선택한다.
  6. 액세스 키 ID와 비밀 액세스 키가 생성된다. 이 두 개의 값을 Travis CI에서 사용한다. 두 개의 값을 복사해논다.

2) Travis CI에 키 등록

  1. Travis CI 사이트에서 리포지토리 설정으로 이동한다.
  2. Environment Variables에 액세스 키 ID와 비밀 액세스 키를 등록한다.
    1. Name에 AWS_ACCESS_KEY를, Value에 액세스 키 ID를 등록한다.
    2. Name에 AWS_SECRET_KEY를, Value에 비밀 액세스 키를 등록한다.
  3. ".travis.yml" 파일에서 $AWS_ACCESS_KEY, $AWS_SECRET_KEY의 형식으로 등록된 값들을 참조할 수 있다.

3) S3 버킷 생성

AWS S3는 일종의 파일 서버이다. Travis CI에서 생성된 배포 파일을 저장하도록 설정한다. AWS CodeDeploy 에서 S3에 저장된 배포 파일을 가져가도록 설정한다.

  1. AWS 사이트에서 S3를 검색하여 이동한다. 버킷 만들기를 선택한다.
  2. 버킷 이름을 입력하고 리전을 선택한다.
  3. 모든 퍼블릭 액세스 차단을 선택한다. Travis CI에 IAM 사용자로 발급받은 키를 사용하므로 퍼블릭 액세스 차단을 선택하더라도 Travis CI가 S3에 접근가능하다.
  4. 별다른 설정 없이 버킷 만들기를 선택하여 버킷을 생성한다.
  5. 생성된 버킷을 확인한다.

4) .travis.yml 파일에 AWS S3 설정 추가

Travis CI에서 빌드된 배포 파일을 S3에 업로드 하도록 .travis.yml 파일에 설정을 추가한다.

before_deploy: # deploy 명령어가 실행되기 전에 수행
  - zip -r todo-app-backend * # 현재 경로의 모든 파일을 todo-app-backend.zip 파일로 압축
  - mkdir -p deploy
  - mv todo-app-backend.zip deploy/todo-app-backend.zip

deploy: # 외부 서비스와 연동될 행위들을 선언
  - provider: s3
    access_key_id: $AWS_ACCESS_KEY # Travis CI의 Repository Environment Variable 설정 값
    secret_access_key: $AWS_SECRET_KEY # Travis CI의 Repository Environment Variable 설정 값
    bucket: todo-app-backend-build # S3 버킷 이름
    region: ap-northeast-2
    skip_cleanup: true
    acl: private # zip 파일 접근 private
    local_dir: deploy # before_deploy에서 생성한 디렉토리
    wait-until-deployed: true
  1. ".travis.yml" 파일을 깃허브에 푸시하면 Travis CI에서 자동으로 빌드하고 S3 버킷에 zip파일을 업로드한다.
  2. S3 버킷에서 업로드가 되었는지 확인한다.

3. Travis CI와 AWS S3, CodeDeploy 연동

1) EC2에 IAM 역할 추가

EC2가 CodeDeploy와 연동할 수 있도록 IAM 역할을 생성해야한다. 이전의 Travis CI가 AWS S3, CodeDeploy에 접근할 수 있도록 IAM에서 사용자를 추가하였다. 이번에는 IAM에서 역할을 추가해야한다.

  • 역할은 AWS 서비스안에서 사용할 수 있는 권한이다.
  • 사용자는 AWS 외부에서 AWS 서비스를 사용할 수 있는 권한이다.
  • EC2는 AWS 안에서 제공되는 서비스이므로 역할을 만들어야한다.
  1. AWS 사이트에서 IAM을 검색하여 이동한다. 액세스 관리에서 역할을 선택하고 역할 만들기를 선택한다.
  2. AWS 서비스와 EC2를 선택하고 다음을 선택한다.
  3. 정책에서 AmazonEC2RoleforAWSCodeDeploy를 선택한다.
  4. 태그를 설정한다. 키 이름은 Name으로 값은 적당한 이름으로 작성한다.
  5. 역할의 이름을 등록하고 최종 확인을 한다.
  6. AWS의 EC2 인스턴스 목록으로 이동하여, 인스턴스를 마우스 우클릭하여 인스턴스 설정 -> IAM 역할/연결바꾸기를 선택하여 방금 생성한 역할을 선택하고 저장한다.

  7. 인스턴스를 재부팅한다.

2) EC2에 CodeDeploy 에이전트 설치

  1. EC2에 접속하여 다음 명령어를 입력한다.
# 아래 명령어를 입력한다.
aws s3 cp s3://aws-codedeploy-ap-northeast-2/latest/install . --region ap-northeast-2
  1. 정상적으로 다운로드되면 아래 메시지가 출력된다.
download: s3://aws-codedeploy-ap-northeast-2/latest/install to ./install
  1. install 파일에 실행 권한을 추가한다.
chmod +x ./install
  1. install 파일로 설치를 진행한다. 만약 "/usr/bin/env: ruby: No such file or directory" 메시지가 뜬다면 루비를 설치한다.
sudo ./install auto
sudo yum install ruby
  1. 설치가 끝나고 에이전트가 정상적으로 실행되고 있는지 상태 검사를 한다.
sudo service codedeploy-agent status
  1. 아래 메시지가 출력되면 정상이다.
The AWS CodeDeploy agent is running as PID xxx

3) CodeDeploy에 IAM 역할 추가

CodeDeploy가 EC2에 연동할 수 있도록 IAM 역할을 생성해야한다. EC2와 마찬가지로 IAM 역할을 추가해야한다.

  1. AWS 사이트에서 IAM을 검색하여 이동한다. 액세스 관리에서 역할을 선택하고 역할 만들기를 선택한다.
  2. AWS 서비스와 CodeDeploy를 선택한다.

  3. AWSCodeDeployRole 권한이 자동으로 추가되어있다. 확인하고 다음으로 넘어간다.
  4. 태그를 설정한다. 키 이름은 Name으로 값은 적당한 이름으로 작성한다.
  5. 역할의 이름을 등록하고 최종 확인을 한다.

4) CodeDeploy 생성

CodeDeploy를 생성해야한다. CodeDeploy는 깃허브, Travis CI, AWS S3와 연동하여 AWS EC2에 배포하는 역할을 한다.
다시한번 간단하게 정리하면 다음과 같다.

  • 깃 로컬 저장소에 커밋한다.
  • 깃허브에 푸시하면 Travis CI가 테스트와 빌드를 수행한다.
  • 빌드된 배포파일을 AWS S3에 저장한다.
  • AWS CodeDeploy가 S3의 배포파일을 AWS EC2 서버에 배포한다.
  1. AWS 사이트에서 CodeDeploy를 검색하여 이동한다. CodeDeploy 시작하기를 선택하고 애플리케이션 생성을 선택한다.
  2. 애플리케이션 이름을 입력하고 컴퓨팅 플랫폼을 선택한다. EC2/온프레미스로 선택한다.
  3. 배포 그룹을 생성을 선택한다.
  4. 배포 그룹 이름을 입력하고 역할에 이전에 생성한 CodeDeploy용 IAM 역할을 선택한다. 배포 유형에서는 현재 위치를 선택한다.
  5. 환경 구성에서 EC2 인스턴스를 선택하고 이전에 생성한 EC2 인스턴스의 태그를 입력한다.
  6. 배포 구성에서 CodeDeployDefault.AllAtOnce를 선택하고, 로드 밸런서에서 로드 밸런싱 활성화는 체크 해제한다.

5) Travis CI, S3, CodeDeploy 연동

  1. EC2 서버에 AWS S3에서 넘겨줄 zip 파일을 저장할 디렉토리를 생성한다.
mkdir ~/app/step2 && mkdir ~/app/step2/zip
  1. Travis CI에서 빌드가 완료되면 S3에 zip 파일이 전송되고, zip 파일이 EC2의 ~/app/step2/zip 경로에 복사되도록 설정한다. Travis CI의 설정은 .travis.yml에 AWS CodeDeploy의 설정은 appspec.yml에 작성한다.

  2. 먼저 .travis.yml과 같은 경로에 appspec.yml을 생성하고 아래 내용을 작성한다.

version: 0.0
os: linux
files:
  - source: / # CodeDeploy에서 destination으로 이동시킬 대상을 지정. 루트를 지정하면 전체파일을 이동
    destination: /home/ec2-user/app/step2/zip # source에서 지정한 파일을 받을 위치 지정
    overwrite: yes # 덮어쓰기 설정
  1. 다음으로 .travis.yml에 아래 내용을 추가한다.
deploy: # 외부 서비스와 연동될 행위들을 선언
  - provider: s3
    access_key_id: $AWS_ACCESS_KEY # Travis CI의 Repository Environment Variable 설정 값
    secret_access_key: $AWS_SECRET_KEY # Travis CI의 Repository Environment Variable 설정 값
    bucket: todo-app-backend-build # S3 버킷 이름
    region: ap-northeast-2
    skip_cleanup: true
    acl: private # zip 파일 접근 private
    local_dir: deploy # before_deploy에서 생성한 디렉토리
    wait-until-deployed: true
  - provider: codedeploy
    access_key_id: $AWS_ACCESS_KEY # Travis CI의 Repository Environment Variable 설정 값
    secret_access_key: $AWS_SECRET_KEY # Travis CI의 Repository Environment Variable 설정 값
    bucket: todo-app-backend-build # S3 버킷 이름
    key: todo-app-backend.zip # 빌드 파일
    bundle_type: zip
    application: todo-app-backend # CodeDeploy 애플리케이션 이름
    deployment_group: todo-app-backend-group # CodeDeploy 배포 그룹 이름
    region: ap-northeast-2
    wait-until-deployed: true
  1. 추가한 .appspec.yml과 수정한 .travis.yml을 커밋하고 푸시한다. Travis CI가 자동으로 시작된다. AWS CodeDeploy 배포 그룹에서 아래와 같이 배포 성공을 확인한다.
  2. EC2 서버에 배포파일이 잘 복사되었는지 확인한다.
cd /home/ec2-user/app/step2/zip
ll
  1. 아래와 같이 파일이 잘 저장된것을 확인한다.

4. 배포 자동화 구성

지금까지 Travis CI, S3, CodeDeploy를 연동하였다. 이제 Jar 파일 배포를 자동화 하도록 설정한다.

1) deploy.sh 파일 생성

기존에 step1 디렉토리에서 작성한 deploy.sh를 기반으로 step2 디렉토리에서 실행될 deploy.sh를 작성해야한다. 먼저 스프링 프로젝트 루트경로에 scripts 디렉토리를 만들고 delply.sh 파일을 생성한다.

#!/bin/bash

# 변수 선언
REPOSITORY=~/app/step2
EXECUTABLE_NAME=todo

echo "> Build 파일 복사"

cp $REPOSITORY/zip/*.jar $REPOSITORY

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

# 명령어 실행결과를 변수에 할당 $()
CURRENT_PID=$(pgrep -fl ${EXECUTABLE_NAME} | grep java | awk '{print $1}')

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

# [ -z ] 는 문자열의 길이가 0이면 참
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 -v plain | tail -n 1)

echo "> JAR Name: $JAR_NAME"

echo "> $JAR_NAME 에 실행권한 추가"

chmod +x $JAR_NAME

echo "> $JAR_NAME 실행"

# nohup은 터미널이 끊겨도 실행한 프로세스는 계속 동작하게 함. 실행 중 생기는 메시지를 nohup.out 파일을 만들어 이 파일에 출력
# 2>&1는 표준오류(stderr)를 표준출력(stdout)이 전달되는 곳으로 보냄. 따라서 nohup.out에 표준오류도 같이 씀
# 마지막 &는 명령을 백그라운드 작업으로 실행함
nohup java -jar $JAR_NAME --spring.profiles.active=dev > $REPOSITORY/nohup.out 2>&1 &

2) .travis.yml 파일 수정

이전의 .travis.yml에서는 모든 파일을 zip파일로 만들었다. 하지만 실제로 필요한 파일은 jar, appspec.yml, deploy.sh 파일이다. 따라서 실제로 필요한 파일만 선택하여 zip파일로 만들도록 수정한다.

before_deploy: # 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로 이동 후 모든 파일을 before-deploy.zip 파일로 압축
  - cd .. && mkdir -p deploy # 상위 디렉토리로 이동 후 deploy 디렉토리 생성
  - mv before-deploy/before-deploy.zip deploy/todo-app-backend.zip # deploy로 zip파일 이동

3) appspec.yml 파일 수정

appspec.yml 파일에 아래 코드를 추가한다.

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

hooks:
  ApplicationStart:
    - location: deploy.sh
      timeout: 60
      runas: ec2-user

4) 배포 자동화 확인

  1. 깃허브로 수정내용을 커밋하고 푸시하여 Travis CI에서 성공 메시지를 확인한다.
  2. CodeDeploy로 배포되는 부분은 /opt/codedeploy-agent/deployment-root/deployment-logs 디렉토리의 codedeploy-agent-deployments.log 파일을 확인한다. delply.sh 파일의 실행 결과를 확인할 수 있다.
  3. /opt/codedeploy-agent/deployment-root 디렉토리 하위의 디렉토리 중 영문과 대시로 이루어진 디렉토리는 CodeDeploy ID이다. 이 디렉토리안에는 배포 단위별로 배포 파일들을 확인할 수 있다. 각각의 로그도 확인할 수 있다.

지금까지 깃허브에 푸시하면 테스트, 빌드, 배포까지 자동화를 구축하였다. 다음에는 배포하는 동안 스프링 부트 프로젝트가 종료되는데 배포하는 동안에도 서비스를 유지하는 무중단 배포를 구축해본다.

0개의 댓글