[S-HOOK] 기존에 진행하던 CD에 존재하는 문제점 해결을 위한 수정
기존 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 가 조금 늦어도 된다! 이런 말은 아닙니다 🫨 )
또한 배포 과정에서 발생한 문제를 개발자가 직접 파악하고 해결해야 하는 상황이 더 많은 시간이 요구될 것 같습니다! 😭
- 문제의 발생 가능성을 최대한 제거
- 빠른 트러블 슈팅이 가능한 CD 플로우
clone 시간을 감안하더라도 위에 두가지를 위한 CD 과정 변경이 배포 시간을 줄이는데 더 효율적이라고 생각합니다 🙇
위에 내용들은 물론 저의 개인적인 의견입니다~

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