All checks were successful
deploy-dev / deploy (push) Successful in 39s
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>
130 lines
5.7 KiB
Bash
130 lines
5.7 KiB
Bash
#!/usr/bin/env bash
|
|
# arko_renual 개발서버 배포 스크립트 (csv.zioinfo.co.kr)
|
|
#
|
|
# 서버에 설치 위치: /opt/arko-dev/bin/arko-dev-deploy.sh
|
|
# 호출 주체 : Gitea Actions (.gitea/workflows/deploy-dev.yml) 또는 수동 실행
|
|
#
|
|
# 전제:
|
|
# - JDK 8 : /opt/arko-dev/jdk8 (Temurin 8, 빌드/런타임 공용)
|
|
# - Tomcat: /opt/arko-dev/tomcat (Tomcat 9, HTTP 8088)
|
|
# - 환경설정: /opt/arko-dev/config/globals-dev.properties, globals-live.properties
|
|
# (git에 없는 gitignore 대상 파일 — 서버에만 보관하고 배포 때 주입)
|
|
#
|
|
# 동작:
|
|
# 1) WAR 검증 2) 현재 ROOT 백업(최근 3개 유지) 3) Tomcat 중지
|
|
# 4) ROOT 재전개 5) 서버 보관 globals 주입 6) Tomcat 기동 7) 헬스체크
|
|
#
|
|
# 실패 시 자동 롤백(직전 백업 복구 + 재기동).
|
|
set -euo pipefail
|
|
|
|
APP_HOME=/opt/arko-dev
|
|
TOMCAT_HOME=$APP_HOME/tomcat
|
|
CONFIG_DIR=$APP_HOME/config
|
|
BACKUP_DIR=$APP_HOME/backup
|
|
LOG_DIR=$APP_HOME/logs
|
|
SERVICE=arko-dev
|
|
HEALTH_URL=http://127.0.0.1:8088/
|
|
HEALTH_TIMEOUT=180 # 초. arko는 첫 기동 시 JSP 컴파일로 60~90초 소요
|
|
KEEP_BACKUPS=3
|
|
|
|
WAR_SRC=${1:-}
|
|
TS=$(date +%Y%m%d_%H%M%S)
|
|
|
|
log() { echo "[$(date +'%F %T')] $*" | tee -a "$LOG_DIR/deploy.log"; }
|
|
die() { log "ERROR: $*"; exit 1; }
|
|
|
|
mkdir -p "$BACKUP_DIR" "$LOG_DIR"
|
|
|
|
# ---------------------------------------------------------------- 1. WAR 검증
|
|
[ -n "$WAR_SRC" ] || die "사용법: $0 <arko.war 경로>"
|
|
[ -f "$WAR_SRC" ] || die "WAR 파일 없음: $WAR_SRC"
|
|
# unzip을 두 번 돌리고 grep -q로 파이프하면 set -o pipefail과 맞물려 취약하다
|
|
# (2026-07-30: 정상 WAR인데 Actions 실행 중 "web.xml 없음"으로 오판해 배포가 멈췄다).
|
|
# 목록을 한 번만 읽어 변수에 담고 파이프 없이 검사한다.
|
|
WAR_LIST=$(unzip -l "$WAR_SRC") || die "WAR 파일이 손상됨: $WAR_SRC"
|
|
grep -q "WEB-INF/web.xml" <<< "$WAR_LIST" || die "WEB-INF/web.xml 없음 — 정상 WAR 아님"
|
|
WAR_SIZE=$(du -h "$WAR_SRC" | cut -f1)
|
|
log "배포 시작: $WAR_SRC ($WAR_SIZE)"
|
|
|
|
# 디스크 여유 확인 — 전개본 + 백업까지 고려해 WAR 3배 필요
|
|
NEED_KB=$(( $(du -k "$WAR_SRC" | cut -f1) * 3 ))
|
|
FREE_KB=$(df -k "$APP_HOME" | awk 'NR==2 {print $4}')
|
|
[ "$FREE_KB" -gt "$NEED_KB" ] || die "디스크 부족: 여유 $((FREE_KB/1024))MB, 필요 $((NEED_KB/1024))MB"
|
|
|
|
# ------------------------------------------------------------- 2. 현재본 백업
|
|
if [ -d "$TOMCAT_HOME/webapps/ROOT" ]; then
|
|
log "현재 ROOT 백업 → $BACKUP_DIR/ROOT_$TS.tar.gz"
|
|
tar -czf "$BACKUP_DIR/ROOT_$TS.tar.gz" -C "$TOMCAT_HOME/webapps" ROOT
|
|
# 오래된 백업 정리 (디스크 여유가 빠듯한 서버라 필수)
|
|
ls -1t "$BACKUP_DIR"/ROOT_*.tar.gz 2>/dev/null | tail -n +$((KEEP_BACKUPS+1)) | xargs -r rm -f
|
|
fi
|
|
|
|
# --------------------------------------------------------------- 3. Tomcat 중지
|
|
log "Tomcat 중지"
|
|
systemctl stop "$SERVICE" || true
|
|
for i in $(seq 1 30); do
|
|
pgrep -f "$TOMCAT_HOME" > /dev/null || break
|
|
sleep 1
|
|
done
|
|
pgrep -f "$TOMCAT_HOME" > /dev/null && { log "정상 종료 실패 → 강제 종료"; pkill -9 -f "$TOMCAT_HOME" || true; sleep 2; }
|
|
|
|
# --------------------------------------------------------------- 4. ROOT 재전개
|
|
# arko는 JSP가 리소스를 /new/css/... 처럼 컨텍스트 경로 없이 하드코딩하므로 반드시 ROOT 배포
|
|
log "ROOT 재전개"
|
|
rm -rf "$TOMCAT_HOME/webapps/ROOT" "$TOMCAT_HOME/webapps/ROOT.war"
|
|
rm -rf "$TOMCAT_HOME/work/Catalina/localhost/_" # 이전 JSP 컴파일 캐시 제거
|
|
mkdir -p "$TOMCAT_HOME/webapps/ROOT"
|
|
unzip -q "$WAR_SRC" -d "$TOMCAT_HOME/webapps/ROOT"
|
|
|
|
# ------------------------------------------------- 5. 서버 보관 환경설정 주입
|
|
# globals-{dev,live}.properties는 .gitignore 대상이라 저장소/WAR에 없음.
|
|
# context-datasource.xml은 globals-dev.properties만 읽으므로 이 주입이 없으면 기동 실패.
|
|
PROPS_DEST="$TOMCAT_HOME/webapps/ROOT/WEB-INF/classes/egovframework/egovProps"
|
|
mkdir -p "$PROPS_DEST"
|
|
for f in globals-dev.properties globals-live.properties; do
|
|
[ -f "$CONFIG_DIR/$f" ] || die "서버 환경설정 없음: $CONFIG_DIR/$f"
|
|
cp -f "$CONFIG_DIR/$f" "$PROPS_DEST/$f"
|
|
log "환경설정 주입: $f"
|
|
done
|
|
chmod 640 "$PROPS_DEST"/globals-*.properties
|
|
|
|
# 업로드 파일 보관소는 재배포로 날아가면 안 되므로 심볼릭 링크로 외부 고정
|
|
if [ -d "$APP_HOME/upload" ]; then
|
|
rm -rf "$TOMCAT_HOME/webapps/ROOT/file"
|
|
ln -s "$APP_HOME/upload" "$TOMCAT_HOME/webapps/ROOT/file"
|
|
log "업로드 디렉토리 링크: $APP_HOME/upload"
|
|
fi
|
|
|
|
chown -R arko:arko "$TOMCAT_HOME/webapps/ROOT" 2>/dev/null || true
|
|
|
|
# ------------------------------------------------------------------ 6. 기동
|
|
log "Tomcat 기동"
|
|
systemctl start "$SERVICE"
|
|
|
|
# --------------------------------------------------------------- 7. 헬스체크
|
|
log "헬스체크 (최대 ${HEALTH_TIMEOUT}초 — 첫 요청은 JSP 컴파일로 느림)"
|
|
OK=0
|
|
for i in $(seq 1 "$HEALTH_TIMEOUT"); do
|
|
CODE=$(curl -s -o /dev/null -w "%{http_code}" --max-time 10 "$HEALTH_URL" || echo 000)
|
|
if [ "$CODE" = "200" ] || [ "$CODE" = "302" ]; then OK=1; log "헬스체크 통과 (HTTP $CODE, ${i}초)"; break; fi
|
|
sleep 1
|
|
done
|
|
|
|
if [ "$OK" != "1" ]; then
|
|
log "헬스체크 실패 — 롤백 시작"
|
|
tail -50 "$TOMCAT_HOME/logs/catalina.out" 2>/dev/null | tee -a "$LOG_DIR/deploy.log" || true
|
|
LATEST_BACKUP=$(ls -1t "$BACKUP_DIR"/ROOT_*.tar.gz 2>/dev/null | head -1 || true)
|
|
if [ -n "$LATEST_BACKUP" ]; then
|
|
systemctl stop "$SERVICE" || true
|
|
rm -rf "$TOMCAT_HOME/webapps/ROOT"
|
|
tar -xzf "$LATEST_BACKUP" -C "$TOMCAT_HOME/webapps"
|
|
systemctl start "$SERVICE"
|
|
log "롤백 완료: $LATEST_BACKUP"
|
|
else
|
|
log "롤백할 백업 없음 (최초 배포)"
|
|
fi
|
|
die "배포 실패"
|
|
fi
|
|
|
|
log "배포 완료: $WAR_SRC → ROOT ($WAR_SIZE)"
|