BigBang 배포 스크립트(*_deploy.sh) - 100-hours-a-week/2-hertz-wiki GitHub Wiki

📚 목차


🚀 BigBang 배포 스크립트(*_deploy.sh)

1. Frontend (Next.JS)

/home/deploy/fe_deploy.sh

#!/bin/bash
echo "🚀 [프론트엔드 배포 시작] $(date)"

set -e
### 설정
GIT_BRANCH="develop"
APP_NAME_FE="next-frontend"
DEPLOY_DIR="/home/deploy/2-hertz-fe"
CURRENT_COMMIT_FILE="/home/deploy/commit-hashes/fe-current-commit.txt"
ROLLBACK_COMMIT_FILE="/home/deploy/commit-hashes/fe-deployed-commit.txt"

# 디렉토리 준비
mkdir -p /home/deploy/commit-hashes
cd $DEPLOY_DIR
git config --global --add safe.directory "$DEPLOY_DIR"


# 롤백 분기
if [ "$1" == "--rollback" ]; then
  if [ -f "$ROLLBACK_COMMIT_FILE" ]; then
    ROLLBACK_COMMIT=$(cat "$ROLLBACK_COMMIT_FILE")
    echo "🔙 롤백 실행: $ROLLBACK_COMMIT"
    git switch --detach "$ROLLBACK_COMMIT" || {
      echo "❌ git switch 실패: $ROLLBACK_COMMIT"
      exit 1
    }
  else
    echo "❌ 롤백 실패: $ROLLBACK_COMMIT_FILE 없음"
    exit 1
  fi
else
  echo "📦 최신 코드 pull"
  git fetch origin
  git checkout "$GIT_BRANCH"
  git reset --hard "origin/$GIT_BRANCH"  # 로컬 브랜치를 원격 상태로 강제 초기화
fi

# 현재 커밋 해시 저장
CURRENT_COMMIT=$(git rev-parse HEAD)
echo "$CURRENT_COMMIT" > "$CURRENT_COMMIT_FILE"
echo "📌 현재 커밋 기록됨: $CURRENT_COMMIT"

### 빌드
echo "🔨 npm install / build 시작..."

LAST_LOCKFILE=".last_package_lock"
BUILD_HASH_FILE=".build_hash"

### npm install 캐시 조건
if [ ! -d node_modules ] || ! cmp -s package-lock.json "$LAST_LOCKFILE" 2>/dev/null; then
  echo "🔨 npm install 수행 (node_modules 없음 또는 package-lock.json 변경)"
  npm install || exit 1
  cp package-lock.json "$LAST_LOCKFILE"
else
  echo "✅ npm install 생략 (package-lock.json 변경 없음)"
fi

### 빌드 캐시 로직
NEW_HASH=$(find . -name '*.js' -o -name '*.ts' -o -name '*.json' | sort | xargs sha1sum | sha1sum)

if [ -f "$BUILD_HASH_FILE" ] && grep -q "$NEW_HASH" "$BUILD_HASH_FILE"; then
  echo "✅ 빌드 생략 (소스 변경 없음)"
else
  echo "🔨 npm run build 시작..."
        npm run build || exit 1
        echo "$NEW_HASH" > "$BUILD_HASH_FILE"
fi

echo "♻️ PM2 재시작..."
if pm2 describe "$APP_NAME_FE" > /dev/null; then
  pm2 restart /home/deploy/ecosystem.config.js --only "$APP_NAME_FE" || exit 1
else
  pm2 start /home/deploy/ecosystem.config.js --only "$APP_NAME_FE" || exit 1
fi
# 커밋 정보 저장 (배포 성공 시에만)
if [ "$1" != "--rollback" ]; then
        echo "$CURRENT_COMMIT" > "$ROLLBACK_COMMIT_FILE"
        echo "📝 배포 성공 커밋 저장됨: $CURRENT_COMMIT"
fi

echo "✅ 프론트엔드 배포 완료!"

2. backend (Spring Boot)

/home/deploy/be_deploy.sh

#!/bin/bash
echo "🚀 [백엔드 배포 시작] $(date)"

set -e

### ✅ 설정
DEPLOY_DIR="/home/deploy/2-hertz-be/hertz-be"
BUILD_DIR="$DEPLOY_DIR/build/libs"
GIT_BRANCH="develop"
APP_NAME_BE="spring-backend"

# 커밋 해시 파일
CURRENT_COMMIT_FILE="/home/deploy/commit-hashes/be-current-commit.txt"
DEPLOYED_COMMIT_FILE="/home/deploy/commit-hashes/be-deployed-commit.txt"

