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"
}
}
]
};