무중단 배포를 구축해 보자 - Front End 구성 편
Server안녕하세요 Lovefield입니다.
이전 글 “무중단 배포를 구축해 보자 - 환경 편”에 이어서 이번 글에서는 프론트 엔드 서버를 무중단 배포하기 위한 설정을 서술하려 합니다. 프론트 엔드 프레임워크로는 Nuxt.js를 사용했습니다. 구동 환경은 Bun.js를 사용했습니다. 우선적으로 빌드를 해야했기에 Git Action을 이용해 빌드와 업로드 기능을 작성해 주겠습니다.
Git Action을 작성하기 이전에 secrets 값을 지정해 줘야 합니다.
- ENV : 프론트 엔드 에서 사용하는 환경변수 값입니다.
- HOST_DIR : 서버에서 사용할 기본 디렉토리 위치입니다.
- HOST_IP : 서버 주소입니다.
- HOST_RUN_DIR : 서버에서 스크립트를 실행하는 디렉토리 위치입니다.
- HOST_USER : 서버 유저 이름입니다.
- PEM_VALUE : 서버에 접근하기위한 PEM키의 값입니다.
docker-compose.yml
YAML
name: Deploy
on:
push: # 브런치에 PUSH가 될 경우 실행
branches:
- main
workflow_dispatch: # GitHub action 페이지에서 수동으로 실행
name:
description: "print name"
jobs:
buildAndDeploy:
runs-on: ubuntu-latest
permissions:
contents: read
packages: write
steps:
- uses: actions/checkout@v3
- uses: oven-sh/setup-bun@v2 # bun.js 사용
- name: Module Install
run: bun install --frozen-lockfile
- name: Create env
run: echo "$ENV" >> .env
env:
ENV: ${{ secrets.ENV }}
- name: Blue
run: bun run build
- name: Create ZIP
run: zip -qq -r ./.output.zip ./.output
- name: Upload ZIP file
uses: appleboy/scp-action@v0.1.7
with:
host: ${{ secrets.HOST_IP }}
username: ${{ secrets.HOST_USER }}
key: ${{ secrets.PEM_VALUE }}
port: 22
source: "./.output.zip"
target: ${{ secrets.HOST_DIR }}/
- name: Run Deploy Script
uses: appleboy/ssh-action@v1.1.0
env:
HOST_DIR: ${{ secrets.HOST_DIR }}
HOST_RUN_DIR: ${{ secrets.HOST_RUN_DIR }}
with:
host: ${{ secrets.HOST_IP }}
username: ${{ secrets.HOST_USER }}
key: ${{ secrets.PEM_VALUE }}
port: 22
envs: HOST_DIR,HOST_RUN_DIR
script: |
sudo cp -vf $HOST_DIR/.output.zip $HOST_RUN_DIR/front/blue/.output.zip
sudo cp -vf $HOST_DIR/.output.zip $HOST_RUN_DIR/front/green/.output.zip
cd $HOST_RUN_DIR
sudo sh deploy-frontend.sh
GitHub Action을 이용해 빌드 및 업로드를 진행한 후, 서버에 있는 deploy-frontend.sh 를 실행하도록 구성했는데요. 이 deploy-frontend.sh를 통해서 Blue, Green 컨테이너를 업데이트 해줍니다.
deploy-frontend.sh
Bash
#!/bin/bash
# 실행중인 blue가 있는지
EXIST_BLUE=$(sudo docker compose ps -a | grep "frontend-blue")
target="blue";
target_port="8002"
TIMESTAMP=`date +%Y%m`
LOGFILE="./logs/frontend-$TIMESTAMP.log"
func_reload() # 재실행 함수
{
echo "[$(date +%Y-%m-%d\ %H\:%M\:%S)] $1 Container down" >> $LOGFILE
sudo docker compose down frontend-$1 # 컨테이너 다운
rm -rf ./front/$1/.output # 기존 파일 삭제
sudo unzip -o ./front/$1/.output.zip -d ./front/$1 # 압축 해제
rm ./front/$1/.output.zip # 압축파일 삭제
echo "[$(date +%Y-%m-%d\ %H\:%M\:%S)] $1 Container up" >> $LOGFILE
sudo docker compose up frontend-$1 -d # 컨테이너 업
sleep 1.5
while true; do # 2초마다 헬스 체크
RESPONSE=$(curl -I http://localhost:$2/health-check | grep "200" | awk '{print $2}')
echo "[$(date +%Y-%m-%d\ %H\:%M\:%S)] $1 Container Health Check" >> $LOGFILE
if [ "$RESPONSE" -eq "200" ]; then
break;
fi
sleep 2
done
}
touch $LOGFILE
echo "[$(date +%Y-%m-%d\ %H\:%M\:%S)] >>> 프론트 배포 스크립트 시작 <<<" >> $LOGFILE
# Blue가 실행중이라면
if [ "$EXIST_BLUE" ]; then
target="green";
target_port="8001"
else
target="blue";
target_port="8002"
fi
func_reload $target $target_port
if [ "$target" -eq "blue" ]; then
func_reload "green" "8001"
else
func_reload "blue" "8002"
fi
echo "[$(date +%Y-%m-%d\ %H\:%M\:%S)] >>> 프론트 배포 스크립트 종료 <<<" >> $LOGFILE
위와 같이 작성 후 Git Push를 통해서나 직접 Action을 생성해서든 실행이 된다면 다음과 같은 순서대로 진행이 됩니다.
Git Action에서 main 브런치를 기준으로 빌드합니다.
Git Action에서 빌드된 파일을 압축하여 서버에 전송합니다.
Git Action에서 서버에 있는 배포 스크립트를 실행합니다.
배포 스크립트에서 전달받은 압축파일을 풀고 Blue, Green 컨테이너를 각각 업데이트 합니다.
전부 진행이 되었다면 logs 폴더에 다음과 같은 log 파일이 생성됩니다.
Plain Text
[2024-12-22 23:28:50] >>> 프론트 배포 스크립트 시작 <<<
[2024-12-22 23:28:50] green Container down
[2024-12-22 23:29:03] green Container up
[2024-12-22 23:29:11] green Container Health Check
[2024-12-22 23:29:11] blue Container down
[2024-12-22 23:29:22] blue Container up
[2024-12-22 23:29:28] blue Container Health Check
[2024-12-22 23:29:28] >>> 프론트 배포 스크립트 종료 <<<
이렇게 해서 프론트 배포 구성이 완료되었습니다.