본문 바로가기

AWS

Github Action과 AWS CodeDeploy를 사용하여 Spring Boot 프로젝트 CI/CD 파이프라인 구축하기

🔐 들어가며

안녕하세요! 이번 포스팅에서는 Github Action과 AWS CodeDeploy를 사용해서 CI/CD를 구축하는 방법에 대해 설명드리려고 합니다.

수동으로 매번 배포를 할 수도 있지만, CI/CD 파이프라인을 한 번 구축해두고 나면 매번 수동으로 배포할 필요가 없어 정말 편리하기 때문에 꼭 구축해보시는 것을 추천드립니다.

이 포스팅에서 구축할 파이프라인의 진행할 순서를 간단하게 그림으로 요약하면 아래와 같습니다.

  1. 개발자가 코드를 변경하고 Github에 Push 한다.
  2. Github Action이 돌아가며 빌드&테스트를 하고(CI), 문제가 없다면 압축파일(.zip)을 만든다.
  3. 만든 압축파일(.zip)을 AWS S3에 업로드한다.
  4. 업로드한 압축파일을 넘겨주며 CodeDeploy를 이용해 배포한다. (CD)

📚 포스팅에서 다루지 않는 것

한 포스팅에 A부터 Z까지 모든 내용을 다루는 것은 무리가 있기 때문에 CI/CD 파이프라인을 구축하는 방법을 중점적으로 다룰 예정입니다. 아래 내용들은 다루지 않으니 참고해주세요!

  • AWS EC2 인스턴스 만드는 방법
  • AWS EC2 인스턴스 ssh 접속하는 방법

그럼 이제 하나하나 진행해보도록 하겠습니다!


😸 Github Action CI

가장 먼저 Github Action을 통해 CI가 제대로 돌아가는지 확인해보도록 하겠습니다. .github/workflowsdeploy.yml 파일을 만들어줍니다.

name: deploy

on:
  push:
    branches: [ main ]
jobs:
  build:
    runs-on: ubuntu-latest

    steps:
      - name: Checkout
        uses: actions/checkout@v2

      - name: Set up JDK 1.8
        uses: actions/setup-java@v1
        with:
          java-version: 1.8

      - name: Grant execute permission for gradlew
        run: chmod +x ./gradlew
        shell: bash

      - name: Build with Gradle
        run: ./gradlew build
        shell: bash

제 애플리케이션은 Java8 + Gradle을 사용하고 있기 때문에 위와 같은 설정을 해주었습니다. 만약 다른 버전이나, 다른 프로젝트 관리 도구를 사용하신다면 코드를 수정해서 적용해주면 됩니다.

이제 Github Action이 잘 돌아가는지 확인해보기 위해 commit - push를 한 뒤에 Github로 돌아와 줍니다.

프로젝트에 문제가 없다면, 성공적으로 돌아가는 것을 확인할 수 있습니다! 🎉

이제 자동으로 빌드와 테스트가 되게 작업을 완료해두었으니, 이번에는 인스턴스에 자동으로 배포되게 해 보도록 하겠습니다.

CD를 위한 작업의 단계를 간단하게 나열해보자면 아래와 같습니다.

  1. CodeDeploy 만들기
  2. 관련 EC2 작업 (Java 설치, codedeploy-agent 설치 …)
  3. GithubAction CD 코드 추가

이제부터 1번부터 차근차근 진행해보도록 하겠습니다! 어렵지 않으니 따라 하다 보면 금방 완성할 수 있습니다. 🙂

 

🌧 CodeDeploy

CodeDeploy는 코드를 배포하기 위해 AWS에서 제공해주는 배포 서비스입니다. CodeDeploy를 사용하면 인스턴스에 배포를 자동으로 해줍니다.

역할 만들기

CodeDeploy가 제대로 돌아기 위해서는 먼저 CodeDeploy에게 역할을 부여해주어야 합니다. IAM → 역할에 들어간 뒤에, 새로운 역할을 만들어줍니다.

사용 사례를 CodeDeploy로 설정해줍니다.

그 이후에 이름을 입력하고 역할을 만들어줍니다.

 

애플리케이션 만들기

AWS CodeDeploy의 애플리케이션에 들어간 뒤에, 애플리케이션 생성을 눌러줍니다.

애플리케이션 이름을 넣어주고, 컴퓨팅 플랫폼은 EC2/온프레미스로 선택해준 뒤에 애플리케이션을 생성해줍니다.

 

