name: Deploy KCG on: push: branches: [main] jobs: deploy: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 # ═══ Frontend ═══ - name: Configure npm registry run: | echo "registry=https://nexus.gc-si.dev/repository/npm-public/" > frontend/.npmrc echo "//nexus.gc-si.dev/repository/npm-public/:_auth=${{ secrets.NEXUS_NPM_AUTH }}" >> frontend/.npmrc - name: Build frontend working-directory: frontend env: VITE_GOOGLE_CLIENT_ID: ${{ secrets.GOOGLE_CLIENT_ID }} run: | npm ci npx vite build - name: Deploy frontend run: | rm -rf /deploy/kcg/* cp -r frontend/dist/* /deploy/kcg/ echo "Frontend deployed at $(date '+%Y-%m-%d %H:%M:%S')" # ═══ Backend ═══ - name: Install JDK 21 + Maven run: | apt-get update -qq apt-get install -y -qq wget apt-transport-https gpg maven openssh-client > /dev/null 2>&1 wget -qO - https://packages.adoptium.net/artifactory/api/gpg/key/public | gpg --dearmor -o /usr/share/keyrings/adoptium.gpg echo "deb [signed-by=/usr/share/keyrings/adoptium.gpg] https://packages.adoptium.net/artifactory/deb bookworm main" > /etc/apt/sources.list.d/adoptium.list apt-get update -qq apt-get install -y -qq temurin-21-jdk > /dev/null 2>&1 echo "JAVA_HOME=/usr/lib/jvm/temurin-21-jdk-amd64" >> $GITHUB_ENV echo "/usr/lib/jvm/temurin-21-jdk-amd64/bin" >> $GITHUB_PATH /usr/lib/jvm/temurin-21-jdk-amd64/bin/java -version mvn --version - name: Build backend working-directory: backend run: mvn -B clean package -DskipTests - name: Deploy backend files env: GOOGLE_CLIENT_ID: ${{ secrets.GOOGLE_CLIENT_ID }} JWT_SECRET: ${{ secrets.JWT_SECRET }} DB_PASSWORD: ${{ secrets.DB_PASSWORD }} OPENSKY_CLIENT_ID: ${{ secrets.OPENSKY_CLIENT_ID }} OPENSKY_CLIENT_SECRET: ${{ secrets.OPENSKY_CLIENT_SECRET }} run: | DEPLOY_DIR=/deploy/kcg-backend mkdir -p $DEPLOY_DIR/backup # JAR 백업 (최근 5개 유지) if [ -f $DEPLOY_DIR/kcg.jar ]; then cp $DEPLOY_DIR/kcg.jar $DEPLOY_DIR/backup/kcg-$(date +%Y%m%d%H%M%S).jar ls -t $DEPLOY_DIR/backup/*.jar | tail -n +6 | xargs -r rm fi # Secrets → 환경변수 파일 : > $DEPLOY_DIR/.env [ -n "$GOOGLE_CLIENT_ID" ] && echo "GOOGLE_CLIENT_ID=${GOOGLE_CLIENT_ID}" >> $DEPLOY_DIR/.env [ -n "$JWT_SECRET" ] && echo "JWT_SECRET=${JWT_SECRET}" >> $DEPLOY_DIR/.env [ -n "$DB_PASSWORD" ] && echo "DB_PASSWORD=${DB_PASSWORD}" >> $DEPLOY_DIR/.env [ -n "$OPENSKY_CLIENT_ID" ] && echo "OPENSKY_CLIENT_ID=${OPENSKY_CLIENT_ID}" >> $DEPLOY_DIR/.env [ -n "$OPENSKY_CLIENT_SECRET" ] && echo "OPENSKY_CLIENT_SECRET=${OPENSKY_CLIENT_SECRET}" >> $DEPLOY_DIR/.env echo "PREDICTION_BASE_URL=http://192.168.1.18:8001" >> $DEPLOY_DIR/.env # JAR 내부에 application-prod.yml이 있으면 외부 파일 제거 if unzip -l backend/target/kcg.jar | grep -q 'application-prod.yml$'; then rm -f $DEPLOY_DIR/application-prod.yml echo "JAR 내부 application-prod.yml 감지 → 외부 파일 제거" fi # systemd 서비스 파일 배포 cp deploy/kcg-backend.service $DEPLOY_DIR/kcg-backend.service # JAR 교체 cp backend/target/kcg.jar $DEPLOY_DIR/kcg.jar echo "Backend files deployed at $(date '+%Y-%m-%d %H:%M:%S')" - name: Restart backend via SSH env: DEPLOY_KEY: ${{ secrets.DEPLOY_SSH_KEY }} DEPLOY_HOST: 192.168.1.20 run: | mkdir -p ~/.ssh printf '%s\n' "$DEPLOY_KEY" > ~/.ssh/id_deploy chmod 600 ~/.ssh/id_deploy ssh-keyscan -T 5 $DEPLOY_HOST >> ~/.ssh/known_hosts 2>/dev/null || true SSH_OPTS="-i ~/.ssh/id_deploy -o StrictHostKeyChecking=no -o ConnectTimeout=10 -o ServerAliveInterval=15" # 재시작 스크립트를 SCP로 업로드 후 SSH로 실행 (각각 재시도) cat > /tmp/restart-kcg.sh << 'SCRIPT' #!/bin/bash DEPLOY_DIR=/devdata/services/kcg/backend SYSTEMD_DIR=/etc/systemd/system if [ -f "$DEPLOY_DIR/kcg-backend.service" ] && ! diff -q "$DEPLOY_DIR/kcg-backend.service" "$SYSTEMD_DIR/kcg-backend.service" >/dev/null 2>&1; then cp "$DEPLOY_DIR/kcg-backend.service" "$SYSTEMD_DIR/kcg-backend.service" systemctl daemon-reload fi echo "--- Restarting kcg-backend ---" systemctl restart kcg-backend for i in $(seq 1 60); do HTTP=$(curl -s -o /dev/null -w '%{http_code}' http://localhost:8080/api/aircraft 2>/dev/null || echo "000") if [ "$HTTP" = "200" ] || [ "$HTTP" = "401" ] || [ "$HTTP" = "403" ]; then echo "Backend started successfully (${i}s, HTTP $HTTP)" exit 0 fi sleep 1 done echo "WARNING: Startup timeout" journalctl -u kcg-backend --no-pager -n 10 exit 1 SCRIPT # SCP 업로드 (최대 3회 재시도) for attempt in 1 2 3; do echo "SCP upload attempt $attempt/3..." if scp $SSH_OPTS /tmp/restart-kcg.sh root@$DEPLOY_HOST:/tmp/restart-kcg.sh; then break fi [ "$attempt" -eq 3 ] && { echo "ERROR: SCP failed after 3 attempts"; exit 1; } sleep 10 done # SSH 실행 (최대 3회 재시도) for attempt in 1 2 3; do echo "SSH execute attempt $attempt/3..." if ssh $SSH_OPTS root@$DEPLOY_HOST "bash /tmp/restart-kcg.sh && rm -f /tmp/restart-kcg.sh"; then exit 0 fi SSH_EXIT=$? [ "$attempt" -eq 3 ] && { echo "ERROR: SSH failed after 3 attempts (exit $SSH_EXIT)"; exit 1; } echo "SSH failed (exit $SSH_EXIT), retrying in 10s..." sleep 10 done # ═══ Prediction (FastAPI → redis-211) ═══ - name: Deploy prediction via SSH env: DEPLOY_KEY: ${{ secrets.DEPLOY_SSH_KEY }} PRED_HOST: 192.168.1.18 PRED_PORT: 32023 run: | mkdir -p ~/.ssh printf '%s\n' "$DEPLOY_KEY" > ~/.ssh/id_deploy chmod 600 ~/.ssh/id_deploy SSH_OPTS="-i ~/.ssh/id_deploy -o StrictHostKeyChecking=no -o ConnectTimeout=10 -o ServerAliveInterval=15 -p $PRED_PORT" SCP_OPTS="-i ~/.ssh/id_deploy -o StrictHostKeyChecking=no -P $PRED_PORT" REMOTE_DIR=/home/apps/kcg-prediction # 코드 전송 (rsync 대체: tar + scp) tar czf /tmp/prediction.tar.gz -C prediction --exclude='__pycache__' --exclude='venv' --exclude='.env' . for attempt in 1 2 3; do echo "SCP prediction attempt $attempt/3..." if scp $SCP_OPTS /tmp/prediction.tar.gz root@$PRED_HOST:/tmp/prediction.tar.gz; then break; fi [ "$attempt" -eq 3 ] && { echo "ERROR: SCP failed"; exit 1; } sleep 10 done # systemd 서비스 파일 전송 scp $SCP_OPTS deploy/kcg-prediction.service root@$PRED_HOST:/tmp/kcg-prediction.service # 원격 설치 + 재시작 for attempt in 1 2 3; do echo "SSH deploy attempt $attempt/3..." if ssh $SSH_OPTS root@$PRED_HOST bash -s << 'SCRIPT' set -e REMOTE_DIR=/home/apps/kcg-prediction mkdir -p $REMOTE_DIR cd $REMOTE_DIR # 코드 배포 tar xzf /tmp/prediction.tar.gz -C $REMOTE_DIR rm -f /tmp/prediction.tar.gz # venv + 의존성 python3 -m venv venv 2>/dev/null || true venv/bin/pip install -r requirements.txt -q # systemd 서비스 갱신 if ! diff -q /tmp/kcg-prediction.service /etc/systemd/system/kcg-prediction.service >/dev/null 2>&1; then cp /tmp/kcg-prediction.service /etc/systemd/system/kcg-prediction.service systemctl daemon-reload systemctl enable kcg-prediction fi rm -f /tmp/kcg-prediction.service # 재시작 systemctl restart kcg-prediction # health 확인 (30초) for i in $(seq 1 6); do if curl -sf http://localhost:8001/health > /dev/null 2>&1; then echo "Prediction healthy (${i})" exit 0 fi sleep 5 done echo "WARNING: Prediction health timeout" journalctl -u kcg-prediction --no-pager -n 10 exit 1 SCRIPT then exit 0; fi [ "$attempt" -eq 3 ] && { echo "ERROR: SSH failed"; exit 1; } sleep 10 done - name: Cleanup if: always() run: rm -f ~/.ssh/id_deploy