# 디렉토리 준비
mkdir -p /home/deploy/commit-hashes
cd "$DEPLOY_DIR"
git config --global --add safe.directory "$DEPLOY_DIR"

# 롤백 분기
if [ "$1" == "--rollback" ]; then
  if [ -f "$DEPLOYED_COMMIT_FILE" ]; then
    ROLLBACK_COMMIT=$(cat "$DEPLOYED_COMMIT_FILE")
    echo "🔙 롤백 실행: $ROLLBACK_COMMIT"
    git switch --detach "$ROLLBACK_COMMIT" || {
      echo "❌ git switch 실패"
      exit 1
    }
  else
    echo "❌ 롤백 실패: $DEPLOYED_COMMIT_FILE 없음"
    exit 1
  fi
else
  echo "📦 최신 코드 pull"
  git fetch origin
  git checkout "$GIT_BRANCH"
  git reset --hard "origin/$GIT_BRANCH"  # 로컬 브랜치를 원격 상태로 강제 초기화
fi

# 현재 커밋 해시 저장
CURRENT_COMMIT=$(git rev-parse HEAD)
echo "$CURRENT_COMMIT" > "$CURRENT_COMMIT_FILE"
echo "📌 현재 커밋 기록됨: $CURRENT_COMMIT"

### 빌드
echo "🔨 Spring Boot 빌드 시작..."
./gradlew bootJar -x test || {
  echo "❌ Gradle 빌드 실패"
  exit 1
}

### JAR 링크 생성
echo "🔗 최신 JAR → app.jar 링크 생성..."
cd "$BUILD_DIR"
LATEST_JAR=$(ls -t *.jar | head -n 1)

if [ -z "$LATEST_JAR" ](/100-hours-a-week/2-hertz-wiki/wiki/--z-"$LATEST_JAR"-); then
  echo "❌ JAR 파일 없음"
  exit 1
fi

rm -f app.jar
ln -s "$LATEST_JAR" app.jar
echo "✅ app.jar → $LATEST_JAR 링크 완료"

### PM2 재시작
echo "♻️ PM2 재시작..."
if pm2 describe "$APP_NAME_BE" > /dev/null; then
  pm2 restart /home/deploy/ecosystem.config.js --only "$APP_NAME_BE" || exit 1
else
  pm2 start /home/deploy/ecosystem.config.js --only "$APP_NAME_BE" || exit 1
fi

### 배포 성공 시 커밋 저장
if [ "$1" != "--rollback" ]; then
  echo "$CURRENT_COMMIT" > "$DEPLOYED_COMMIT_FILE"
  echo "📝 배포 성공 커밋 저장됨: $CURRENT_COMMIT"
fi

echo "✅ 백엔드 배포 완료!"

3. AI (FastAPI, PM2)

#!/bin/bash

set -e

echo "🚀 [AI 서버 배포 시작] $(date)"

### ✅ 설정
APP_NAME_AI="fastapi-ai"
APP_DIR="/home/deploy/2-hertz-ai"
GIT_BRANCH="develop"
VENV_DIR="/home/deploy/venv"
ENV="${ENV:-dev}"

# 커밋 해시 관련
COMMIT_DIR="/home/deploy/commit-hashes"
CURRENT_COMMIT_FILE="$COMMIT_DIR/ai-current-commit.txt"
DEPLOYED_COMMIT_FILE="$COMMIT_DIR/ai-deployed-commit.txt"

# 디렉토리 준비
mkdir -p "$COMMIT_DIR"
cd "$APP_DIR"
git config --global --add safe.directory "$APP_DIR"

# 롤백 분기
if [ "$1" == "--rollback" ]; then
  if [ -f "$DEPLOYED_COMMIT_FILE" ]; then
    ROLLBACK_COMMIT=$(cat "$DEPLOYED_COMMIT_FILE")
    echo "🔙 롤백 실행: $ROLLBACK_COMMIT"
    git switch --detach "$ROLLBACK_COMMIT" || {
      echo "❌ git switch 실패"
      exit 1
    }
  else
    echo "❌ 롤백 실패: $DEPLOYED_COMMIT_FILE 없음"
    exit 1
  fi
else
  echo "📦 최신 코드 pull"
  git fetch origin
  git checkout "$GIT_BRANCH"
  git reset --hard "origin/$GIT_BRANCH"  # 로컬 브랜치를 원격 상태로 강제 초기화
