diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..df34ff6 --- /dev/null +++ b/.gitattributes @@ -0,0 +1,8 @@ +# 배포/CI 파일은 리눅스 서버에서 그대로 실행되므로 개행을 LF로 고정한다. +# (CRLF로 커밋되면 서버에서 "bad interpreter: /bin/bash^M" 로 죽는다) +# 나머지 소스는 기존 방식 그대로 두기 위해 경로를 한정한다. +deploy/dev/*.sh text eol=lf +deploy/dev/*.service text eol=lf +deploy/dev/*.yaml text eol=lf +deploy/dev/*.conf text eol=lf +.gitea/workflows/*.yml text eol=lf diff --git a/.gitea/workflows/deploy-dev.yml b/.gitea/workflows/deploy-dev.yml new file mode 100644 index 0000000..b761d4d --- /dev/null +++ b/.gitea/workflows/deploy-dev.yml @@ -0,0 +1,118 @@ +# arko_renual — 개발서버(csv.zioinfo.co.kr) 자동 배포 파이프라인 +# +# ┌ 설계 배경 ─────────────────────────────────────────────────────────────┐ +# │ 이 서버(zio-server)는 2코어 / 메모리 여유 1G에 40개 이상의 서비스가 │ +# │ 동거한다. 실제로 구축 중 39분간 응답 불능 후 재부팅된 이력이 있다. │ +# │ 그래서 Maven 빌드(힙 1G)는 서버에서 돌리지 않는다. │ +# └────────────────────────────────────────────────────────────────────────┘ +# +# [개발자 PC] bash deploy/dev/build-and-upload.sh → 빌드 + WAR 업로드 +# [개발자 PC] git push origin master → 이 워크플로 자동 실행 +# [서버] ROOT 재전개 + 환경설정 주입 + 재기동 + 헬스체크(+실패 시 롤백) +# +# 수동 실행(workflow_dispatch)으로 재배포·롤백도 가능하다. +name: deploy-dev + +on: + push: + branches: [master] + paths-ignore: # 문서만 바뀐 커밋으로 재배포하지 않는다 + - '**.md' + - '**.docx' + - '**.pptx' + - 'docs/**' + - '_workspace_privacy/**' + workflow_dispatch: + inputs: + action: + description: '수행할 작업' + required: true + default: 'deploy' + type: choice + options: + - deploy + - rollback + +concurrency: + group: arko-dev-deploy + cancel-in-progress: false # 배포 도중 취소되면 ROOT가 반쪽 상태로 남는다 + +env: + APP_HOME: /opt/arko-dev + INCOMING_WAR: /opt/arko-dev/incoming/arko.war + SITE_URL: https://csv.zioinfo.co.kr/ + +jobs: + deploy: + runs-on: arko-dev + timeout-minutes: 20 + + steps: + - name: 사전 점검 + run: | + set -e + echo "트리거: ${{ gitea.event_name }} / 작업: ${{ inputs.action || 'deploy' }}" + df -h "$APP_HOME" | tail -1 + free -h | head -2 + uptime + + - name: WAR 확인 + if: ${{ inputs.action != 'rollback' }} + run: | + set -e + if [ ! -f "$INCOMING_WAR" ]; then + echo "::error::업로드된 WAR이 없다: $INCOMING_WAR" + echo "개발자 PC에서 'bash deploy/dev/build-and-upload.sh' 를 먼저 실행할 것." + exit 1 + fi + ls -lh "$INCOMING_WAR" + WAR_EPOCH=$(stat -c %Y "$INCOMING_WAR") + echo "WAR 빌드 시각: $(date -d @$WAR_EPOCH '+%F %T')" + + # WAR이 이번 커밋보다 오래되었으면 옛 산출물을 배포하는 것이다. + # 문서/CI 파일만 바뀐 커밋도 있으므로 실패시키지 않고 경고만 남긴다. + COMMIT_EPOCH=$(git log -1 --format=%ct 2>/dev/null || echo 0) + if [ "$COMMIT_EPOCH" -gt "$WAR_EPOCH" ]; then + AGE_MIN=$(( (COMMIT_EPOCH - WAR_EPOCH) / 60 )) + echo "::warning::업로드된 WAR이 이번 커밋보다 ${AGE_MIN}분 오래됐다. 소스 변경이 포함된 커밋이라면 build-and-upload.sh 로 다시 올릴 것." + fi + + - name: 배포 + if: ${{ inputs.action != 'rollback' }} + run: | + set -e + bash "$APP_HOME/bin/arko-dev-deploy.sh" "$INCOMING_WAR" + + - name: 롤백 + if: ${{ inputs.action == 'rollback' }} + run: | + set -e + LATEST=$(ls -1t "$APP_HOME"/backup/ROOT_*.tar.gz 2>/dev/null | head -1 || true) + if [ -z "$LATEST" ]; then + echo "::error::롤백할 백업이 없다" + exit 1 + fi + echo "롤백 대상: $LATEST" + systemctl stop arko-dev + rm -rf "$APP_HOME/tomcat/webapps/ROOT" + tar -xzf "$LATEST" -C "$APP_HOME/tomcat/webapps" + systemctl start arko-dev + + - name: 결과 확인 + run: | + set -e + CODE=000 + for i in $(seq 1 60); do + CODE=$(curl -s -o /dev/null -w "%{http_code}" --max-time 10 http://127.0.0.1:8088/ || echo 000) + if [ "$CODE" = "200" ] || [ "$CODE" = "302" ]; then break; fi + sleep 3 + done + echo "Tomcat 직접 → HTTP $CODE" + + EXT=$(curl -s -o /dev/null -w "%{http_code}" --max-time 20 "$SITE_URL" || echo 000) + echo "$SITE_URL → HTTP $EXT" + + case "$CODE" in + 200|302) echo "배포 정상" ;; + *) echo "::error::Tomcat 응답 없음 (HTTP $CODE)"; exit 1 ;; + esac diff --git a/deploy/dev/00-server-setup.sh b/deploy/dev/00-server-setup.sh new file mode 100644 index 0000000..3d503ae --- /dev/null +++ b/deploy/dev/00-server-setup.sh @@ -0,0 +1,197 @@ +#!/usr/bin/env bash +# arko_renual 개발서버 최초 1회 구축 스크립트 (zio-server / 101.79.17.164) +# +# 실행: sudo bash 00-server-setup.sh +# 멱등: 이미 설치된 항목은 건너뛴다. 여러 번 실행해도 안전. +# +# 이 서버는 이미 40개 이상의 서비스가 돌고 있고 디스크가 99% 찬 상태다. +# 그래서 아래 순서로만 진행하고, 각 단계에서 실패하면 즉시 멈춘다. +# +# 1) 사전 점검(디스크/메모리) — 여유가 없으면 중단 +# 2) JDK 8 설치 (Temurin 8 — arko는 source/target 1.7이라 17/21로는 컴파일 불가) +# 3) Tomcat 9 설치 (HTTP 8088, AJP 비활성, 셧다운 8105) +# 4) 디렉토리/환경설정 뼈대 +# 5) systemd 서비스 등록 +# 6) nginx 가상호스트 등록 +# 7) Gitea Actions runner(act_runner) 설치 — 등록 토큰은 수동 입력 +# +# 주의: DB(csv 스키마)는 이미 서버 MariaDB(127.0.0.1:3306)에 존재하므로 이 스크립트는 건드리지 않는다. +set -euo pipefail + +APP_HOME=/opt/arko-dev +JDK_DIR=$APP_HOME/jdk8 +TOMCAT_DIR=$APP_HOME/tomcat +TOMCAT_VER=9.0.120 +HTTP_PORT=8088 +SHUTDOWN_PORT=8105 +DOMAIN=csv.zioinfo.co.kr +SRC_DIR=$(cd "$(dirname "$0")" && pwd) + +log() { echo -e "\n[$(date +'%F %T')] === $* ==="; } + +# ------------------------------------------------------------ 1. 사전 점검 +log "1. 사전 점검" +FREE_MB=$(df -m / | awk 'NR==2 {print $4}') +AVAIL_MB=$(free -m | awk '/^Mem:/ {print $7}') +echo "디스크 여유: ${FREE_MB}MB / 메모리 여유: ${AVAIL_MB}MB" +if [ "$FREE_MB" -lt 2000 ]; then + echo "중단: 디스크 여유 ${FREE_MB}MB. JDK8(180MB)+Tomcat(20MB)+WAR(200MB)+전개본(200MB)+백업(400MB) ≈ 1GB에 여유분까지 최소 2GB 필요." + echo " 정리 후보: journalctl --vacuum-time=7d / /var/log 오래된 로그 / /root/.m2 미사용 아티팩트" + exit 1 +fi +if [ "$AVAIL_MB" -lt 1200 ]; then + echo "경고: 메모리 여유 ${AVAIL_MB}MB. Tomcat 힙 768m + Maven 빌드 1g가 동시에 뜨면 OOM 위험." + echo " 빌드와 기동이 겹치지 않게 하거나 스왑을 확보할 것." +fi + +# ------------------------------------------------------------ 2. JDK 8 +log "2. JDK 8 설치" +if [ -x "$JDK_DIR/bin/javac" ]; then + echo "이미 설치됨: $("$JDK_DIR/bin/java" -version 2>&1 | head -1)" +else + mkdir -p "$JDK_DIR" + # Ubuntu 24.04 공식 저장소에는 openjdk-8이 없다(noble에서 제거). Temurin 8 tarball을 직접 받는다. + TEMURIN_URL="https://api.adoptium.net/v3/binary/latest/8/ga/linux/x64/jdk/hotspot/normal/eclipse" + echo "다운로드: $TEMURIN_URL" + curl -fsSL "$TEMURIN_URL" -o /tmp/temurin8.tar.gz + tar -xzf /tmp/temurin8.tar.gz -C "$JDK_DIR" --strip-components=1 + rm -f /tmp/temurin8.tar.gz + "$JDK_DIR/bin/java" -version +fi + +# ------------------------------------------------------------ 3. Tomcat 9 +log "3. Tomcat 9 설치" +if [ -x "$TOMCAT_DIR/bin/catalina.sh" ]; then + echo "이미 설치됨: $TOMCAT_DIR" +else + mkdir -p "$TOMCAT_DIR" + TC_URL="https://archive.apache.org/dist/tomcat/tomcat-9/v${TOMCAT_VER}/bin/apache-tomcat-${TOMCAT_VER}.tar.gz" + echo "다운로드: $TC_URL" + curl -fsSL "$TC_URL" -o /tmp/tomcat9.tar.gz + tar -xzf /tmp/tomcat9.tar.gz -C "$TOMCAT_DIR" --strip-components=1 + rm -f /tmp/tomcat9.tar.gz + chmod +x "$TOMCAT_DIR"/bin/*.sh + # 기본 안내 웹앱 제거 — 개발서버라도 외부 노출되므로 남기지 않는다 + rm -rf "$TOMCAT_DIR"/webapps/{ROOT,docs,examples,host-manager,manager} +fi + +log "3-1. server.xml 포트 조정 (HTTP $HTTP_PORT / 셧다운 $SHUTDOWN_PORT / AJP 비활성)" +SERVER_XML=$TOMCAT_DIR/conf/server.xml +if ! grep -q "port=\"$HTTP_PORT\"" "$SERVER_XML"; then + cp -n "$SERVER_XML" "$SERVER_XML.orig" + sed -i "s/port=\"8005\" shutdown=\"SHUTDOWN\"/port=\"$SHUTDOWN_PORT\" shutdown=\"SHUTDOWN\"/" "$SERVER_XML" + sed -i "s/port=\"8080\" protocol=\"HTTP\/1.1\"/port=\"$HTTP_PORT\" protocol=\"HTTP\/1.1\" address=\"127.0.0.1\" URIEncoding=\"UTF-8\" maxThreads=\"100\" compression=\"on\"/" "$SERVER_XML" + # AJP 커넥터는 Tomcat 9 기본 배포본이 이미 주석 처리해 두므로 건드리지 않는다. + # (한 번 더