Skip to content

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

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 기반 !


Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

Type

No type

Projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions