Skip to content

GitHub Actions 활용한 CI / CD 프로세스 구축 #90

@gdtknight

Description

@gdtknight

아래 내용은 이번 프로젝트 배포에 적용되어 현재 정상적으로 작동되고 있습니다.

추가 (23-08-10) - SSL 인증서 적용 완료


CD; Continuous Delivery / Continuous Deployment

  • AWS 이용시 반드시 Region 을 확인하고 이후 작업 진행해야 함. (IAM 등 Global 서비스 제외)

IAM (Identity and Access Management) 에서 생성해야 할 것들

사용자 생성하기

  • EC2(Elastic Compute Cloud) 유저 -> 필수 아님
  • S3(Simple Storage Service) 유저
    • S3 유저 생성 후 보안 자격 증명 탭에서 access-key 를 생성
    • 해당 내용들 반드시 메모 (GitHub Repository - Settings 탭에 GitHub Action Secret Key 에 해당 값 세팅 필요)
    • 권한 설정 - AmazonS3FullAccess, AWSCodeDeployFullAccess

역할 생성하기

  • CodeDeploy 에서 사용할 역할
    • AWSCodeDeployRole
  • EC2 에서 사용할 역할 (EC2 인스턴스 요약 -> 보안 -> IAM 역할 에서 확인 가능)
    • AmazonS3FullAccess
    • AWSCodeDeployFullAccess

EC2 인스턴스 생성 및 CodeDeployAgent 설치

$ tail -F /var/log/aws/codedeploy-agent/codedeploy-agent.log

S3 Bucket 생성

  • Public Access 차단 설정

Code Deploy Application 생성

  • Deploy Group 생성 (CodeDeploy 에서 사용하기 위해 IAM에서 생성한 역할 연결)
  • GitHub Action Secret Key 선언 및 값 설정

GitHub Action Workflow 작성 (.github/workflows/deploy.yml)

name: CI-CD

on:
  push:
    branches:
      - develop

env:
  S3_BUCKET_NAME: kdtmini5-s3 #S3 버킷 생성시 설정
  ADMIN_RESOURCE_PATH: ./admin/src/main/resources/application-prod.yml #admin module
  APPLICATION_RESOURCE_PATH: ./application/src/main/resources/application-prod.yml #application module
  CODE_DEPLOY_APPLICATION_NAME: kdtmini5_be_server
  CODE_DEPLOY_DEPLOYMENT_GROUP_NAME: kdtmini5-code-deploy-group

