#!/usr/bin/env bash # ============================================================ # WBX Spring Core — 프로덕션 배포 스크립트 (Linux) # 사용법: sudo ./scripts/deploy-prod.sh # ============================================================ set -euo pipefail # ---------- 색상 ---------- RED='\033[0;31m'; GREEN='\033[0;32m'; YELLOW='\033[1;33m'; CYAN='\033[0;36m'; NC='\033[0m' ok() { echo -e " ${GREEN}[OK]${NC} $1"; } warn() { echo -e " ${YELLOW}[WARN]${NC} $1"; } fail() { echo -e " ${RED}[FAIL]${NC} $1"; exit 1; } info() { echo -e " ${CYAN}[INFO]${NC} $1"; } APP_DIR="/opt/wbx-app" SERVICE_USER="wbxapp" SERVICE_NAME="wbx-app" JAR_NAME="app.jar" echo "" echo "==========================================" echo " WBX Spring Core — 프로덕션 배포" echo "==========================================" echo "" # ---------- Root 권한 ---------- if [ "$EUID" -ne 0 ]; then fail "root 권한이 필요합니다: sudo $0" fi # ---------- 1. 서비스 계정 ---------- echo "1. 서비스 계정 (${SERVICE_USER})" if id "$SERVICE_USER" &>/dev/null; then ok "이미 존재" else useradd -r -m -s /bin/bash "$SERVICE_USER" ok "생성 완료" fi # ---------- 2. 타임존/로케일 ---------- echo "2. 시스템 설정" timedatectl set-timezone Asia/Seoul 2>/dev/null && ok "타임존: Asia/Seoul" || warn "타임존 설정 실패 — 수동 확인" # ---------- 3. 디렉토리 ---------- echo "3. 디렉토리 구조" mkdir -p "${APP_DIR}"/{logs,uploads,backup} chown -R "${SERVICE_USER}:${SERVICE_USER}" "${APP_DIR}" ok "${APP_DIR}/{logs,uploads,backup}" # ---------- 4. 시스템 리밋 ---------- echo "4. 파일 디스크립터 리밋" LIMITS_FILE="/etc/security/limits.d/${SERVICE_USER}.conf" if [ ! -f "$LIMITS_FILE" ]; then cat > "$LIMITS_FILE" << EOF ${SERVICE_USER} soft nofile 65535 ${SERVICE_USER} hard nofile 65535 EOF ok "${LIMITS_FILE} 생성" else ok "이미 존재" fi # ---------- 5. JAR 복사 ---------- echo "5. JAR 배포" JAR_SOURCE="build/libs/wbx-spring-core-*.jar" FOUND_JAR=$(ls $JAR_SOURCE 2>/dev/null | head -1) if [ -z "$FOUND_JAR" ]; then warn "JAR 파일 없음 — 먼저 ./gradlew bootJar 실행 필요" else # 이전 버전 백업 if [ -f "${APP_DIR}/${JAR_NAME}" ]; then cp "${APP_DIR}/${JAR_NAME}" "${APP_DIR}/backup/${JAR_NAME}.$(date +%Y%m%d_%H%M%S)" info "이전 JAR 백업 완료" fi cp "$FOUND_JAR" "${APP_DIR}/${JAR_NAME}" chown "${SERVICE_USER}:${SERVICE_USER}" "${APP_DIR}/${JAR_NAME}" ok "$(basename "$FOUND_JAR") → ${APP_DIR}/${JAR_NAME}" fi # ---------- 6. .env 템플릿 ---------- echo "6. 환경변수 파일" if [ ! -f "${APP_DIR}/.env" ]; then cat > "${APP_DIR}/.env" << 'ENVEOF' # ===== WBX Spring Core — 프로덕션 환경변수 ===== SPRING_PROFILES_ACTIVE=prod,mysql SERVER_CONTEXT_PATH=/ # JWT (필수 변경!) JWT_SECRET=your-production-secret-key-minimum-256-bits-long # DB DB_HOST=localhost DB_PORT=3306 DB_NAME=wbx_spring DB_USER=wbxapp DB_PASS=StrongP@ss # Redis SPRING_DATA_REDIS_HOST=localhost # CORS CORS_ORIGINS=https://app.company.com # 로그 LOG_PATH=/opt/wbx-app/logs/app.log ENVEOF chown "${SERVICE_USER}:${SERVICE_USER}" "${APP_DIR}/.env" chmod 600 "${APP_DIR}/.env" ok "${APP_DIR}/.env 생성 (값을 반드시 수정하세요!)" else ok "이미 존재 — 건너뜀" fi # ---------- 7. systemd 서비스 ---------- echo "7. systemd 서비스" SERVICE_FILE="/etc/systemd/system/${SERVICE_NAME}.service" cat > "$SERVICE_FILE" << EOF [Unit] Description=WBX Spring Application After=network.target [Service] Type=simple User=${SERVICE_USER} WorkingDirectory=${APP_DIR} EnvironmentFile=${APP_DIR}/.env ExecStart=/usr/bin/java \\ -XX:+UseG1GC \\ -XX:MaxRAMPercentage=75.0 \\ -Dspring.profiles.active=\${SPRING_PROFILES_ACTIVE} \\ -jar ${APP_DIR}/${JAR_NAME} Restart=always RestartSec=5 StandardOutput=append:${APP_DIR}/logs/app.log StandardError=append:${APP_DIR}/logs/app.log [Install] WantedBy=multi-user.target EOF systemctl daemon-reload systemctl enable "$SERVICE_NAME" --quiet ok "${SERVICE_FILE}" # ---------- 8. 백업 cron ---------- echo "8. DB 백업 cron" BACKUP_SCRIPT="${APP_DIR}/backup/db-backup.sh" if [ ! -f "$BACKUP_SCRIPT" ]; then cat > "$BACKUP_SCRIPT" << 'CRONEOF' #!/usr/bin/env bash # WBX Spring — DB 백업 (crontab: 0 2 * * *) set -euo pipefail source /opt/wbx-app/.env DATE=$(date +%Y%m%d_%H%M%S) BACKUP_DIR="/opt/wbx-app/backup" mysqldump -h"${DB_HOST}" -u"${DB_USER}" -p"${DB_PASS}" "${DB_NAME}" \ | gzip > "${BACKUP_DIR}/${DB_NAME}_${DATE}.sql.gz" # 30일 이상 백업 삭제 find "${BACKUP_DIR}" -name '*.gz' -mtime +30 -delete echo "[$(date)] backup done: ${DB_NAME}_${DATE}.sql.gz" CRONEOF chmod +x "$BACKUP_SCRIPT" chown "${SERVICE_USER}:${SERVICE_USER}" "$BACKUP_SCRIPT" ok "${BACKUP_SCRIPT} 생성" # crontab 등록 CRON_LINE="0 2 * * * ${BACKUP_SCRIPT} >> ${APP_DIR}/logs/backup.log 2>&1" (crontab -u "$SERVICE_USER" -l 2>/dev/null | grep -v "db-backup.sh"; echo "$CRON_LINE") \ | crontab -u "$SERVICE_USER" - ok "crontab 등록 (매일 02:00)" else ok "이미 존재 — 건너뜀" fi # ---------- 9. 방화벽 ---------- echo "9. 방화벽" if command -v firewall-cmd &>/dev/null; then firewall-cmd --permanent --add-service=http --quiet 2>/dev/null || true firewall-cmd --permanent --add-service=https --quiet 2>/dev/null || true firewall-cmd --reload --quiet 2>/dev/null || true ok "HTTP/HTTPS 허용" else warn "firewalld 없음 — 수동으로 방화벽 설정 필요" fi # ---------- 10. SELinux ---------- echo "10. SELinux" if command -v setsebool &>/dev/null && getenforce 2>/dev/null | grep -qi enforcing; then setsebool -P httpd_can_network_connect 1 2>/dev/null ok "httpd_can_network_connect = 1" else info "SELinux 비활성 또는 미설치 — 건너뜀" fi # ---------- 결과 ---------- echo "" echo "==========================================" echo -e " ${GREEN}배포 준비 완료${NC}" echo "" echo " 체크리스트:" echo " [ ] ${APP_DIR}/.env 값 수정 (JWT_SECRET, DB_PASS 필수!)" echo " [ ] DB 생성 + 사용자 권한 부여" echo " [ ] Redis 설치 + bind 127.0.0.1 + requirepass" echo " [ ] JAR 빌드: ./gradlew bootJar" echo " [ ] 서비스 시작: sudo systemctl start ${SERVICE_NAME}" echo " [ ] 확인: curl http://localhost:8080/health" echo " [ ] 리버스 프록시 설정 (Nginx/Caddy)" echo " [ ] SSL 인증서 설치" echo "==========================================" echo ""