flnxi-Wu/一键镜像部署.ps1
WuYongJie f646b71813
Some checks failed
CI Check / check (push) Has been cancelled
chore: 初始化本地仓库并同步最近改动
2025-11-18 17:50:16 +08:00

254 lines
8.7 KiB
PowerShell
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# Docker Registry 部署脚本
# 使用容器镜像服务(如阿里云 ACR)进行部署
# 完全避免上传大文件,部署更快
[CmdletBinding()]
param(
[Parameter(Mandatory = $true)]
[string]$Server,
[string]$Username = "root",
[string]$Password,
[Parameter(Mandatory = $true)]
[string]$RegistryHost,
[Parameter(Mandatory = $true)]
[string]$RegistryNamespace,
[string]$RegistryImage = "youyidian-3d-material",
[string]$RegistryUser = "",
[string]$RegistryPassword = "",
[string]$Tag = "latest",
[string]$ImageName = "youyidian-3d-material",
[string]$Dockerfile = "Dockerfile",
[string]$Context = ".",
[string]$RemotePath = "/root/deploy",
[string]$ComposeFile = "docker-compose.prod.yml",
[switch]$SkipCheck,
[string]$CacheTag = "previous",
[switch]$DisableCache,
# 可选:如果需要严格校验 SSH 主机指纹,可在此传入
[string]$SshHostKeyFingerprint = ""
)
Set-StrictMode -Version Latest
$ErrorActionPreference = "Stop"
[Console]::OutputEncoding = [System.Text.Encoding]::UTF8
$OutputEncoding = [System.Text.Encoding]::UTF8
function Get-PlinkPath {
$candidates = @(
"C:\Program Files\PuTTY\plink.exe",
"C:\Program Files (x86)\PuTTY\plink.exe"
)
foreach ($path in $candidates) {
if (Test-Path $path) { return $path }
}
$cmd = Get-Command plink.exe -ErrorAction SilentlyContinue
if ($cmd) { return $cmd.Source }
throw "未找到 plink.exe,请安装 PuTTY 或将其加入 PATH。"
}
function Invoke-RemoteCommand {
param(
[Parameter(Mandatory = $true)][string]$Server,
[Parameter(Mandatory = $true)][string]$Username,
[Parameter(Mandatory = $true)][string]$Command,
[string]$Password,
[string]$HostKeyFingerprint = "",
[switch]$IgnoreError
)
$plinkPath = Get-PlinkPath
$plinkArgs = @("-batch")
if ($HostKeyFingerprint) {
$plinkArgs += @("-hostkey", $HostKeyFingerprint)
}
if ($Password) {
$plinkArgs += @("-pw", $Password)
}
$plinkArgs += "$Username@$Server"
$plinkArgs += $Command
$output = & $plinkPath $plinkArgs
$exitCode = $LASTEXITCODE
if (-not $IgnoreError -and $exitCode -ne 0) {
throw "远程命令执行失败 (exit $exitCode): $Command`n$output"
}
return [pscustomobject]@{
ExitCode = $exitCode
Output = $output
}
}
# 启用 BuildKit
if (-not $env:DOCKER_BUILDKIT) {
$env:DOCKER_BUILDKIT = "1"
Write-Host "已启用 BuildKit" -ForegroundColor Green
}
$funTips = @{
Warmup = @(
"[=^_^=] 部署小助手上线,先把工具擦亮~",
"(ง •̀_•́)ง 例行热身,马上开始忙活。"
)
Build = @(
"( ̄▽ ̄)ゞ 正在拼装镜像零件,喝口水继续等。",
"♪(^∀^●) 构建火力全开,稍等就好。"
)
Push = @(
"(~ ̄▽ ̄)~ 把新鲜镜像塞进 Registry 里~",
"(=^・ω・^=) 正在排队上传,请耐心排号。"
)
Remote = @(
"( •̀ ω •́ )✧ 正在远程唤醒服务器,不要慌~",
"(ノ>ω<)ノ 让服务器也跟上节奏!"
)
Done = @(
"✧(≖ ◡ ≖✿) 大功告成,去后台转一圈吧!",
"(づ ̄ 3 ̄)づ 部署完成,撒花~"
)
}
function Show-FunTip {
param([string]$Stage)
if ($funTips.ContainsKey($Stage)) {
$msg = $funTips[$Stage] | Get-Random
Write-Host " $msg" -ForegroundColor DarkCyan
}
}
$projectRoot = Resolve-Path "$PSScriptRoot"
Push-Location $projectRoot
try {
# 构建镜像
$fullImageName = "${RegistryHost}/${RegistryNamespace}/${RegistryImage}:${Tag}"
Write-Host "===> 构建镜像: $fullImageName" -ForegroundColor Cyan
Show-FunTip -Stage "Warmup"
$buildArgs = @(
"--build-arg", "DATABASE_URL=postgresql://placeholder:placeholder@localhost:5432/placeholder",
"--build-arg", "NEXTAUTH_URL=http://45.249.208.118:4320",
"--build-arg", "APP_PUBLIC_URL=http://45.249.208.118:4320",
"--build-arg", "BUILDKIT_INLINE_CACHE=1"
)
$cacheImage = $null
if (-not $DisableCache -and $CacheTag) {
$cacheImage = "${RegistryHost}/${RegistryNamespace}/${RegistryImage}:${CacheTag}"
Write-Host "尝试拉取缓存镜像: $cacheImage" -ForegroundColor Gray
docker pull $cacheImage | Out-Null
if ($LASTEXITCODE -eq 0) {
$buildArgs += @("--cache-from", $cacheImage)
Write-Host "✓ 已启用缓存" -ForegroundColor Green
} else {
Write-Host "缓存镜像不可用,继续无缓存构建" -ForegroundColor Yellow
$cacheImage = $null
}
}
if (-not $SkipCheck) {
Write-Host "===> 运行检查" -ForegroundColor Cyan
npm run check
}
Write-Host "正在构建镜像..." -ForegroundColor Yellow
Write-Host " [友情提示] 这个阶段日志可能会暂时安静,1-2 分钟都属正常~" -ForegroundColor DarkYellow
Show-FunTip -Stage "Build"
$buildStartTime = Get-Date
docker build --progress=plain -t $fullImageName -f $Dockerfile $buildArgs $Context
if ($LASTEXITCODE -ne 0) {
throw "构建失败"
}
$buildDuration = ((Get-Date) - $buildStartTime).TotalMinutes
Write-Host "构建完成,耗时: $([Math]::Round($buildDuration, 1)) 分钟" -ForegroundColor Green
# 登录 Registry(如果需要认证)
if ($RegistryUser -and $RegistryPassword) {
Write-Host "===> 登录容器镜像服务" -ForegroundColor Cyan
echo $RegistryPassword | docker login $RegistryHost -u $RegistryUser --password-stdin
if ($LASTEXITCODE -ne 0) {
throw "登录 Registry 失败"
}
Write-Host "✓ 登录成功" -ForegroundColor Green
} else {
Write-Host "跳过登录(未配置认证)" -ForegroundColor Gray
}
# 推送镜像
Write-Host "===> 推送镜像到 Registry" -ForegroundColor Cyan
Show-FunTip -Stage "Push"
Write-Host "镜像: $fullImageName" -ForegroundColor Gray
docker push $fullImageName
if ($LASTEXITCODE -ne 0) {
throw "推送失败"
}
Write-Host "✓ 推送成功" -ForegroundColor Green
if ($cacheImage) {
Write-Host "同步缓存标签: $CacheTag" -ForegroundColor Gray
docker tag $fullImageName $cacheImage
docker push $cacheImage | Out-Null
if ($LASTEXITCODE -eq 0) {
Write-Host "✓ 缓存标签已更新 ($cacheImage)" -ForegroundColor Green
} else {
Write-Host "缓存标签推送失败(可忽略)" -ForegroundColor Yellow
}
}
# 服务器端操作
Write-Host "===> 在服务器上拉取并部署" -ForegroundColor Cyan
Write-Host " [友情提示] 远程执行可能需要十几秒,看到日志停顿也别慌~" -ForegroundColor DarkYellow
Show-FunTip -Stage "Remote"
$remoteCommandParams = @{
Server = $Server
Username = $Username
Password = $Password
HostKeyFingerprint = $SshHostKeyFingerprint
}
# 在服务器上登录 Registry(如果需要认证)
if ($RegistryUser -and $RegistryPassword) {
Write-Host "在服务器上登录 Registry..." -ForegroundColor Gray
Invoke-RemoteCommand @remoteCommandParams -Command "echo '$RegistryPassword' | docker login $RegistryHost -u $RegistryUser --password-stdin" -IgnoreError:$false | Out-Null
} else {
Write-Host "跳过服务器登录(未配置认证)" -ForegroundColor Gray
}
# 拉取镜像
Write-Host "在服务器上拉取镜像..." -ForegroundColor Gray
Invoke-RemoteCommand @remoteCommandParams -Command "docker pull ${fullImageName}" | Out-Null
Write-Host "✓ 镜像拉取成功" -ForegroundColor Green
# 更新 docker-compose.yml
Write-Host "更新 docker-compose.yml..." -ForegroundColor Gray
$updateImageCmd = "cd ${RemotePath} && sed -i 's|image:.*youyidian-3d-material.*|image: ${fullImageName}|g' ${ComposeFile}"
Invoke-RemoteCommand @remoteCommandParams -Command $updateImageCmd | Out-Null
# 部署
Write-Host "部署应用..." -ForegroundColor Gray
Invoke-RemoteCommand @remoteCommandParams -Command "cd ${RemotePath} && docker compose -f ${ComposeFile} up -d" | Out-Null
Write-Host "✓ 部署成功" -ForegroundColor Green
Write-Host ""
Write-Host "===> 部署完成" -ForegroundColor Green
Write-Host "镜像: $fullImageName" -ForegroundColor Gray
Write-Host "服务器: $Server" -ForegroundColor Gray
Show-FunTip -Stage "Done"
}
catch {
Write-Host "✗ 部署失败: $_" -ForegroundColor Red
exit 1
}
finally {
Pop-Location
}