[설치 스크립트 수정사항] - PYTHONIOENCODING=utf-8 systemd/NSSM 서비스 환경변수 추가 (Windows cp949 오류 예방) - db_init.py 헬퍼 추가: 스키마 불일치 자동 감지 → 백업 → 재초기화 - 포트 8001 충돌 감지 및 기존 프로세스 자동 종료 로직 추가 - --test 검증 항목 강화: HTTP 응답 + 로그인 API + UTF-8 인코딩 포함 - setup_ubuntu/centos/rhel: PYTHONUNBUFFERED=1 추가 [workspace 자동분석 워크플로우] - workspace/ 디렉토리 생성 (소스코드 투입 위치) - .claude/skills/workspace-analyzer/SKILL.md 스킬 생성 Phase 0~6: 탐색→스택탐지→심층분석→리포트→개발환경가이드→하네스생성→CLAUDE.md - CLAUDE.md에 workspace 워크플로우 안내 등록 Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
264 lines
11 KiB
PowerShell
264 lines
11 KiB
PowerShell
# ==============================================================
|
|
# GUARDiA ITSM 설치 스크립트 — Windows Server 2019/2022
|
|
# ==============================================================
|
|
# 전제조건: 순수 Windows Server OS (PowerShell 5.1+)
|
|
# 실행 방법: PowerShell -ExecutionPolicy Bypass -File setup_windows.ps1
|
|
# 설치 테스트: .\setup_windows.ps1 -Test
|
|
# ==============================================================
|
|
|
|
param(
|
|
[switch]$Test = $false
|
|
)
|
|
|
|
$ErrorActionPreference = "Stop"
|
|
$ScriptDir = Split-Path -Parent $MyInvocation.MyCommand.Path
|
|
$GuardiaRoot = Split-Path -Parent $ScriptDir
|
|
$LogFile = "C:\guardia_install.log"
|
|
|
|
function Write-OK { param($msg) Write-Host "[OK] $msg" -ForegroundColor Green }
|
|
function Write-Warn { param($msg) Write-Host "[WARN] $msg" -ForegroundColor Yellow }
|
|
function Write-Fail { param($msg) Write-Host "[FAIL] $msg" -ForegroundColor Red; exit 1 }
|
|
function Write-Info { param($msg) Write-Host " $msg" }
|
|
|
|
Start-Transcript -Path $LogFile -Append | Out-Null
|
|
|
|
Write-Host "=================================================="
|
|
Write-Host " GUARDiA ITSM 설치 — Windows Server"
|
|
Write-Host " 시작: $(Get-Date)"
|
|
Write-Host "=================================================="
|
|
|
|
# ── 테스트 모드 ─────────────────────────────────────────────
|
|
if ($Test) {
|
|
Write-Host "=== 설치 검증 모드 ==="
|
|
$pass = 0; $fail = 0
|
|
|
|
function Check-Item {
|
|
param($desc, [scriptblock]$cmd)
|
|
try {
|
|
& $cmd | Out-Null
|
|
Write-OK $desc; $script:pass++
|
|
} catch {
|
|
Write-Host "[FAIL] $desc" -ForegroundColor Red; $script:fail++
|
|
}
|
|
}
|
|
|
|
Check-Item "Python 3.11+" { python --version 2>&1 | Select-String "3\.(1[1-9]|[2-9]\d)" }
|
|
Check-Item "PostgreSQL 응답" { pg_isready -q }
|
|
Check-Item "Redis 응답" { redis-cli ping }
|
|
Check-Item "NSSM 서비스 등록" { Get-Service "guardia-itsm" -ErrorAction Stop }
|
|
Check-Item "GUARDiA 서비스 실행" {
|
|
$s = Get-Service "guardia-itsm"
|
|
if ($s.Status -ne "Running") { throw "서비스 상태: $($s.Status)" }
|
|
}
|
|
Check-Item "GUARDiA HTTP 응답" {
|
|
$r = Invoke-WebRequest "http://localhost:8001/" -UseBasicParsing -TimeoutSec 10
|
|
if ($r.StatusCode -ne 200) { throw "HTTP $($r.StatusCode)" }
|
|
}
|
|
Check-Item "GUARDiA 로그인 API" {
|
|
$body = '{"username":"admin","password":"1111"}'
|
|
$r = Invoke-WebRequest "http://localhost:8001/api/auth/login" -Method POST `
|
|
-ContentType "application/json" -Body $body -UseBasicParsing -TimeoutSec 10
|
|
if ($r.StatusCode -ne 200) { throw "로그인 실패 ($($r.StatusCode))" }
|
|
}
|
|
Check-Item "Python UTF-8 인코딩" {
|
|
$env:PYTHONIOENCODING = "utf-8"
|
|
$out = & python -c "print('OK')" 2>&1
|
|
if ("$out" -notmatch "OK") { throw "인코딩 오류: $out" }
|
|
}
|
|
Check-Item "Nginx 설정" { nginx -t 2>&1 }
|
|
|
|
Write-Host ""
|
|
Write-Host "검증 결과: 성공 $pass / 실패 $fail"
|
|
if ($fail -eq 0) {
|
|
Write-OK "모든 검사 통과 — GUARDiA ITSM 정상 설치됨"
|
|
} else {
|
|
Write-Fail "일부 검사 실패 — 로그 확인: $LogFile"
|
|
}
|
|
Stop-Transcript | Out-Null
|
|
exit 0
|
|
}
|
|
|
|
# ── 관리자 권한 확인 ────────────────────────────────────────
|
|
$isAdmin = ([Security.Principal.WindowsPrincipal][Security.Principal.WindowsIdentity]::GetCurrent()).IsInRole(
|
|
[Security.Principal.WindowsBuiltInRole]::Administrator
|
|
)
|
|
if (-not $isAdmin) { Write-Fail "관리자 권한으로 실행하세요 (우클릭 → 관리자로 실행)" }
|
|
|
|
# ── Chocolatey 패키지 관리자 ────────────────────────────────
|
|
Write-Host ""
|
|
Write-Host "[1/8] Chocolatey 패키지 관리자 설치..."
|
|
if (-not (Get-Command choco -ErrorAction SilentlyContinue)) {
|
|
Set-ExecutionPolicy Bypass -Scope Process -Force
|
|
[System.Net.ServicePointManager]::SecurityProtocol = [System.Net.ServicePointManager]::SecurityProtocol -bor 3072
|
|
Invoke-Expression ((New-Object System.Net.WebClient).DownloadString('https://community.chocolatey.org/install.ps1'))
|
|
$env:Path = [System.Environment]::GetEnvironmentVariable("Path","Machine") + ";" + [System.Environment]::GetEnvironmentVariable("Path","User")
|
|
}
|
|
Write-OK "Chocolatey 준비 완료"
|
|
|
|
# ── 1. 필수 패키지 설치 ──────────────────────────────────────
|
|
Write-Host ""
|
|
Write-Host "[2/8] 필수 패키지 설치 (Python, PostgreSQL, Redis, Nginx, NSSM)..."
|
|
|
|
$packages = @(
|
|
"python --version=3.11.9",
|
|
"postgresql",
|
|
"redis-64",
|
|
"nginx-winssl",
|
|
"nssm",
|
|
"git"
|
|
)
|
|
|
|
foreach ($pkg in $packages) {
|
|
$name = $pkg.Split(" ")[0]
|
|
Write-Host " 설치 중: $name"
|
|
choco install $pkg -y --no-progress 2>&1 | Out-Null
|
|
}
|
|
|
|
# PATH 갱신
|
|
$env:Path = [System.Environment]::GetEnvironmentVariable("Path","Machine") + ";" + [System.Environment]::GetEnvironmentVariable("Path","User")
|
|
Write-OK "필수 패키지 설치 완료"
|
|
|
|
# ── 2. Python 가상환경 ──────────────────────────────────────
|
|
Write-Host ""
|
|
Write-Host "[3/8] Python 가상환경 설정..."
|
|
$venvPath = "C:\guardia\venv"
|
|
if (-not (Test-Path $venvPath)) {
|
|
python -m venv $venvPath
|
|
}
|
|
$pip = "$venvPath\Scripts\pip.exe"
|
|
& $pip install --upgrade pip -q
|
|
& $pip install -r "$GuardiaRoot\itsm\requirements.txt" -q
|
|
Write-OK "Python 패키지 설치 완료"
|
|
|
|
# ── 3. PostgreSQL 초기화 ────────────────────────────────────
|
|
Write-Host ""
|
|
Write-Host "[4/8] PostgreSQL 초기화..."
|
|
$pgBin = "C:\Program Files\PostgreSQL\16\bin"
|
|
if (-not (Test-Path $pgBin)) {
|
|
$pgBin = Get-ChildItem "C:\Program Files\PostgreSQL" -Directory | Sort-Object Name -Descending | Select-Object -First 1 | ForEach-Object { "$($_.FullName)\bin" }
|
|
}
|
|
|
|
$env:PGPASSWORD = "postgres"
|
|
# DB/사용자 생성
|
|
& "$pgBin\psql.exe" -U postgres -c "CREATE USER guardia WITH PASSWORD 'guardia_secure_pw';" 2>$null
|
|
& "$pgBin\psql.exe" -U postgres -c "CREATE DATABASE guardia OWNER guardia;" 2>$null
|
|
Write-OK "PostgreSQL 설정 완료"
|
|
|
|
# ── 4. Redis 서비스 등록 ────────────────────────────────────
|
|
Write-Host ""
|
|
Write-Host "[5/8] Redis 서비스 등록..."
|
|
$redisExe = (Get-Command redis-server -ErrorAction SilentlyContinue)?.Source
|
|
if ($redisExe) {
|
|
nssm install redis-server $redisExe 2>$null
|
|
nssm start redis-server 2>$null
|
|
}
|
|
Write-OK "Redis 완료"
|
|
|
|
# ── 5. 환경 설정 파일 ───────────────────────────────────────
|
|
Write-Host ""
|
|
Write-Host "[6/8] 환경 설정 파일 생성..."
|
|
$envFile = "$GuardiaRoot\itsm\.env"
|
|
if (-not (Test-Path $envFile)) {
|
|
@"
|
|
DATABASE_URL=postgresql+asyncpg://guardia:guardia_secure_pw@localhost:5432/guardia
|
|
SECRET_KEY=change_this_secret_key_in_production_min_32chars
|
|
ALGORITHM=HS256
|
|
ACCESS_TOKEN_EXPIRE_MINUTES=480
|
|
REDIS_URL=redis://localhost:6379/0
|
|
OLLAMA_BASE_URL=http://localhost:11434
|
|
GUARDIA_LLM_MODEL=llama3.1:8b
|
|
MESSENGER_BASE_URL=http://localhost:8002
|
|
MESSENGER_OPS_ROOM=ops
|
|
"@ | Out-File -FilePath $envFile -Encoding utf8
|
|
Write-Warn ".env 생성됨 — SECRET_KEY를 변경하세요: $envFile"
|
|
}
|
|
|
|
# ── 6. DB 초기화 (스키마 불일치 자동 감지·복구) ────────────────────────
|
|
Write-Host ""
|
|
Write-Host "[7/8] DB 초기화..."
|
|
|
|
# 포트 8001 기존 프로세스 종료 (업그레이드 시 충돌 방지)
|
|
$portProc = Get-NetTCPConnection -LocalPort 8001 -State Listen -ErrorAction SilentlyContinue | Select-Object -First 1
|
|
if ($portProc) {
|
|
Write-Warn "포트 8001 사용 중 (PID $($portProc.OwningProcess)) — 종료..."
|
|
Stop-Process -Id $portProc.OwningProcess -Force -ErrorAction SilentlyContinue
|
|
Start-Sleep -Seconds 2
|
|
}
|
|
|
|
Push-Location "$GuardiaRoot\itsm"
|
|
$env:PYTHONIOENCODING = "utf-8"
|
|
$env:PYTHONUNBUFFERED = "1"
|
|
$dbResult = & "$venvPath\Scripts\python.exe" tools\db_init.py --force 2>&1
|
|
$dbResult | ForEach-Object { Write-Host " $_" }
|
|
if ($LASTEXITCODE -ne 0) {
|
|
Write-Fail "DB 초기화 실패 — 로그를 확인하세요"
|
|
}
|
|
Pop-Location
|
|
Write-OK "DB 초기화 완료"
|
|
|
|
# ── 7. NSSM Windows 서비스 등록 ─────────────────────────────
|
|
Write-Host ""
|
|
Write-Host "[8/8] Windows 서비스 등록 (NSSM)..."
|
|
|
|
$uvicorn = "$venvPath\Scripts\uvicorn.exe"
|
|
$svcName = "guardia-itsm"
|
|
|
|
# 기존 서비스 중지 및 제거 (업그레이드)
|
|
try { nssm stop $svcName 2>$null; Start-Sleep -Seconds 2 } catch {}
|
|
try { nssm remove $svcName confirm 2>$null } catch {}
|
|
|
|
nssm install $svcName $uvicorn
|
|
nssm set $svcName AppParameters "main:app --host 0.0.0.0 --port 8001 --workers 4"
|
|
nssm set $svcName AppDirectory "$GuardiaRoot\itsm"
|
|
# PYTHONIOENCODING=utf-8 필수 — Windows 기본 cp949에서 한글 print 오류 방지
|
|
nssm set $svcName AppEnvironmentExtra "PATH=$venvPath\Scripts;$env:PATH PYTHONIOENCODING=utf-8 PYTHONUNBUFFERED=1"
|
|
nssm set $svcName Start SERVICE_AUTO_START
|
|
nssm set $svcName AppStdout "C:\guardia\logs\guardia-itsm.log"
|
|
nssm set $svcName AppStderr "C:\guardia\logs\guardia-itsm-err.log"
|
|
|
|
New-Item -ItemType Directory -Force "C:\guardia\logs" | Out-Null
|
|
nssm start $svcName
|
|
Write-OK "Windows 서비스 등록 완료"
|
|
|
|
# ── Nginx 리버스 프록시 ──────────────────────────────────────
|
|
$nginxConf = "C:\tools\nginx-winssl\conf\conf.d\guardia.conf"
|
|
New-Item -ItemType Directory -Force (Split-Path $nginxConf) | Out-Null
|
|
@"
|
|
server {
|
|
listen 80;
|
|
server_name _;
|
|
client_max_body_size 100M;
|
|
|
|
location / {
|
|
proxy_pass http://127.0.0.1:8001;
|
|
proxy_http_version 1.1;
|
|
proxy_set_header Upgrade `$http_upgrade;
|
|
proxy_set_header Connection "upgrade";
|
|
proxy_set_header Host `$host;
|
|
proxy_set_header X-Real-IP `$remote_addr;
|
|
proxy_read_timeout 300s;
|
|
}
|
|
}
|
|
"@ | Out-File -FilePath $nginxConf -Encoding utf8
|
|
|
|
# ── 방화벽 규칙 ──────────────────────────────────────────────
|
|
New-NetFirewallRule -DisplayName "GUARDiA HTTP" -Direction Inbound -Protocol TCP -LocalPort 80 -Action Allow 2>$null
|
|
New-NetFirewallRule -DisplayName "GUARDiA HTTPS" -Direction Inbound -Protocol TCP -LocalPort 443 -Action Allow 2>$null
|
|
Write-OK "방화벽 규칙 추가 완료"
|
|
|
|
Write-Host ""
|
|
Write-Host "=================================================="
|
|
Write-OK "GUARDiA ITSM 설치 완료 — Windows Server"
|
|
Write-Host ""
|
|
Write-Info "접속 URL: http://$(Get-NetIPAddress -AddressFamily IPv4 -InterfaceAlias 'Ethernet*' | Select-Object -First 1 | ForEach-Object { $_.IPAddress })"
|
|
Write-Info "설치 로그: $LogFile"
|
|
Write-Info "서비스 상태: Get-Service guardia-itsm"
|
|
Write-Info "검증: .\setup_windows.ps1 -Test"
|
|
Write-Host ""
|
|
Write-Warn "보안 필수 조치:"
|
|
Write-Warn " 1. $envFile 의 SECRET_KEY 변경"
|
|
Write-Warn " 2. PostgreSQL postgres 계정 비밀번호 변경"
|
|
Write-Host "=================================================="
|
|
|
|
Stop-Transcript | Out-Null
|