kintex/deploy/deploy_kintex.sh
zio 63386b62aa fix(devops): 백엔드 미배포 근본수정 — deploy_kintex.sh 단일 빌드 진실원 + SIGPIPE 검증 버그
증상: push 후 프론트만 배포, 백엔드 jar 스테일(V6 고정, V7/V8 미적용).
근본원인 3종:
1) deploy_kintex.sh가 백엔드 빌드 안 함(복사만) → 이제 프론트+백엔드 직접 빌드(단일 진실원)
2) gradlew mode 100644(비실행) → git +x(100755) + sh ./gradlew(실행권한 불요)
3) jar 검증 'unzip -l | grep -q'가 set -o pipefail 하에서 grep 조기종료→unzip SIGPIPE(141)
   →메인클래스 발견에도 오탐 실패→die→jar 미복사. → grep -q 제거(전량 소비) + 선택 tail(파이프 조기종료 회피)

- deploy_server kintex 블록: npm/gradle/verify 개별단계 제거 → git pull → deploy_kintex.sh(빌드+배포) → health
- Jenkinsfile: 동일 스크립트 위임(경로 함정 회피, WORKSPACE 일치)
- 서버 /opt/zioinfo/deploy_server.py 반영·재시작 완료(백업)

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-11 19:44:30 +09:00

93 lines
5.0 KiB
Bash
Executable File