fi

# 현재 커밋 해시 저장
CURRENT_COMMIT=$(git rev-parse HEAD)
echo "$CURRENT_COMMIT" > "$CURRENT_COMMIT_FILE"
echo "📌 현재 커밋 기록됨: $CURRENT_COMMIT"

### 🐍 가상환경 확인 및 활성화
echo "🐍 가상환경 확인 및 활성화..."
if [ ! -d "$VENV_DIR" ]; then
  echo "가상환경이 존재하지 않음, 생성 중..."
  python3 -m venv "$VENV_DIR"
fi

source "$VENV_DIR/bin/activate"

if [ -z "$VIRTUAL_ENV" ](/100-hours-a-week/2-hertz-wiki/wiki/--z-"$VIRTUAL_ENV"-); then
  echo "❌ 가상환경 활성화 실패"
  exit 1
fi

echo "✅ 가상환경 활성화 완료: $VIRTUAL_ENV"

pip install -r requirements.txt || echo "📦 requirements.txt 없음, 건너뜀"
pip install -r requirements-dev.txt || echo "📦 requirements-dev.txt 없음, 건너뜀"

### ♻️ PM2 재시작
if pm2 describe "$APP_NAME_AI" > /dev/null; then
  echo "♻️ PM2 프로세스 재시작 중..."
  pm2 restart /home/deploy/ecosystem.config.js --only "$APP_NAME_AI" || exit 1
else
  echo "🚀 PM2 프로세스 새로 시작..."
  pm2 start /home/deploy/ecosystem.config.js --only "$APP_NAME_AI" || exit 1
fi

### 배포 성공 시 커밋 저장
if [ "$1" != "--rollback" ]; then
  echo "$CURRENT_COMMIT" > "$DEPLOYED_COMMIT_FILE"
  echo "📝 배포 성공 커밋 저장됨: $CURRENT_COMMIT"
fi

echo "✅ AI 서버 배포 완료!"

⚙️ PM2 프로세스 관리 파일

1. Spring Boot + Next.JS

/home/deploy/ecosystem.config.js

module.exports = {
        apps: [
    {
      name: "spring-backend",
      script: "java",
      args: "-jar /home/deploy/2-hertz-be/hertz-be/build/libs/hertz-be-0.0.1-SNAPSHOT.jar",//hertz-be-0.0.1-SNAPSHOT.jar
      cwd: "/home/deploy/2-hertz-be/hertz-be",
      interpreter: "",
      env_file: "/home/deploy/2-hertz-be/hertz-be/.env",  // .env 파일 경로 지정
      env: {
              "JAVA_OPTS": "-Xms512m -Xmx1024m"
      },
      watch: false,  // 백엔드는 코드 변경 watch 필요 없음
      restart_delay: 5000, // 재시작 간격 5초
      max_restarts: 5, // 5번 이상 연속 재시작 실패하면 멈춤
      out_file: "/home/deploy/logs/spring-out.log", // 표준 출력 로그
      error_file: "/home/deploy/logs/spring-err.log", // 에러 로그
    },
    {
      name: "next-frontend",
      script: "npm",
      args: "start",
      cwd: "/home/deploy/2-hertz-fe",
      interpreter: "",
      env: {
        NODE_ENV: "production",
        PORT: 3000
      },
      watch: false,  // Next.js도 배포 후에는 watch 필요 없음
      restart_delay: 5000,
      max_restarts: 5,
      out_file: "/home/deploy/logs/next-out.log",
      error_file: "/home/deploy/logs/next-err.log",
    }
  ]
};

2. FastAPI + ChromaDB

/home/deploy/ecosystem.config.js

module.exports = {
  apps: [
    {
      name: "fastapi-ai",
      script: "/home/deploy/venv/bin/python", // python3 대신 python으로 시도
      args: "-m uvicorn app.main:app --host 0.0.0.0 --port 8000",
      cwd: "/home/deploy/2-hertz-ai",
      // interpreter 항목 제거
      env: {
        ENV: "dev",
        PYTHONIOENCODING: "utf-8", // 인코딩 명시적으로 설정
        PYTHONPATH: "."
      },
      watch: false,
      restart_delay: 5000
    },
    {
      name: "chromadb",
      script: "/home/deploy/venv/bin/chroma",
      args: "run --port 8001 --path /home/deploy/chroma-data",
      interpreter: "none",
      exec_mode: "fork",
      env: {
        ENV: "prod"
      }
    }
  ]
};