5 minute read

기존 Github Action CD 에서 겪은 문제점

  • repository를 유지하고 있다보니 pull 과정에서 문제가 발생했었다.
    • 기존에는 해당 과정을 force pull 로 해결
  • 쉘 스크립트에서 CD 의 모든 과정을 진행하다보니 Github Action 에서 CD의 정확한 결과를 볼 수 없다.
    • runner는 쉘 스크립트 실행하는 행위만을 통해 성공, 실패를 결정
    • 개발자가 직접 해당 action 의 로그들로 파악해야 한다.
  • 프론트엔드와 백엔드 CD가 분리되어 있지 않았다.
    • 애플리케이션 재실행이 필요하지 않은 프론트엔드의 정적 리소스파일 변경에도 애플리케이션이 변경되었다.
  • Prod, Develop 환경의 CD 가 분리되어 있지 않았다.

해결을 위해 CD 과정 전반적 수정

백엔드

기존 백엔드 CD yml

  • worflow_dispatch 에서 브랜치를 직접 입력 받아서 쉘스크립트한테 전달
  • 쉘 스크립트에 브랜치명을 전달 이후 모든 CD 과정 쉘 스크립트에서 진행
name: Backend Develop Deploy (CD)

on:
  workflow_dispatch:
    inputs:
      branch:
        description: 'Branch Name'
        required: true

jobs:
  build:
    name: Backend Deploy
    runs-on: shook-runner

    steps:
      - name: Log pwd
        shell: bash
        run: pwd
      - name: Log Branch Name
        shell: bash
        run: echo "$"
      - name: Deploy
        shell: bash
        run: bash /home/ubuntu/deploy.sh

수정 방향

쉘 스크립트로 진행되는 과정을 Action의 Job 단위로 세분화 한다.

  • 쉘 스크립트로 전체를 진행할 때 문제점
    • 중간에 명령어가 실패하더라도 다음 작업은 성공적으로 동작할 수도 있다.
      • build 를 실패더라도 .jar파일 존재할 경우 해당 .jar로 서버가 실행된다.
    • 실패과정을 세부 로그를 보면서 직접 파악해야 한다.
  • 세분화하여 얻게 되는 이점
    • Actions 에서는 Job이 실패할 경우 X 표시와 함께 다음 진행을 멈춘다.
    • 실패과정을 Job 의 상태로 바로 파악할 수 있다.

애플리케이션을 실행하기 위한 필수 파일만 저장하도록 바꾼다.

  • 기존에는 서버에 repository 의 상태가 저장되어 있었다.
    • repository 를 최신화하기 위해 pull하는 과정에서 문제가 발생한 적이 여러번 있다.
    • 꼭 필요하지 않은 데이터로 서버의 용량이 낭비된다.
  • 변경 방식
    • 백엔드의 경우 .jar 파일 만 저장한다.
    • 프론트엔드의 경우 패키지 재설치후 빌드 한 정적 파일 리소스들만 저장한다.

변경 후 백엔드 CD 예상 시나리오

  • _work 디렉토리 안에서 runner가 repository를 지정한 branch 의 상태로 clone, checkout 한다.

  • 서브모듈 접근 토큰을 발급받아 submodule 업데이트를 진행한다.

  • 백엔드 빌드가 성공할 경우 생성된 .jar 파일을 ~/shook-jar 디렉토리 안으로 복사한다.

  • 복사가 끝난 이후 _work 디렉토리에 clone 된 repository 를 삭제한다.

  • 배포만 쉘 스크립트로 진행한다.

    • 기존 동작하던 애플리케이션을 종료
    • sudo 명령어를 통해 ~/shook-jar 안에 존재하는 jar 파일을 실행하여 서버를 구동시킨다.
      • 개발 환경 : –spring.profiles.active = dev
      • 배포 환경 : –spring.profiles.active = prod
    • 이 과정에서 ERROR 발생 시 슬랙으로 ERROR 로그 내용과 함께 알림이 온다.