jobs:
  build:
    runs-on: ubuntu-latest

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

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

      - name: Set admin yaml file
        uses: microsoft/variable-substitution@v1
        with:
          files: ${{ env.ADMIN_RESOURCE_PATH }}
        env:
          secret-key: ${{ secrets.JWT_SECRET_KEY }}
          aes-key: ${{ secrets.AES_SECRET_KEY }}
          spring.mail.host: ${{ secrets.MAIL_SENDER_HOST }}
          spring.mail.port: ${{ secrets.MAIL_SENDER_PORT }}
          spring.mail.username: ${{ secrets.MAIL_SENDER_USERNAME }}
          spring.mail.password: ${{ secrets.MAIL_SENDER_PASSWORD }}
          spring.mail.properties.mail.smtp.ssl.true: ${{ secrets.MAIL_SENDER_HOST }}
          spring.datasource.url: ${{ secrets.DB_URL }}
          spring.datasource.driver-class-name: ${{ secrets.DB_DRIVER }}
          spring.datasource.username: ${{ secrets.DB_USER }}
          spring.datasource.password: ${{ secrets.DB_PASSWORD}}

      - name: Set application yaml file
        uses: microsoft/variable-substitution@v1
        with:
          files: ${{ env.APPLICATION_RESOURCE_PATH}}
        env:
          secret-key: ${{ secrets.JWT_SECRET_KEY }}
          aes-key: ${{ secrets.AES_SECRET_KEY }}
          spring.mail.host: ${{ secrets.MAIL_SENDER_HOST }}
          spring.mail.port: ${{ secrets.MAIL_SENDER_PORT }}
          spring.mail.username: ${{ secrets.MAIL_SENDER_USERNAME }}
          spring.mail.password: ${{ secrets.MAIL_SENDER_PASSWORD }}
          spring.mail.properties.mail.smtp.ssl.true: ${{ secrets.MAIL_SENDER_HOST }}
          spring.datasource.url: ${{ secrets.DB_URL }}
          spring.datasource.driver-class-name: ${{ secrets.DB_DRIVER }}
          spring.datasource.username: ${{ secrets.DB_USER }}
          spring.datasource.password: ${{ secrets.DB_PASSWORD}}

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

      - name: Build with Gradle
        run: ./gradlew clean :application:buildNeeded :admin:buildNeeded --stacktrace --info -x test
        shell: bash

      - name: Make zip file
        run: zip -r ./$GITHUB_SHA .
        shell: bash

      - name: Configure AWS credentials
        uses: aws-actions/configure-aws-credentials@v1
        with:
          aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }}
          aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
          aws-region: ${{ secrets.AWS_REGION }}

      - name: Update to S3
        run: aws s3 cp --region ap-northeast-2 ./$GITHUB_SHA.zip s3://$S3_BUCKET_NAME/$GITHUB_SHA.zip

      - name: Code Deploy
        run: |
          aws deploy create-deployment \
          --deployment-config-name CodeDeployDefault.AllAtOnce \
          --application-name ${{ env.CODE_DEPLOY_APPLICATION_NAME }} \
          --deployment-group-name ${{ env.CODE_DEPLOY_DEPLOYMENT_GROUP_NAME }} \
          --s3-location bucket=$S3_BUCKET_NAME,bundleType=zip,key=$GITHUB_SHA.zip

CodeDeploy 에 의해 실행되는 부분 - (./appspec.yml)

version: 0.0
os: linux
files:
  - source: /
    destination: /home/ubuntu/kdtmini5_be_server
    overwrite: yes

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

hooks:
  ApplicationStart:
    - location: scripts/gh_deploy.sh
      timeout: 60
      runas: ubuntu

EC2 에서 Jar 파일 실행을 위한 스크립트 작성 (./scripts/gh_deploy.sh)

#!/bin/bash

#GitHub Repository 이름?
PROJECT_NAME="kdtmini5_be_server"
DEPLOY_PATH=/home/ubuntu/$PROJECT_NAME/
DEPLOY_LOG_PATH="/home/ubuntu/$PROJECT_NAME/deploy.log"
DEPLOY_ERR_LOG_PATH="/home/ubuntu/$PROJECT_NAME/deploy_err.log"

ADMIN_LOG_PATH="/home/ubuntu/$PROJECT_NAME/admin.log"
APPLICATION_LOG_PATH="/home/ubuntu/$PROJECT_NAME/application.log"

ADMIN_JAR_PATH="/home/ubuntu/$PROJECT_NAME/admin/build/libs/*.jar"
APPLICATION_JAR_PATH="/home/ubuntu/$PROJECT_NAME/application/build/libs/*.jar"

ADMIN_BUILD_JAR=$(ls $ADMIN_JAR_PATH)
APPLICATION_BUILD_JAR=$(ls $APPLICATION_JAR_PATH)

ADMIN_JAR_NAME=$(basename $ADMIN_BUILD_JAR)
APPLICATION_JAR_NAME=$(basename $APPLICATION_BUILD_JAR)

echo "==== 배포 시작 : $(date +%c) ====" >> $DEPLOY_LOG_PATH

echo "> admin build 파일명 : $ADMIN_JAR_NAME" >> $DEPLOY_LOG_PATH
echo "> application build 파일명 : $APPLICATION_JAR_NAME" >> $DEPLOY_LOG_PATH

