149 lines
5.5 KiB
PowerShell
149 lines
5.5 KiB
PowerShell
# boot-public-runtime.ps1
|
|
# 목적: PC 재부팅/재로그온 후 퍼블릭 런타임을 자동으로 복구한다.
|
|
# 순서: Docker Desktop 데몬 대기 -> postgres(vignette-dev-db) 기동 ->
|
|
# 관리자/인증 API 가 이미 healthy 이고 엔진도 healthy 면 스킵, 아니면
|
|
# start-public-runtime.ps1(-SkipWebRestart) 로 필요한 프로세스만 복구한다.
|
|
# 멱등: 어느 단계든 이미 살아있으면 건드리지 않는다. 수동으로 여러 번 실행해도 안전.
|
|
#
|
|
# 등록(로그온 시 자동 실행, 숨김 창):
|
|
# powershell -NoProfile -ExecutionPolicy Bypass -File scripts\register-boot-task.ps1
|
|
# 또는 수동:
|
|
# powershell -NoProfile -ExecutionPolicy Bypass -File scripts\boot-public-runtime.ps1
|
|
|
|
param(
|
|
[string]$Workspace = "D:\workspace\vignette",
|
|
[string]$DockerDesktop = "C:\Program Files\Docker\Docker\Docker Desktop.exe",
|
|
[int]$DaemonTimeoutSec = 360,
|
|
[int]$DbTimeoutSec = 90,
|
|
[int]$DbPort = 55432,
|
|
[int]$ApiPort = 8001,
|
|
[string]$BootLog = "D:\workspace\vignette\boot-public-runtime.log"
|
|
)
|
|
|
|
$ErrorActionPreference = "Continue" # 부트 스크립트는 끝까지 로깅하고 종료한다.
|
|
|
|
function Write-BootLog([string]$Message) {
|
|
$line = "[{0}] {1}" -f (Get-Date -Format "yyyy-MM-dd HH:mm:ss"), $Message
|
|
try {
|
|
Add-Content -LiteralPath $BootLog -Value $line -Encoding UTF8
|
|
} catch {
|
|
# 로그 파일이 잠겨도 부팅은 계속한다.
|
|
}
|
|
}
|
|
|
|
function Test-DockerDaemon([int]$TimeoutSec = 10) {
|
|
try {
|
|
$ver = & docker.exe info --format '{{.ServerVersion}}' 2>$null
|
|
if ($LASTEXITCODE -eq 0 -and $ver) { return $true }
|
|
} catch {}
|
|
return $false
|
|
}
|
|
|
|
function Test-Tcp([string]$Host_, [int]$Port) {
|
|
try {
|
|
$t = (Test-NetConnection -ComputerName $Host_ -Port $Port -WarningAction SilentlyContinue)
|
|
return [bool]$t.TcpTestSucceeded
|
|
} catch { return $false }
|
|
}
|
|
|
|
function Test-ApiControlPlaneHealthy {
|
|
# HttpWebRequest + Proxy=$null: WININET/시스템 프록시에 영향받지 않는 가장 직결적인 검사.
|
|
# 비대화형 스케줄러 컨텍스트에서도 127.0.0.1 로 직접 연결한다. 3회 재시도.
|
|
for ($i = 1; $i -le 3; $i++) {
|
|
try {
|
|
$req = [System.Net.HttpWebRequest]::Create("http://127.0.0.1:$ApiPort/health")
|
|
$req.Timeout = 5000
|
|
$req.ReadWriteTimeout = 5000
|
|
$req.Proxy = $null
|
|
$resp = $req.GetResponse()
|
|
$reader = New-Object System.IO.StreamReader($resp.GetResponseStream())
|
|
$body = $reader.ReadToEnd()
|
|
$reader.Close(); $resp.Close()
|
|
$h = $body | ConvertFrom-Json
|
|
if ($h.environment -eq "prod" -and $h.db -eq $true) { return $true }
|
|
Write-BootLog (" health probe attempt {0}: not-healthy body={1}" -f $i, $body)
|
|
return $false
|
|
} catch {
|
|
Write-BootLog (" health probe attempt {0} failed: {1}" -f $i, $_.Exception.Message)
|
|
Start-Sleep -Seconds 2
|
|
}
|
|
}
|
|
return $false
|
|
}
|
|
|
|
function Test-EngineHealthy {
|
|
try {
|
|
$response = Invoke-RestMethod -Uri "http://127.0.0.1:9099/health" -TimeoutSec 10
|
|
return $response.ok -eq $true
|
|
} catch {
|
|
return $false
|
|
}
|
|
}
|
|
|
|
Write-BootLog "================ boot start ================"
|
|
|
|
# 1) Docker 데몬(내려가 있으면 Docker Desktop 기동 후 대기)
|
|
if (-not (Test-DockerDaemon)) {
|
|
Write-BootLog "docker daemon down; launching Docker Desktop"
|
|
if (Test-Path -LiteralPath $DockerDesktop) {
|
|
Start-Process -FilePath $DockerDesktop | Out-Null
|
|
} else {
|
|
Write-BootLog "ERROR: Docker Desktop.exe not found at $DockerDesktop"
|
|
exit 1
|
|
}
|
|
$deadline = (Get-Date).AddSeconds($DaemonTimeoutSec)
|
|
while ((Get-Date) -lt $deadline) {
|
|
Start-Sleep -Seconds 5
|
|
if (Test-DockerDaemon) { break }
|
|
}
|
|
}
|
|
if (-not (Test-DockerDaemon)) {
|
|
Write-BootLog "ERROR: docker daemon did not come up within ${DaemonTimeoutSec}s"
|
|
exit 1
|
|
}
|
|
Write-BootLog "docker daemon up"
|
|
|
|
# 2) postgres 컨테이너(restart 정책 백업 + 명시 기동) 후 포트 대기
|
|
$null = & docker.exe update --restart unless-stopped vignette-dev-db 2>$null
|
|
$null = & docker.exe start vignette-dev-db 2>$null
|
|
$dbDeadline = (Get-Date).AddSeconds($DbTimeoutSec)
|
|
while ((Get-Date) -lt $dbDeadline) {
|
|
if (Test-Tcp -Host_ "127.0.0.1" -Port $DbPort) { break }
|
|
Start-Sleep -Seconds 3
|
|
}
|
|
if (-not (Test-Tcp -Host_ "127.0.0.1" -Port $DbPort)) {
|
|
Write-BootLog "ERROR: postgres not listening on 127.0.0.1:$DbPort"
|
|
exit 1
|
|
}
|
|
Write-BootLog "postgres 127.0.0.1:$DbPort up"
|
|
|
|
# 3) 엔진/API/cloudflared — 이미 healthy 면 스킵(불필요한 재시작/다운타임 방지)
|
|
if ((Test-ApiControlPlaneHealthy) -and (Test-EngineHealthy)) {
|
|
Write-BootLog "control plane and engine already healthy; skipping runtime restart"
|
|
} else {
|
|
$pub = Join-Path $Workspace "scripts\start-public-runtime.ps1"
|
|
if (-not (Test-Path -LiteralPath $pub)) {
|
|
Write-BootLog "ERROR: start-public-runtime.ps1 not found at $pub"
|
|
exit 1
|
|
}
|
|
Write-BootLog "running start-public-runtime.ps1 -SkipWebRestart"
|
|
$out = & powershell.exe -NoProfile -ExecutionPolicy Bypass -File $pub -SkipWebRestart 2>&1
|
|
$out | ForEach-Object { Write-BootLog (" pub> " + $_) }
|
|
if ($LASTEXITCODE -ne 0) {
|
|
Write-BootLog "ERROR: start-public-runtime.ps1 exit $LASTEXITCODE"
|
|
exit 1
|
|
}
|
|
}
|
|
|
|
# 4) 최종 확인
|
|
if (Test-ApiControlPlaneHealthy) {
|
|
if (Test-EngineHealthy) {
|
|
Write-BootLog "boot OK: control plane and engine healthy"
|
|
} else {
|
|
Write-BootLog "boot OK: admin/auth control plane healthy; engine remains degraded"
|
|
}
|
|
exit 0
|
|
} else {
|
|
Write-BootLog "WARN: boot finished but admin/auth control plane failed — see apps/api/api.public.err.log"
|
|
exit 2
|
|
}
|