수정 후 예상 백엔드 CD yml

name: Backend Develop Deploy (CD)

on:
  workflow_dispatch:

jobs:
  build:
    name: Backend Develop Deploy
    runs-on: shook-runner

    steps:
      - name: Checkout Source Code
        uses: actions/checkout@v2
        with:
          token: $
          submodules: recursive
      - name: Grant execute permission to gradlew
        shell: bash
        run: chmod +x backend/gradlew
      - name: Check Java Version
        shell: bash
        run: java --version
      - name: Gradlew bootJar
        shell: bash
        run: |
          cd backend
          ./gradlew bootJar
      - name: Copy Jar
        shell: bash
        run: cp backend/build/libs/shook-0.0.1-SNAPSHOT.jar /home/ubuntu/application-jar
      - name: Backend Deploy
        shell: bash
        run: bash /home/ubuntu/backend-deploy.sh

기존 backend 배포 스크립트

PROJECT_NAME=2023-shook
PROJECT_BACKEND=/home/ubuntu/2023-shook/backend

# git clone 받은 위치로 이동
echo "> Move $PROJECT_BACKEND"
cd $PROJECT_BACKEND

# main 브랜치로 이동
echo "> Git Checkout $1"
git switch $1

# main 브랜치의 최신 내용 받기
echo "> Git Pull $1"
git fetch --all
git reset --hard origin/$1
git pull origin $1

echo "> Submodule Update"
git fetch --recurse-submodules
git submodule update --remote

echo "> 현재 구동중인 애플리케이션 pid 확인"
CURRENT_PID=$(pgrep -f shook)
echo "> 현재 구동중인 애플리케이션 pid: $CURRENT_PID"

if [ -z "$CURRENT_PID" ]; then
        echo "> 구동중인 애플리케이션이 없습니다."
else
        while [ -n "$CURRENT_PID" ]
        do
                echo "> kill -15 $CURRENT_PID"
                sudo kill -15 $CURRENT_PID
                sleep 5
                CURRENT_PID=$(pgrep -f shook)
        done
fi

# build 수행
echo "> project build start"
chmod +x ./gradlew
./gradlew clean bootJar

echo "> jar directory로 이동"
cd $PROJECT_BACKEND/build/libs

echo "> 새 애플리케이션 배포"
JAR_NAME=$(ls -t | grep jar | head -n 1)
echo "$JAR_NAME"
sudo nohup java -jar $PROJECT_BACKEND/build/libs/$JAR_NAME --spring.profiles.active=prod 2>&1 &

수정 후 예상 배포 스크립트

배포만 진행하는 스크립트로 사용된다.

echo "> 현재 구동중인 애플리케이션 pid 를 조회합니다."
CURRENT_PID=$(pgrep -f shook)

if [ -z "$CURRENT_PID" ]; then
        echo "> 구동중인 애플리케이션이 없습니다."
else
        while [ -n "$CURRENT_PID" ]
        do
			    echo "> 현재 구동중인 애플리케이션 pid: $CURRENT_PID"
                echo "> kill -15 $CURRENT_PID"
                sudo kill -15 $CURRENT_PID
                sleep 5
                CURRENT_PID=$(pgrep -f shook)
        done
fi

echo "> 구동중인 애플리케이션 종료가 완료되었습니다."

# 애플리케이션 배포
echo "> 애플리케이션 배포를 시작합니다."
JAR_NAME=$(ls -t ~/application-jar/ | grep jar | head -n 1)

echo ">> $JAR_NAME 를 통한 배포 실행"
sudo nohup java -jar ~/application-jar/$JAR_NAME --spring.profiles.active=prod 2>&1 &

CURRENT_PID=$(pgrep -f shook)
echo ">> 실행된 애플리케이션 pid: $CURRENT_PID"