배포 그룹 만들기

만들어진 애플리케이션에 들어가서, 배포 그룹을 생성해줍니다. 배포 그룹은 환경별로 나눌 수 있습니다. (ex. live, qa, dev …)

배포 그룹 이름을 지정해주고, 서비스 역할에는 아까 만든 IAM 역할을 선택해줍니다.

저는 단일 인스턴스에 배포할 예정이기 때문에 Amazon EC2 인스턴스를 선택했습니다.

태그 관리는 EC2에서 가능합니다! 만약 인스턴스에 태깅이 되어있지 않다면 태그를 추가해줍니다.

나머지는 기본 설정으로 두고 배포 그룹을 생성해줍니다.

 

프로젝트 코드 추가

배포 그룹을 생성했으니 이제 프로젝트에 CodeDeploy 설정과 관련한 코드를 추가하도록 하겠습니다. 프로젝트 최상단에 appspec.yml이라는 파일을 생성해줍니다.

version: 0.0
os: linux

files:
  - source: /
    destination: /home/ec2-user/app # 인스턴스에서 파일이 저장될 위치
hooks:
  AfterInstall:
    - location: deploy.sh
      timeout: 60
      runas: root

appsepc은 CodeDeploy의 설정 파일로, 배포 시점에 특정한 쉘을 실행시킬 수 있습니다. 하지만 저희가 할 배포는 단순 배포이기 때문에 설치가 된 이후에 deploy.sh라는 스크립트를 실행시키게 설정해줍니다.

deploy.sh

REPOSITORY=/home/ec2-user/app
cd $REPOSITORY

APP_NAME=curriculum #1
JAR_NAME=$(ls $REPOSITORY/build/libs/ | grep '.jar' | tail -n 1)
JAR_PATH=$REPOSITORY/build/libs/$JAR_NAME

CURRENT_PID=$(pgrep -f $APP_NAME)

if [ -z $CURRENT_PID ] #2
then
  echo "> 현재 구동중인 애플리케이션이 없으므로 종료하지 않습니다."
else
  echo "> kill -15 $CURRENT_PID"
  sudo kill -15 $CURRENT_PID
  sleep 5
fi

echo "> $JAR_PATH 배포" #3
nohup java -jar /home/ec2-user/app/build/libs/curriculum-1.0.jar --spring.config.location=/home/ec2-user/application.yml > /dev/null 2> /dev/null < /dev/null &

인스턴스를 배포하는 간단한 쉘 스크립트입니다. 중요한 부분을 간단하게 설명해드리겠습니다.

1) 애플리케이션이 구동 중인지 확인하기 위해 사용할 애플리케이션 이름입니다. jar 파일 이름으로 적어주면 됩니다. 저는 jar 파일이 curriculum-1.0.jar이기 때문에 curriculum이라고 명시해주었습니다.

2) 현재 인스턴스에서 애플리케이션이 구동중인지 확인하고, 구동 중이면 종료해줍니다.

3) jar 파일을 배포합니다. 저는 QA 환경용 설정 파일을 따로 사용하기 때문에 --spring.config.location 옵션을 통해 환경 파일을 지정해주었습니다. 만약 없다면 해당 옵션을 제거해주어도 무방합니다.


✌️ EC2

권한 설정

EC2에서 CodeDeploy, S3에 접근할 수 있는 권한이 없기 때문에 만들어둔 인스턴스에서 IAM 역할을 설정해주어야 합니다.

인스턴스를 누르고 작업 - 보안 - IAM 역할 수정을 선택합니다.

서비스는 EC2로 선택하고, 아래 권한 정책들을 추가해줍니다.

  • AmazonS3FullAccess
  • AWSCodeDeployFullAccess

다시 IAM 역할로 돌아와서 새로고침 버튼을 누른 뒤에 역할을 수정해주고, 저장을 눌러줍니다.

java 설치하기

ssh를 통해 인스턴스에 접속해준 이후에, 가장 먼저 Java를 설치해줍니다.

$ sudo yum list | grep jdk

설치 가능한 jdk 버전을 확인하고, 적절한 jdk를 설치해줍니다. 저는 java-1.8.0-openjdk-devel.aarch64를 설치하도록 하겠습니다.

$ sudo yum install -y java-1.8.0-openjdk-devel.aarch64

그 이후에 제대로 설치되었는지 확인해줍니다.

$ java -version
openjdk version "1.8.0_302"

 

code-agent 설치하기