#!/usr/bin/env bash
# KINTEX 배포 스크립트 — deploy_server.py(webhook) 또는 Jenkins가 실행.
# 인자: $1 = 소스 체크아웃 루트(기본 /opt/kintex/src). git pull 이후의 실제 경로와 반드시 일치.
# 동작(단일 진실원): 프론트 빌드 + 백엔드 빌드 → 검증 → 원자적 교체 → 서비스 재기동 → 헬스체크 → 실패 시 롤백.
#
# 재발방지(2026-07-11):
# ① 백엔드 미빌드 버그 — 과거엔 build/libs jar를 "복사만" 했다. 이제 이 스크립트가 직접 bootJar 빌드(단일 진실원).
# ② gradlew 실행권한 — 리포가 mode 100644로 커밋될 수 있어 `sh ./gradlew`로 실행(+x 불요) + chmod 병행.
# ③ jar 검증 SIGPIPE — `unzip -l | grep -q` 는 pipefail 하에서 grep 조기 종료→unzip SIGPIPE(141)→오탐 실패.
# → grep 을 -q 없이 전량 소비(>/dev/null)해 SIGPIPE 회피. jar 선택도 head 대신 tail(파이프 조기종료 방지).
set -euo pipefail
SRC="${1:-/opt/kintex/src}"
WEB_ROOT=/var/www/kintex
APP_JAR=/opt/kintex/app/app.jar
BACKEND_PORT=8021
MAIN_CLASS_PATH='BOOT-INF/classes/com/zioinfo/kintex/KintexApplication.class'
log(){ echo "[deploy-kintex] $*"; }
die(){ echo "[deploy-kintex] ERROR: $*" >&2; exit 1; }
[ -d "$SRC/src/backend" ] || die "소스 경로 불일치: $SRC/src/backend 없음(인자 \$1=$SRC 확인)"
# ── 1. 프론트 빌드(Vite) ──────────────────────────────────────────────────────
log "프론트 빌드 시작"
( cd "$SRC/src/frontend" && (npm ci 2>/dev/null || npm install) && \
NODE_OPTIONS=--max-old-space-size=4096 npm run build ) || die "프론트 빌드 실패"
# ── 2. 백엔드 빌드(bootJar) — `sh ./gradlew`(+x 불요), clean 으로 스테일 jar 제거 ──
log "백엔드 빌드 시작(bootJar)"
sed -i 's/\r$//' "$SRC/src/backend/gradlew" 2>/dev/null || true
chmod +x "$SRC/src/backend/gradlew" 2>/dev/null || true
( cd "$SRC/src/backend" && sh ./gradlew clean bootJar -x test --no-daemon ) \
|| die "백엔드 bootJar 빌드 실패"
# ── 3. 프론트: 완전성 검증 → 스테이징 → 원자적 rename 교체(빈 사이트 구간 제거) ──
DIST="$SRC/src/frontend/dist"
[ -f "$DIST/index.html" ] || die "dist/index.html 없음 — 불완전 빌드, 중단(라이브 무변경)"
[ -d "$DIST/assets" ] || die "dist/assets 없음 — 불완전 빌드, 중단"
mapfile -t REFS < <(grep -oE '/assets/[A-Za-z0-9._-]+\.(js|css)' "$DIST/index.html" | sort -u)
[ "${#REFS[@]}" -gt 0 ] || die "index.html 참조 assets 없음 — 비정상 빌드, 중단"
for ref in "${REFS[@]}"; do
[ -f "${DIST}${ref}" ] || die "참조 자산 누락: ${ref} — 불완전 빌드, 중단"
done
log "프론트 빌드 검증 통과(${#REFS[@]} assets)"
STAGE="${WEB_ROOT}.new.$$"
rm -rf "$STAGE"; mkdir -p "$STAGE"
cp -r "$DIST/." "$STAGE/"
if [ -d "$WEB_ROOT" ] && [ -n "$(ls -A "$WEB_ROOT" 2>/dev/null)" ]; then
tar czf "/var/www/kintex.bak_$(date +%Y%m%d_%H%M%S).tgz" -C "$WEB_ROOT" . || log "WARN: 프론트 백업 실패(계속)"
fi
OLD="${WEB_ROOT}.old.$$"
[ -d "$WEB_ROOT" ] && mv "$WEB_ROOT" "$OLD" || true
mv "$STAGE" "$WEB_ROOT"
rm -rf "$OLD"
log "프론트 배포 완료 → $WEB_ROOT"
# ── 4. 백엔드 jar: 선택(tail — SIGPIPE 회피) → 검증(grep 전량소비 — SIGPIPE 회피) → 교체 ──
JAR=$(ls -1 "$SRC"/src/backend/build/libs/kintex-backend-*.jar 2>/dev/null | grep -v plain | tail -1 || true)
[ -n "$JAR" ] || die "bootJar 없음($SRC/src/backend/build/libs) — 빌드 산출물 확인"
# grep 을 -q 없이 실행해 unzip 출력을 끝까지 소비(pipefail+SIGPIPE 오탐 방지). 발견 시 rc=0.
if ! unzip -l "$JAR" | grep -F "$MAIN_CLASS_PATH" >/dev/null; then
die "유효하지 않은 jar(메인 클래스 없음: $JAR) — 배포 중단"
fi
[ -f "$APP_JAR" ] && cp "$APP_JAR" "${APP_JAR}.bak" || true
cp "$JAR" "$APP_JAR"
log "백엔드 jar 교체 → $APP_JAR ($(basename "$JAR"), $(stat -c%s "$JAR") bytes)"
# ── 5. 워커 의존성 갱신(멱등) ──
if [ -x /opt/kintex/worker/venv/bin/pip ]; then
/opt/kintex/worker/venv/bin/pip install -q redis Pillow google-genai 2>/dev/null || log "WARN: 워커 의존성 갱신 스킵"
fi
# ── 6. 서비스 재기동 ──
systemctl restart kintex.service
systemctl restart kintex-nanobanana.service 2>/dev/null || log "WARN: 워커 재기동 스킵"
# ── 7. 헬스체크(/health 200) — 실패 시 jar 롤백 ──
ok=0
for i in $(seq 1 20); do
sleep 3
code=$(curl -s -m 5 -o /dev/null -w '%{http_code}' "http://127.0.0.1:${BACKEND_PORT}/health" || true)
if [ "$code" = "200" ]; then log "헬스체크 OK ($code)"; ok=1; break; fi
done
if [ "$ok" != "1" ]; then
log "헬스체크 실패 — jar 롤백 시도"
if [ -f "${APP_JAR}.bak" ]; then cp "${APP_JAR}.bak" "$APP_JAR"; systemctl restart kintex.service; fi
systemctl is-active kintex.service || true
die "배포 헬스체크 실패(롤백 수행)"
fi
log "배포 완료 — 백엔드 8021 UP(jar 갱신), 프론트 배포, 워커 재기동"