echo "> 애플리케이션 배포가 완료되었습니다."

변경 중 추가된 사항

  • Self-hosted-runner 가 작업을 마무리하는 과정에서 사용한 repository 를 삭제하면 문제가 발생했다.
  • Repository를 Self-hosted-runner 의 workspace 에서 checkout 관련 문제가 발생시 repository 를 스스로 삭제하는 것을 파악
  • 기존에 sudo 를 통해 root 권한으로 생성된 디렉토리, 파일에 대해서 github runner의 권한 문제로 인해 삭제가 불가능한 문제가 발생하여 불필요한 sudo 명령어를 모두 제거하였다.

프론트엔드

수정 방향

  • 모든 진행을 Github Action Job 단위로만 진행한다.

변경 후 프론트엔드 CD 예상 시나리오

  • _work 디렉토리 안에서 runner가 repository를 지정한 branch 의 상태로 clone, checkout 한다.

  • frontend 디렉토리로 이동해서 패키지 재설치를 진행한다. (clean install)

  • 정적 페이지 생성을 위한 build를 진행한다.

  • 생성된 정적 페이지들을 담은 dist 디렉토리를 ~/dist 에 덮어씌운다.

  • _work 디렉토리에 clone 된 repository 를 삭제한다.

기존 frontend 스크립트 ( 삭제 예상 )

#!/bin/bash

cd /home/ubuntu/2023-shook/frontend
git checkout $1
git fetch --all
git reset --hard origin/$1
git pull origin $1

sudo npm ci
sudo npm run build

새로 생성될 Github Action yml

name: Frontend Develop Deploy (CD)

on:
  workflow_dispatch:

jobs:
  build:
    name: Frontend Develop Deploy

    runs-on: shook-runner
    
    steps:
      - name: Checkout Source Code
        uses: actions/checkout@v2
      - name: Package clean install
        shell: bash
        run: |
          cd frontend
          npm ci
      - name: Build files
        shell: bash
        run: |
          cd frontend
          npm run build
      - name: Remove legacy static pages
        shell: bash
        run: rm -rf /home/ubuntu/dist
      - name: Move generated new static pages
        shell: bash
        run: cp -r frontend/dist ~/

팀원의 의견

나의 답변

나중에 파일이 많아졌을 경우 clone 시간이 늘어날 수 있다는 점은 매우 동의합니다!! !👍

이 부분에 있어서 clone이 진행되는 경우가 배포를 위한 준비과정이기에 서비스 운영이 멈추는 시간이 늘어나거나 하는 운영에 있어서 치명적인 시간 지연은 발생하지 않을 것으로 예상됩니다😊

베로가 걱정하는 clone 되는 시간이 늘어나서 생길 만한 문제상황은 개인적으로 hotfix가 진행되야 하는 경우가 있을 것 같습니다.

이 부분에서 지금 저희 현재 서비스와 도메인 특성상 hotfix의 초단위 지연이 큰 피해로 이어지는 크리티컬한 문제 발생 가능성은 없다고 생각했습니다! 🧐

( 물론 큰 피해가 없으니 hotfix 가 조금 늦어도 된다! 이런 말은 아닙니다  🫨 )

또한 배포 과정에서 발생한 문제를 개발자가 직접 파악하고 해결해야 하는 상황이 더 많은 시간이 요구될 것 같습니다! 😭

  1. 문제의 발생 가능성을 최대한 제거
  2. 빠른 트러블 슈팅이 가능한 CD 플로우

clone 시간을 감안하더라도 위에 두가지를 위한 CD 과정 변경이 배포 시간을 줄이는데 더 효율적이라고 생각합니다 🙇

위에 내용들은 물론 저의 개인적인 의견입니다~

위에 작성했듯이 github runner 의 마무리 작업에서 repository 를 삭제하면 안되는 문제가 발생하여 repository 의 상태는 유지하고 있게 되었다.