233 lines
8.6 KiB
Batchfile
233 lines
8.6 KiB
Batchfile
# Piano Plan 部署脚本 - 钢琴练习方案系统
|
|
# 版本:v2.0 (安全版)
|
|
# 核心原则:备份优先、不删除、自动验证
|
|
#
|
|
# 使用前:
|
|
# 1. 修改下方 CONFIG 部分,填写正确的值
|
|
# 2. 确认本地代码已验证通过
|
|
# 3. 确认目标环境正确
|
|
|
|
# ============================================================
|
|
# 配置(请根据实际情况修改)
|
|
# ============================================================
|
|
$IMAGE_NAME = "piano-plan"
|
|
$IMAGE_TAG = "latest"
|
|
$CONTAINER_NAME = "piano-plan"
|
|
$REMOTE_HOST = "root@47.106.65.108"
|
|
$LOCAL_PATH = "D:\F\NewI\opencode\daily-workspace\projects\青年钢琴集体课\练习方案系统"
|
|
$REMOTE_IMAGE_PATH = "/tmp/piano-plan.tar"
|
|
|
|
# 必须保留的挂载(不要修改)
|
|
# ============================================================
|
|
|
|
$ErrorActionPreference = "Stop"
|
|
|
|
Write-Host ""
|
|
Write-Host "========================================" -ForegroundColor Cyan
|
|
Write-Host " Piano Plan 部署脚本 (安全版 v2.0)" -ForegroundColor Cyan
|
|
Write-Host "========================================" -ForegroundColor Cyan
|
|
Write-Host ""
|
|
|
|
# ----------------------------------------------------------
|
|
# 部署前确认(必须!)
|
|
# ----------------------------------------------------------
|
|
Write-Host "【重要】部署前必须获得用户明确同意" -ForegroundColor Yellow
|
|
Write-Host ""
|
|
$confirm = Read-Host "是否确认要执行部署?(输入 'YES' 继续)"
|
|
if ($confirm -ne "YES") {
|
|
Write-Host "部署已取消" -ForegroundColor Red
|
|
exit 0
|
|
}
|
|
Write-Host ""
|
|
|
|
# ----------------------------------------------------------
|
|
# 安全检查函数
|
|
# ----------------------------------------------------------
|
|
function Test-ContainerExists {
|
|
param([string]$Name)
|
|
$result = ssh -i ~/.ssh/id_rsa $REMOTE_HOST "docker ps -a --filter name=$Name --format '{{.Names}}'"
|
|
return ($result -eq $Name)
|
|
}
|
|
|
|
function Backup-File {
|
|
param([string]$Source, [string]$Dest)
|
|
$timestamp = Get-Date -Format "yyyyMMdd_HHmmss"
|
|
$backupPath = "$Dest backup_$timestamp"
|
|
Write-Host " [备份] $Source -> $backupPath" -ForegroundColor Yellow
|
|
ssh -i ~/.ssh/id_rsa $REMOTE_HOST "cp -r '$Source' '$backupPath'"
|
|
if ($LASTEXITCODE -eq 0) {
|
|
Write-Host " [OK] 备份成功" -ForegroundColor Green
|
|
} else {
|
|
Write-Host " [ERROR] 备份失败!" -ForegroundColor Red
|
|
throw "备份失败,终止部署"
|
|
}
|
|
}
|
|
|
|
function Get-CurrentMounts {
|
|
$mounts = ssh -i ~/.ssh/id_rsa $REMOTE_HOST "docker inspect $CONTAINER_NAME --format '{{json .HostConfig.Binds}}'" 2>$null
|
|
if ($LASTEXITCODE -ne 0 -or [string]::IsNullOrEmpty($mounts)) {
|
|
Write-Host " [WARNING] 无法获取当前挂载,将使用默认挂载配置" -ForegroundColor Yellow
|
|
return $null
|
|
}
|
|
return $mounts
|
|
}
|
|
|
|
function Write-Section {
|
|
param([string]$Title)
|
|
Write-Host ""
|
|
Write-Host "[$Title]" -ForegroundColor Cyan
|
|
}
|
|
|
|
function Write-Step {
|
|
param([int]$Num, [int]$Total, [string]$Msg)
|
|
Write-Host " Step $Num/$Total`: $Msg" -ForegroundColor White
|
|
}
|
|
|
|
function Test-ServiceHealth {
|
|
Write-Host " 检查服务健康状态..." -ForegroundColor Yellow
|
|
$status = ssh -i ~/.ssh/id_rsa $REMOTE_HOST "curl -s -o /dev/null -w '%{http_code}' http://localhost:5001/ 2>`$null"
|
|
if ($status -eq "302" -or $status -eq "200") {
|
|
Write-Host " [OK] 服务正常 (HTTP $status)" -ForegroundColor Green
|
|
return $true
|
|
} else {
|
|
Write-Host " [WARNING] 服务异常 (HTTP $status)" -ForegroundColor Red
|
|
return $false
|
|
}
|
|
}
|
|
|
|
function Test-DataIntegrity {
|
|
Write-Host " 检查数据完整性..." -ForegroundColor Yellow
|
|
$fileCount = ssh -i ~/.ssh/id_rsa $REMOTE_HOST "docker exec piano-plan ls -1 /app/个性化方案/*.md 2>/dev/null | wc -l"
|
|
if ($fileCount -ge 15) {
|
|
Write-Host " [OK] 问题文件完整 ($fileCount 个 md 文件)" -ForegroundColor Green
|
|
return $true
|
|
} else {
|
|
Write-Host " [ERROR] 问题文件不完整!只有 $fileCount 个" -ForegroundColor Red
|
|
return $false
|
|
}
|
|
}
|
|
|
|
# ============================================================
|
|
# 主流程
|
|
# ============================================================
|
|
|
|
# Step 1: 前置检查
|
|
Write-Section "1. 前置检查"
|
|
Write-Step 1 7 "确认容器存在"
|
|
if (-not (Test-ContainerExists $CONTAINER_NAME)) {
|
|
Write-Host " [ERROR] 容器 '$CONTAINER_NAME' 不存在!" -ForegroundColor Red
|
|
Write-Host " 请检查 CONTAINER_NAME 配置是否正确" -ForegroundColor Red
|
|
exit 1
|
|
}
|
|
Write-Host " [OK] 容器存在" -ForegroundColor Green
|
|
|
|
# Step 2: 获取当前挂载(关键!)
|
|
Write-Step 2 7 "获取当前挂载配置"
|
|
Write-Host " [INFO] 将保留原有挂载配置" -ForegroundColor Cyan
|
|
$currentMounts = Get-CurrentMounts
|
|
|
|
# Step 3: 构建镜像
|
|
Write-Section "2. 构建 Docker 镜像"
|
|
Set-Location $LOCAL_PATH
|
|
Write-Host " 正在构建镜像..." -ForegroundColor Yellow
|
|
docker build -t "$IMAGE_NAME`:$IMAGE_TAG" . 2>&1 | ForEach-Object { Write-Host " $_" }
|
|
if ($LASTEXITCODE -ne 0) {
|
|
Write-Host " [ERROR] Docker 构建失败!" -ForegroundColor Red
|
|
exit 1
|
|
}
|
|
Write-Host " [OK] 镜像构建成功" -ForegroundColor Green
|
|
|
|
# Step 4: 备份当前容器配置(挂载信息)
|
|
Write-Section "3. 备份"
|
|
Write-Step 3 7 "备份容器配置"
|
|
$mountBackupPath = "/tmp/piano_plan_mounts_$(Get-Date -Format 'yyyyMMdd_HHmmss').txt"
|
|
ssh -i ~/.ssh/id_rsa $REMOTE_HOST "echo '$currentMounts' > '$mountBackupPath'"
|
|
Write-Host " [OK] 挂载配置已备份到 $mountBackupPath" -ForegroundColor Green
|
|
|
|
# Step 5: 保存并上传镜像
|
|
Write-Section "4. 上传镜像"
|
|
Write-Step 4 7 "保存镜像"
|
|
docker save "$IMAGE_NAME`:$IMAGE_TAG" -o "$LOCAL_PATH\$IMAGE_NAME.tar"
|
|
Write-Host " [OK] 镜像已保存" -ForegroundColor Green
|
|
|
|
Write-Step 5 7 "上传镜像到服务器"
|
|
scp -i ~/.ssh/id_rsa "$LOCAL_PATH\$IMAGE_NAME.tar" "$REMOTE_HOST`:$REMOTE_IMAGE_PATH"
|
|
Write-Host " [OK] 镜像已上传" -ForegroundColor Green
|
|
|
|
# Step 6: 停止旧容器(关键:不删除!)
|
|
Write-Section "5. 停止旧容器"
|
|
Write-Step 6 7 "停止容器 (NOT 删除)"
|
|
Write-Host " [WARNING] 即将停止容器,配置和挂载将保留" -ForegroundColor Yellow
|
|
ssh -i ~/.ssh/id_rsa $REMOTE_HOST "docker stop $CONTAINER_NAME"
|
|
Write-Host " [OK] 容器已停止" -ForegroundColor Green
|
|
|
|
# Step 7: 加载新镜像
|
|
Write-Section "6. 启动新容器"
|
|
Write-Step 7 7 "加载新镜像并启动"
|
|
|
|
# 构建挂载参数(使用原有挂载)
|
|
$mountArgs = "-v /opt/piano-plan/个性化方案:/app/个性化方案 -v piano-plan-data:/app/data -v piano-plan-output:/app/output -v piano-plan-config:/app/config"
|
|
|
|
$runCmd = @"
|
|
docker rm $CONTAINER_NAME 2>/dev/null
|
|
docker run -d --name $CONTAINER_NAME -p 5001:5001 --restart unless-stopped -e FLASK_ENV=production -e PYTHONDONTWRITEBYTECODE=1 $mountArgs $IMAGE_NAME`:$IMAGE_TAG
|
|
"@
|
|
|
|
Write-Host " [INFO] 启动命令: $runCmd" -ForegroundColor Cyan
|
|
ssh -i ~/.ssh/id_rsa $REMOTE_HOST $runCmd
|
|
Write-Host " [OK] 新容器已启动" -ForegroundColor Green
|
|
|
|
# 等待容器启动
|
|
Start-Sleep -Seconds 3
|
|
|
|
# ============================================================
|
|
# 部署后验证
|
|
# ============================================================
|
|
Write-Section "7. 部署后验证"
|
|
$allPassed = $true
|
|
|
|
Write-Host ""
|
|
Write-Host " 检查项:" -ForegroundColor White
|
|
|
|
# 检查1: 容器状态
|
|
Write-Host " [1/3] 容器状态" -ForegroundColor White
|
|
$containerStatus = ssh -i ~/.ssh/id_rsa $REMOTE_HOST "docker ps --filter name=$CONTAINER_NAME --format '{{.Status}}'"
|
|
if ($containerStatus -like "Up *") {
|
|
Write-Host " [OK] 容器运行中: $containerStatus" -ForegroundColor Green
|
|
} else {
|
|
Write-Host " [ERROR] 容器状态异常: $containerStatus" -ForegroundColor Red
|
|
$allPassed = $false
|
|
}
|
|
|
|
# 检查2: 服务健康
|
|
Write-Host " [2/3] 服务健康检查" -ForegroundColor White
|
|
if (-not (Test-ServiceHealth)) {
|
|
$allPassed = $false
|
|
}
|
|
|
|
# 检查3: 数据完整性
|
|
Write-Host " [3/3] 数据完整性检查" -ForegroundColor White
|
|
if (-not (Test-DataIntegrity)) {
|
|
$allPassed = $false
|
|
}
|
|
|
|
# ============================================================
|
|
# 完成
|
|
# ============================================================
|
|
Write-Host ""
|
|
Write-Host "========================================" -ForegroundColor Cyan
|
|
|
|
if ($allPassed) {
|
|
Write-Host " 部署完成!" -ForegroundColor Green
|
|
} else {
|
|
Write-Host " 部署完成(但有警告,请检查!)" -ForegroundColor Yellow
|
|
}
|
|
|
|
Write-Host ""
|
|
Write-Host " 访问地址: https://piano.yoin.fun" -ForegroundColor Cyan
|
|
Write-Host " 备份位置: $mountBackupPath" -ForegroundColor Cyan
|
|
Write-Host "========================================" -ForegroundColor Cyan
|
|
Write-Host ""
|
|
|
|
# 清理本地临时文件
|
|
Remove-Item "$LOCAL_PATH\$IMAGE_NAME.tar" -ErrorAction SilentlyContinue |