echo "> build 파일 복사" >> $DEPLOY_LOG_PATH
cp $ADMIN_BUILD_JAR $DEPLOY_PATH
cp $APPLICATION_BUILD_JAR $DEPLOY_PATH

echo "> 현재 동작중인 애플리케이션 pid 체크" >> $DEPLOY_LOG_PATH
ADMIN_CURRENT_PID=$(pgrep -f $ADMIN_JAR_NAME)
APPLICATION_CURRENT_PID=$(pgrep -f $APPLICATION_JAR_NAME)

if [ -z $ADMIN_CURRENT_PID ]
then
  echo "> 현재 동작중인 ADMIN 존재 X" >> $DEPLOY_LOG_PATH
else
  echo "> 현재 동작중인 ADMIN 존재 O" >> $DEPLOY_LOG_PATH
  echo "> 현재 동작중인 ADMIN 강제 종료 진행" >> $DEPLOY_LOG_PATH
  echo "> kill -9 $ADMIN_CURRENT_PID" >> $DEPLOY_LOG_PATH
  kill -9 $ADMIN_CURRENT_PID
fi

if [ -z $APPLICATION_CURRENT_PID ]
then
  echo "> 현재 동작중인 APPLICATION 존재 X" >> $DEPLOY_LOG_PATH
else
  echo "> 현재 동작중인 APPLICATION 존재 O" >> $DEPLOY_LOG_PATH
  echo "> 현재 동작중인 APPLICATION 강제 종료 진행" >> $DEPLOY_LOG_PATH
  echo "> kill -9 $APPLICATION_CURRENT_PID" >> $DEPLOY_LOG_PATH
  kill -9 $APPLICATION_CURRENT_PID
fi

ADMIN_DEPLOY_JAR=$DEPLOY_PATH$ADMIN_JAR_NAME
APPLICATION_DEPLOY_JAR=$DEPLOY_PATH$APPLICATION_JAR_NAME

echo "> ADMIN_DEPLOY_JAR 배포" >> $ADMIN_LOG_PATH
nohup java -Xms128m -Xmx196m -jar -Dspring.profiles.active=prod $ADMIN_DEPLOY_JAR >> $ADMIN_LOG_PATH 2> $DEPLOY_ERR_LOG_PATH &

echo "> APPLICATION_DEPLOY_JAR 배포" >> $APPLICATION_LOG_PATH
nohup java -Xms128m -Xmx196m -jar -Dspring.profiles.active=prod $APPLICATION_DEPLOY_JAR >> $APPLICATION_LOG_PATH 2> $DEPLOY_ERR_LOG_PATH &

sleep 3

echo "> 배포 종료 : $(date +%c)" >> $DEPLOY_LOG_PATH

추가 - 위 내용은 엄밀히 따져서 CD (Continuous Deployment, Continuous Delivery) 에 해당

  • 왜냐하면 CI (Continuous Integration) 는 브랜치 통합 시 충돌이 일어나거나 혹은 통합 후 테스트가 실패하는 경우 브랜치의 내용을 이전으로 복구하는 등의 과정을 자동화 시켜서 개발자가 개발에 집중할 수 있도록 도움을 주는 것임.

  • 현재 이런 통합의 과정은 팀원 각자가 진행을 하고 있고, 머지 이후 빌드 오류가 발생하는 경우 직접 Reset/Revert 등의 과정을 거쳐서 복구해야 하기 때문에 CI 에는 해당하지 않음.

  • CI/CD (출처 : https://www.redhat.com/rhdc/managed-files/ci-cd-flow-desktop.png?cicd=32h281b)
    ci-cd-flow-desktop

  • GitHub Action 에서 CI 를 지원하고 있음.

  • 참고 : GitHub Action - 연속 통합

  • ChatGPT : GitHub Action 역시 Elastic Beanstalk 와 마찬가지로 docker 기반 !


Metadata

Metadata

Assignees

Labels

Type

No type

Projects

Status

No status

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions