[ AWS ] 누구나 쉽게 따라할 수 있는 CI/CD 구축하여 프로젝트 배포 자동화하기 #3

해당 게시글의 코드 내용은 아래의 링크에서 보실 수 있습니다.

Git Repository : https://github.com/seungm2n/opendata

안녕하세요, 오늘은 AWS CI/CD 구축하는 방법에 대해서 글을 작성하려고 합니다.
JAVA와 SPRING BOOT를 Github Action으로 AWS에서 제공하는 EC2와 S3, CodeDeploy를 통하여 구축하는 방법에 대해서 설명하려고합니다.

이번 게시글에서는 지난 게시글에 이어AWS S3 설정, Github Actions 설정과 배포 스크립트 작성하는 방법까지 설명을 드리고자 합니다.

** 앞서 설정하지 않았던 부분들이 있다면 이전 게시글을 참고하시기 바랍니다.

AWS S3 설정

AWS의 S3는 Github action의 워크플로우가 수행되어지게 되면 코드를 압축해서 업로드 되어지는 곳이다.

  1. AWS 상단 검색창에 S3를 검색합니다.
  2. S3 > 버킷을 선택합니다.
  3. 버킷을 클릭하여 이동하고 버킷 만들기를 클릭합니다.
  4. 버킷 이름을 작성합니다. ( * 버킷 이름은 쉘스크립트를 작성할 때 필요함으로 꼭 기억해둘 것. )
  5. 스크롤을 내려 ‘이 버킷의 퍼블릭 액세스 차단 설정’ 항목에 ‘모든 퍼블릭 액세스 차단’ 체크박스에 체크를 한다. 체크가 되어 있을 수도 있습니다. S3 버킷은 퍼블릭에서 접근할 수 없도록 해야합니다.
  6. 여기까지 되었다면 제일 하단에 있는 버킷 만들기를 클릭합니다.

AWS CodeDeploy 설정

  1. 상단 검색창에 CodeDeploy를 검색하여 CodeDeploy로 접근합니다.
  2. 좌측 사이드 바에 배포 > 애플리케이션으로 접근하여 애플리케이션 생성 버튼을 클릭합니다.
  3. 애플리케이션 이름을 작성해주고, 컴퓨팅 플랫폼에서 EC2/온프레미스를 선택해주고 난 후 애플리케이션 생성버튼을 클릭합니다.( * 애플리케이션 이름은 쉘스크립트를 작성할 때 필요함으로 꼭 기억해둘 것. )

배포그룹 생성

위의 순서대로 하고나면 CodeDeploy > 배포 > 애플리케이션 > 애플리케이션으로 이동하게 되어집니다.

여기서 배포 그룹 생성 버튼을 클릭합니다.

배포 그룹 이름을 작성하고 서비스 역할 항목 > 이전에 생성했던 CodeDeploy IAM 계정을 선택합니다. ( * 배포 그룹 이름은 쉘스크립트를 작성할 때 필요함으로 꼭 기억해둘 것. )

[참고] [AWS CI/CD 구축 (1)] CI/CD 설명 및 IAM계정 설정, 역할 생성

스크롤을 조금 내려서 보면 환경 구성 항목 > Amazon EC2 인스턴스 > 키-값에 Name – CICD_EC2 (이전에 생성한 EC2) 를 선택한다.

[참고] [AWS CI/CD 구축 (2)] VPC설정, 서브넷 설정, EC2 생성 및 JAVA 설치, IAM 역할 연결

스크롤을 더 내려서 AWS Systems Manager를 사용한 에이전트 구성 > 한번만을 클릭하고 로드밸런서 > 로드 밸런싱 활성화에 체크를 해제해줍니다.

이걸로 AWS에서의 구축 준비는 끝나게 되었습니다.

이어서 각종 스크립트를 작성하는 방법에 대해서 설명하도록 하겠습니다.

코드 작성 및 경로

Github Actions 설정

먼저, 배포할 프로젝트의 레포지토리에 접근합니다.접근하고 난 후 상단 메뉴에 있는 Action을 클릭합니다.github-repository-action

Action에서 set up a workflow yourself을 클릭합니다.

action-set_up_a_workflow_yourself

클릭하고 나면 main.yml 에디터창으로 변하는데 여기서 셀 스크립트를 작성해주어야 합니다.

main.yml

경로 : {Git Repository}/.github/workflows/main.yml

name: CICD
run-name: Running
on:
  push:
    branches:
      - production    # production이라는 브런치로 푸쉬했을 때 실행
      - 'releases/**'

env:
  AWS_REGION: ap-northeast-2
  AWS_S3_BUCKET: app-release-files
  AWS_CODE_BUCKET_NAME : bucket-cicd    # 기억해두라고 했던 버킷 이름
  AWS_CODE_DEPLOY_APPLICATION: cd-application-cicd    # 기억해두라고 했던 애플리케이션 이름
  AWS_CODE_DEPLOY_GROUP: cd-publish-cicd      # 기억해두라고 했던 배포 그룹 이름

jobs:
  build-with-gradle:
    runs-on: ubuntu-20.04   # ubuntu ver
    steps:
    - name: production 브랜치로 이동
      uses: actions/checkout@v3
      with:
        ref: production
    - name: JDK 17 설치
      uses: actions/setup-java@v3
      with:
        java-version: '17'
        distribution: 'corretto'
    - name: gradlew에 실행 권한 부여
      run: chmod +x ./gradlew
    - name: 프로젝트 빌드
      run: ./gradlew clean build -x test
    - name: AWS credential 설정
      uses: aws-actions/configure-aws-credentials@v1
      with:
        aws-region: ${{ env.AWS_REGION }}
        aws-access-key-id: ${{ secrets.CICD_ACCESS_KEY }}
        aws-secret-access-key: ${{ secrets.CICD_SECRET_KEY }}
    - name: S3에 업로드
      run: aws deploy push --application-name ${{ env.AWS_CODE_DEPLOY_APPLICATION }} --ignore-hidden-files --s3-location s3://${{ env.AWS_CODE_BUCKET_NAME }}/cicdDir/$GITHUB_SHA.zip --source .
    - name: EC2에 배포
      run: aws deploy create-deployment --application-name ${{ env.AWS_CODE_DEPLOY_APPLICATION }} --deployment-config-name CodeDeployDefault.AllAtOnce --deployment-group-name ${{ env.AWS_CODE_DEPLOY_GROUP }} --s3-location bucket=${{ env.AWS_CODE_BUCKET_NAME }},key=cicdDir/$GITHUB_SHA.zip,bundleType=zip

위와 같이 입력하고 저장해줍니다.

appspec.yml

경로 : {Git Repository}/appspec.yml

위의 이미지와 같이 Create new file을 클릭하여 파일을 생성하고 아래에 있는 코드를 작성해줍니다.

version: 0.0
os: linux

files:
  - source:  /
    destination: /home/ubuntu/cicd
    overwrite: yes

permissions:
  - object: /
    pattern: "**"
    owner: ubuntu
    group: ubuntu

hooks:
  AfterInstall:
    - location: scripts/stop.sh
      timeout: 60
      runas: ubuntu
  ApplicationStart:
    - location: scripts/start.sh
      timeout: 60
      runas: ubuntu

위와 같이 입력하고 저장해줍니다.

start.sh / deploy.sh

경로 : {Git Repository}/scripts/start.sh

경로 : {Git Repository}/scripts/deploy.sh
— 해당 경로로 할 시, appspec.yml > hooks > ApplicationStart > location 코드 수정이 필요합니다.
#!/bin/bash

ROOT_PATH="/home/ubuntu/cicd"
JAR="$ROOT_PATH/application.jar"

APP_LOG="$ROOT_PATH/application.log"
ERROR_LOG="$ROOT_PATH/error.log"
START_LOG="$ROOT_PATH/start.log"

NOW=$(date +%c)

echo "[$NOW] $JAR 복사" >> $START_LOG
cp $ROOT_PATH/build/libs/*.jar $JAR

echo "[$NOW] > $JAR 실행" >> $START_LOG
nohup java -jar $JAR > $APP_LOG 2> $ERROR_LOG &

SERVICE_PID=$(pgrep -f $JAR)
echo "[$NOW] > 서비스 PID: $SERVICE_PID" >> $START_LOG

stop.sh

경로 : {Git Repository}/scripts/stop.sh

#!/bin/bash

ROOT_PATH="/home/ubuntu/cicd"
JAR="$ROOT_PATH/application.jar"
STOP_LOG="$ROOT_PATH/stop.log"
SERVICE_PID=$(pgrep -f $JAR) # 실행중인 Spring 서버의 PID

if [ -z "$SERVICE_PID" ]; then
  echo "서비스 NouFound" >> $STOP_LOG
else
  echo "서비스 종료 " >> $STOP_LOG
  kill "$SERVICE_PID"
  # kill -9 $SERVICE_PID # 강제 종료를 하고 싶다면 이 명령어 사용
fi

stop.sh 까지 작성하였다면 모든 준비는 완료가 되었습니다.

배포 확인

Github Actions Workflows 확인

  1. github Repository에서 production이라는 브런치를 생성 후, push를 발생시킵니다.
  2. push를 발생시켰다면 Github Repository > Actions으로 이동합니다.
  3. main.yml의 Running 성공을 확인할 수 있습니다.

AWS S3 확인

  1. AWS S3로 이동합니다.
  2. 해당 버킷을 선택하여 들어가면 cicdDir 폴더를 볼 수 있으며, 폴더 안에 접근하게 되면 .zip파일로 되어진 파일을 볼 수 있습니다.

AWS CodeDeploy 확인

  1. AWS CodeDeploy로 이동합니다.
  2. 배포 > 배포로 접근합니다.
  3. 그럼 다음과 같이 배포 상태를 확인할 수 있습니다.

AWS EC2에서 확인(1)

  1. AWS EC2에 접근하여 EC2 인스턴스 쉘에 접근합니다.
  2. 아래의 명령어를 입력합니다.
    ps -ef | grep java
  3. 아래와 같이 application.jar 가 실행 중인 것을 확인할 수 있습니다.

AWS EC2에서 확인(2)

  1. AWS EC2에 접근하여 EC2 인스턴스 쉘에 접근합니다.
  2. 아래의 명령어를 입력합니다.
    cd cicd
    ll
  3. 위의 명령어를 순차대로 입력을 하고나면 아래와 같이 application.jar 파일이 복사되어져 있으며, start.log와 stop.log 그리고 error.log파일이 생성되어져 있는 것을 확인할 수 있습니다.

Public IP 주소를 입력한 확인

저는 배포하기 전 Spring Boot에서 Controller와 index.html을 간략하게 하나 작성해서 배포하였습니다.
{Public IP}:8080/를 호출하게 되면 다음과 같이 출력이 되어집니다.

[주의]

위의 작성된 스크립트는 Branch에서 Push 했을 경우,

Branch 기준에 있는 코드가 배포되는 소스로 Main 혹은 Master가 아닌 Branch에 해당 소스를 제어하는 Controller나 .html이 정상적으로 Branch에 존재하는지 확인해야 합니다.

Pull이 안되어있을 수도 있으니 Pull이 되어 있는지 확인하고 Pull을 받은 후 Branch에서 Push가 되도록 하여 Git Actions가 다시 한번 실행되도록 하여야 합니다.