arko_renual/deploy/dev/build-and-upload.sh
infraurp e4973fb777
All checks were successful
deploy-dev / deploy (push) Successful in 39s
배포 파이프라인 결함 3건 수정
2026-07-30 나눔고딕 글꼴 배포 중 드러난 문제들이다.

1) build-and-upload.sh: 업로드 검증이 없어 잘린 WAR이 통과했다
   pscp가 대용량 전송 중 약 30초 지점에서 조용히 끊기며 exit 0을 반환해,
   194MB WAR이 66MB/84MB로 잘린 채 "업로드 완료"로 처리됐다. 그 WAR로
   배포가 돌아 실패했다. 30MB 조각 전송 + 조각별 크기 확인(3회 재시도) +
   서버 결합 후 size/md5 대조를 통과할 때만 arko.war 로 교체하도록 바꿨다.

2) build-and-upload.sh: mvn -ntp 는 Maven 3.6.1+ 전용
   PATH에 3.5.4가 잡히면 "Unrecognized option: -ntp"로 빌드가 즉시 죽는다.
   진행률 억제 옵션일 뿐이라 제거했다.

3) arko-dev-deploy.sh: WAR 검증이 파이프+pipefail 조합으로 취약
   정상 WAR인데 Actions 실행 중 "WEB-INF/web.xml 없음"으로 오판해 배포가
   멈췄다(서버에서 동일 명령 재실행 시 20/20 통과). unzip 목록을 한 번만
   읽어 변수에 담고 파이프 없이 검사한다.
   ※ 서버의 /opt/arko-dev/bin/ 사본은 워크플로가 갱신하지 않으므로 별도 반영 필요.

덧붙여 워크플로 주석의 "빈 커밋으로 재배포" 안내를 정정했다 —
paths-ignore 때문에 변경 경로가 없는 커밋은 워크플로가 실행되지 않는다.

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

153 lines
7.0 KiB
Bash