CodeDeploy를 사용하기 위해 EC2 인스턴스에서 code-agent를 설치해줍니다.

$ sudo yum update

$ sudo yum install ruby
$ sudo yum install wget

$ cd /home/ec2-user
$ wget https://aws-codedeploy-ap-northeast-2.s3.ap-northeast-2.amazonaws.com/latest/install

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

설치가 모두 끝나면 code agent가 제대로 설치되었는지 확인하기 위해 아래 명령줄을 실행해줍니다.

$ sudo service codedeploy-agent status

# 실행이 되고 있지 않다면
$ sudo service codedeploy-agent start

 

😸 Github Action CD

이제 CodeDeploy, EC2까지 모든 설정이 완료되었기 때문에 마지막으로 Github Action에 코드를 추가하도록 하겠습니다!

접근 권한 만들어주기

Github Action에서 AWS 서비스를 이용하기 위해 Github Action 접근 권한을 만들어줍니다.

  • AmazonS3FullAccess
  • AWSCodeDeployRole

위 역할들을 추가해주고, 액세스 키와 시크릿키를 저장해둡니다.

Github Repository - Setting - Secrets에 들어가 시크릿 키를 만들어 저장해줍니다.

 

코드 추가

name: deploy

on:
  push:
    branches: [ main ]
jobs:
  build:
    runs-on: ubuntu-latest

    steps:
      - name: Checkout
        uses: actions/checkout@v2

      - name: Set up JDK 1.8
        uses: actions/setup-java@v1
        with:
          java-version: 1.8

      - name: Grant execute permission for gradlew
        run: chmod +x ./gradlew
        shell: bash

      - name: Build with Gradle
        run: ./gradlew build
        shell: bash

      # 추가
      - name: Make zip file #1
        run: zip -qq -r ./$GITHUB_SHA.zip .
        shell: bash

      - name: Configure AWS credentials #2
        uses: aws-actions/configure-aws-credentials@v1
        with:
          aws-access-key-id: ${{ secrets.ACCESS_KEY_ID }}
          aws-secret-access-key: ${{ secrets.ACCESS_KEY_SECRET }}
          aws-region: ap-northeast-2

      - name: Upload to S3 #3
        run: aws s3 cp --region ap-northeast-2 ./$GITHUB_SHA.zip s3://{저장할 폴더}/$GITHUB_SHA.zip

      - name: Code Deploy #4
        run: aws deploy create-deployment --application-name {애플리케이션 이름}
          --deployment-config-name CodeDeployDefault.OneAtATime
          --deployment-group-name {배포 그룹}
          --s3-location bucket={저장할 폴더},bundleType=zip,key=$GITHUB_SHA.zip

CD를 위해 Giuthub Action workflow에 해당 코드를 추가해줍니다.

1) 파일을 압축합니다. $GITHUB_SHA은 자체적으로 제공해주는 변수로, 중복을 회피하기 위해 사용했습니다.

2) Github Sercrets에 설정한 값들을 가져와 AWS에 인증합니다.

3) S3에 해당 압축 파일을 업로드합니다. region은 한국(ap-northeast-2)으로 설정해주었고, 저장할 폴더를 명시해주면 됩니다.

4) CodeDeploy를 시작합니다.

  • application-name : CodeDeploy의 애플리케이션 이름을 적어줍니다.
  • deployment-config-name : 배포 방법을 설정합니다. 한 번에 배포하는 방식(OneAtATime)을 선택합니다.
  • deployment-group-name : CodeDeploy의 배포 그룹을 적어줍니다. (ex. dev, qa, live …)
  • s3-location : s3의 위치를 명시해줍니다. 아까 저장할 폴더 이름을 그대로 적어줍니다.

그리고 commit - push 과정을 거친 이후에 확인하면 CD 과정까지 잘 돌아가는 것을 확인할 수 있습니다! 이제 CodeDeploy로 옮겨가서 확인해볼까요?

CodeDeploy 역시나 잘 돌아가는 것을 확인할 수 있습니다! 이렇게 하면 CD까지 모두 구성이 끝났습니다!


💛 맺으며

단일 인스턴스를 기준으로 CI/CD를 구성하는 것은 이처럼 정말 간단하게 할 수 있기 때문에 꼭 구성해두는 것을 추천합니다!

혹시 글을 읽으면서 잘못된 내용이 있으면 댓글로 알려주시면 감사하겠습니다! 읽어주셔서 감사합니다! 😊


👏 참고