# ============================================================ # GUARDiA ITSM 배포 스크립트 (Windows PowerShell) # 실행: .\deploy\08_deploy_guardia.ps1 -ServerIP "xxx.xxx.xxx.xxx" # ============================================================ param( [Parameter(Mandatory=$true)] [string]$ServerIP, [string]$KeyPath = "$env:USERPROFILE\.ssh\zio-server", [string]$User = "ubuntu" ) $GUARDIA = "C:\GUARDiA\itsm" $SSH = "ssh -i `"$KeyPath`" -o StrictHostKeyChecking=no ${User}@${ServerIP}" $SCP = "scp -i `"$KeyPath`" -o StrictHostKeyChecking=no -r" function Log-Section { param($msg) Write-Host "`n`e[36m=== $msg ===`e[0m" } function Log-Info { param($msg) Write-Host "`e[32m[OK]`e[0m $msg" } Log-Section "1. GUARDiA 소스 압축" $zipPath = "$env:TEMP\guardia_deploy.zip" if (Test-Path $zipPath) { Remove-Item $zipPath } # 배포 대상 파일만 포함 (node_modules, __pycache__ 제외) Add-Type -Assembly System.IO.Compression.FileSystem $zip = [System.IO.Compression.ZipFile]::Open($zipPath, 'Create') $exclude = @('__pycache__', '.pyc', 'node_modules', '.git', '*.db', 'uploads', '.env') Get-ChildItem $GUARDIA -Recurse -File | Where-Object { $path = $_.FullName -not ($exclude | Where-Object { $path -like "*$_*" }) } | ForEach-Object { $entryName = $_.FullName.Replace("$GUARDIA\", "").Replace("\", "/") try { [System.IO.Compression.ZipFileExtensions]::CreateEntryFromFile( $zip, $_.FullName, $entryName) | Out-Null } catch {} } $zip.Dispose() Log-Info "소스 압축 완료: $zipPath" Log-Section "2. 서버로 업로드" Invoke-Expression "$SCP `"$zipPath`" ${User}@${ServerIP}:/tmp/guardia_deploy.zip" Log-Info "업로드 완료" Log-Section "3. 서버에서 압축 해제 및 설치" $deployScript = @' set -e echo "[1] 압축 해제" mkdir -p /tmp/guardia_src cd /tmp/guardia_src && unzip -qo /tmp/guardia_deploy.zip echo "[2] 파일 복사" cp -r /tmp/guardia_src/* /opt/guardia/app/ chown -R ubuntu:ubuntu /opt/guardia/app echo "[3] DB 초기화" source /opt/guardia/venv/bin/activate cd /opt/guardia/app # .env 파일이 없으면 기본값으로 생성 if [ ! -f .env ]; then cp /opt/guardia/app/.env.example .env 2>/dev/null || true fi # DB 마이그레이션 python3 db_init.py --force 2>/dev/null || python3 -c " from database import engine, Base from models import * import asyncio async def init(): async with engine.begin() as conn: await conn.run_sync(Base.metadata.create_all) asyncio.run(init()) print('DB 초기화 완료') " 2>/dev/null || echo "DB 초기화 스킵 (이미 존재)" echo "[4] 서비스 재시작" sudo systemctl restart guardia sleep 3 sudo systemctl is-active guardia && echo "GUARDiA 서비스 실행 중" || echo "서비스 시작 실패 — 로그 확인 필요" echo "[5] 정리" rm -rf /tmp/guardia_src /tmp/guardia_deploy.zip echo "배포 완료!" '@ Invoke-Expression "$SSH 'bash -s'" | bash -s <<< "$deployScript" Log-Section "4. 접속 확인" $checkScript = @" echo '--- GUARDiA 서비스 상태 ---' sudo systemctl status guardia --no-pager -l | head -20 echo '' echo '--- 포트 확인 ---' ss -tlnp | grep -E ':8001|:8080|:80' echo '' echo '--- 메모리 사용량 ---' free -h echo '' echo '--- GUARDiA API 헬스체크 ---' sleep 2 curl -s http://localhost:8001/api/admin/health | python3 -m json.tool 2>/dev/null || echo 'API 응답 대기 중...' "@ Invoke-Expression "$SSH '$checkScript'" Write-Host "" Write-Host "`e[32m✅ GUARDiA ITSM 배포 완료!`e[0m" Write-Host "" Write-Host " 홈페이지: http://$ServerIP" Write-Host " GUARDiA ITSM: http://$ServerIP`:8001" Write-Host " 관리자 로그인: admin / admin (최초 접속 후 변경 필수)" Write-Host "" Write-Host "`e[33m보안 권고:`e[0m" Write-Host " 1. 최초 로그인 후 즉시 비밀번호 변경" Write-Host " 2. MFA 활성화: POST /api/auth/mfa/setup" Write-Host " 3. 포트 8001 → Nginx 뒤로 숨기기 (07_nginx_all.sh 실행)"