#!/usr/bin/env bash
# arko_renual — 로컬 빌드 → 개발서버 WAR 업로드 (개발자 PC에서 실행)
#
# 표준 흐름:
# 1) bash deploy/dev/build-and-upload.sh 빌드 + WAR 업로드
# 2) git push origin master → Gitea Actions가 자동 배포
#
# 옵션:
# --build-only 빌드만 (업로드 안 함)
# --no-build 업로드만 (기존 target/arko.war 사용)
# --deploy 업로드 후 서버 배포까지 직접 실행 (push 없이 즉시 반영할 때)
#
# 서버(2코어/메모리 여유 1G)에서 Maven을 돌리지 않기 위한 구조다.
# 빌드는 개발자 PC에서, 서버는 전개만 한다.
set -euo pipefail
# ------------------------------------------------------------------ 설정
SERVER_IP=101.79.17.164
SERVER_USER=root
SERVER_PW='1q2w3e!Q'
HOSTKEY="SHA256:Zm5aAv5DJ/EnKiObKSadOxA5nKuXgToCGhNiyhVhAjY"
REMOTE_INCOMING=/opt/arko-dev/incoming
REMOTE_PARTS=/opt/arko-dev/incoming/parts
REMOTE_DEPLOY=/opt/arko-dev/bin/arko-dev-deploy.sh
CHUNK_SIZE=30m # pscp 연결이 30초 부근에서 끊기는 사례가 있어 조각 단위로 올린다
SITE_URL=https://csv.zioinfo.co.kr/
# arko는 maven-compiler-plugin이 source/target 1.7 → JDK 8로만 빌드된다 (17/21 불가)
JDK8_HOME=${JDK8_HOME:-"C:/Program Files/Java/jdk1.8.0_202"}
PROJECT_ROOT=$(cd "$(dirname "$0")/../.." && pwd)
WAR="$PROJECT_ROOT/target/arko.war"
DO_BUILD=1
DO_UPLOAD=1
DO_DEPLOY=0 # 기본은 업로드까지만 — 배포는 git push가 Gitea Actions로 트리거한다
for arg in "$@"; do
case "$arg" in
--build-only) DO_UPLOAD=0 ;;
--no-build) DO_BUILD=0 ;;
--deploy) DO_DEPLOY=1 ;;
*) echo "사용법: $0 [--build-only] [--no-build] [--deploy]"; exit 1 ;;
esac
done
log() { echo -e "\n[$(date +'%F %T')] === $* ==="; }
# PuTTY plink/pscp 사용 (Windows 기본 ssh는 비밀번호 자동입력이 안 된다)
PLINK() { plink -batch -hostkey "$HOSTKEY" -pw "$SERVER_PW" "$SERVER_USER@$SERVER_IP" "$@"; }
PSCP() { pscp -batch -hostkey "$HOSTKEY" -pw "$SERVER_PW" "$@"; }
# ------------------------------------------------------------------ 1. 빌드
if [ "$DO_BUILD" = "1" ]; then
log "1. 로컬 빌드 (JDK 8)"
[ -x "$JDK8_HOME/bin/javac" ] || { echo "JDK 8을 찾을 수 없다: $JDK8_HOME (JDK8_HOME 환경변수로 지정)"; exit 1; }
export JAVA_HOME="$JDK8_HOME"
cd "$PROJECT_ROOT"
# -ntp(--no-transfer-progress)는 Maven 3.6.1+ 전용이다. PATH에 3.5.4가 잡히면
# "Unrecognized option: -ntp"로 즉시 죽으므로 쓰지 않는다.
mvn -B clean package -DskipTests
[ -f "$WAR" ] || { echo "WAR 생성 실패: $WAR"; exit 1; }
# 바이트코드 버전 확인 — 51(0x33)=Java7. JDK 17/21로 잘못 빌드되면 서버에서 못 뜬다
CLS=$(find target/classes -name "*.class" | head -1)
VER=$(od -An -t d1 -j 7 -N 1 "$CLS" | tr -d ' ')
echo "바이트코드 major=$VER (51이어야 정상)"
[ "$VER" = "51" ] || { echo "경고: JDK 8이 아닌 컴파일러로 빌드된 것으로 보인다"; exit 1; }
ls -lh "$WAR"
fi
[ "$DO_UPLOAD" = "1" ] || { log "빌드만 수행하고 종료"; exit 0; }
[ -f "$WAR" ] || { echo "WAR이 없다: $WAR (--no-build 이면 먼저 빌드할 것)"; exit 1; }
# ------------------------------------------------------------- 2. 업로드
log "2. 서버 업로드 ($(du -h "$WAR" | cut -f1))"
PLINK "mkdir -p $REMOTE_INCOMING"
# 서버 디스크 여유 확인 — WAR 3배(업로드+전개+백업)가 필요하다
FREE_MB=$(PLINK "df -m $REMOTE_INCOMING | awk 'NR==2 {print \$4}'" | tr -d '\r')
WAR_MB=$(( $(du -k "$WAR" | cut -f1) / 1024 ))
echo "서버 여유 ${FREE_MB}MB / WAR ${WAR_MB}MB"
[ "$FREE_MB" -gt $(( WAR_MB * 3 )) ] || { echo "서버 디스크 부족"; exit 1; }
# pscp가 대용량 전송 중 약 30초 지점에서 조용히 끊기면서 exit 0을 반환한 사례가 있다
# (2026-07-30: 194MB WAR이 66MB/84MB로 잘린 채 "업로드 완료"로 처리되어 배포가 깨졌다).
# 그래서 ① 조각으로 나눠 올리고 ② 조각마다 크기를 확인하고 ③ 서버에서 합친 뒤
# ④ size+md5가 로컬과 일치할 때만 arko.war 로 교체한다. 검증 실패 시 기존 WAR은 그대로 둔다.
LOCAL_SIZE=$(stat -c %s "$WAR")
LOCAL_MD5=$(md5sum "$WAR" | cut -d' ' -f1)
echo "로컬 size=$LOCAL_SIZE md5=$LOCAL_MD5"
PARTS_DIR="$PROJECT_ROOT/target/warparts"
rm -rf "$PARTS_DIR"; mkdir -p "$PARTS_DIR"
split -b "$CHUNK_SIZE" -d -a 2 "$WAR" "$PARTS_DIR/part_"
PART_CNT=$(ls "$PARTS_DIR" | wc -l)
echo "분할 ${PART_CNT}개 ($CHUNK_SIZE)"
PLINK "rm -rf $REMOTE_PARTS && mkdir -p $REMOTE_PARTS"
IDX=0
for P in "$PARTS_DIR"/part_*; do
IDX=$((IDX+1)); NAME=$(basename "$P"); LSZ=$(stat -c %s "$P"); OK=0
for TRY in 1 2 3; do
PSCP "$P" "$SERVER_USER@$SERVER_IP:$REMOTE_PARTS/$NAME" || true
RSZ=$(PLINK "stat -c %s $REMOTE_PARTS/$NAME 2>/dev/null || echo 0" | tr -d '\r')
if [ "$RSZ" = "$LSZ" ]; then echo " [$IDX/$PART_CNT] $NAME OK ($LSZ bytes)"; OK=1; break; fi
echo " [$IDX/$PART_CNT] $NAME 크기 불일치 (remote=$RSZ local=$LSZ) — 재시도 $TRY/3"
done
[ "$OK" = "1" ] || { echo "조각 전송 3회 실패: $NAME — 중단"; exit 1; }
done
PLINK "cat $REMOTE_PARTS/part_* > $REMOTE_INCOMING/arko.war.uploading && rm -rf $REMOTE_PARTS"
R_SIZE=$(PLINK "stat -c %s $REMOTE_INCOMING/arko.war.uploading" | tr -d '\r')
R_MD5=$(PLINK "md5sum $REMOTE_INCOMING/arko.war.uploading | cut -d' ' -f1" | tr -d '\r')
echo "서버 size=$R_SIZE md5=$R_MD5"
[ "$R_SIZE" = "$LOCAL_SIZE" ] || { echo "업로드 크기 불일치 — arko.war 를 교체하지 않고 중단"; exit 1; }
[ "$R_MD5" = "$LOCAL_MD5" ] || { echo "업로드 md5 불일치 — arko.war 를 교체하지 않고 중단"; exit 1; }
rm -rf "$PARTS_DIR"
PLINK "mv -f $REMOTE_INCOMING/arko.war.uploading $REMOTE_INCOMING/arko.war && ls -lh $REMOTE_INCOMING/arko.war"
# ------------------------------------------------------------- 3. 배포
if [ "$DO_DEPLOY" != "1" ]; then
log "업로드 완료 — 배포는 git push 가 트리거한다"
cat <<EOF
다음 단계:
git push origin master → Gitea Actions(deploy-dev)가 자동 배포
진행 상황: https://git.zioinfo.co.kr/somang4819/arko_renual/actions
push 없이 즉시 반영하려면: $0 --no-build --deploy
EOF
exit 0
fi
log "3. 서버 배포 (--deploy)"
PLINK "bash $REMOTE_DEPLOY $REMOTE_INCOMING/arko.war"
# ------------------------------------------------------------- 4. 확인
log "4. 접속 확인"
LOCAL_CODE=$(PLINK "curl -s -o /dev/null -w '%{http_code}' --max-time 15 http://127.0.0.1:8088/" | tr -d '\r')
echo "Tomcat 직접 → HTTP $LOCAL_CODE"
EXT_CODE=$(curl -s -o /dev/null -w "%{http_code}" --max-time 20 "$SITE_URL" || echo 000)
echo "$SITE_URL → HTTP $EXT_CODE"
case "$LOCAL_CODE" in
200|302) echo -e "\n배포 완료" ;;
*) echo -e "\n배포 실패 — 서버 로그 확인: plink ... 'tail -50 /opt/arko-dev/tomcat/logs/catalina.out'"; exit 1 ;;
esac
if [ "$EXT_CODE" = "000" ]; then
echo "참고: 외부 도메인 미응답 — csv.zioinfo.co.kr A 레코드 등록/SSL 발급 여부를 확인할 것"
fi