flnxi-Wu/scripts/cleanup-harbor.ps1
WuYongJie d1c6efc79d
Some checks failed
CI Check / check (push) Has been cancelled
feat: NAS 部署与 Harbor 推送流程
2025-12-03 14:39:06 +08:00

205 lines
7.7 KiB
PowerShell

# ================================================================
# Harbor 镜像清理脚本
# 功能:清理 Harbor Registry 中的旧镜像和未使用的层
# ================================================================
param(
[string]$HarborUrl = "harbor.flnxi.com",
[string]$HarborUsername = "admin",
[string]$HarborPassword = "Harbor12345",
[string]$ProjectName = "flnxi",
[string]$ImageName = "flnxi-material",
[int]$KeepCount = 5,
[switch]$DryRun,
[switch]$Help
)
# 颜色输出函数
function Write-ColorOutput {
param(
[string]$Message,
[ConsoleColor]$ForegroundColor = "White"
)
Write-Host $Message -ForegroundColor $ForegroundColor
}
function Write-Success {
param([string]$Message)
Write-ColorOutput "[OK] $Message" "Green"
}
function Write-Warning {
param([string]$Message)
Write-ColorOutput "[WARN] $Message" "Yellow"
}
function Write-ErrorColored {
param([string]$Message)
Write-ColorOutput "[ERROR] $Message" "Red"
}
function Write-Info {
param([string]$Message)
Write-ColorOutput "[INFO] $Message" "Cyan"
}
if ($Help) {
Write-ColorOutput "Harbor 镜像清理脚本" "Green"
Write-ColorOutput "========================================" "Green"
Write-ColorOutput "用法:" "White"
Write-ColorOutput " .\scripts\cleanup-harbor.ps1 [参数]" "Yellow"
Write-ColorOutput "" "White"
Write-ColorOutput "参数:" "White"
Write-ColorOutput " -HarborUrl [地址] Harbor地址 (默认: harbor.flnxi.com)" "Gray"
Write-ColorOutput " -HarborUsername [用户] Harbor用户名 (默认: admin)" "Gray"
Write-ColorOutput " -HarborPassword [密码] Harbor密码 (默认: Harbor12345)" "Gray"
Write-ColorOutput " -ProjectName [名称] Harbor项目名称 (默认: flnxi)" "Gray"
Write-ColorOutput " -ImageName [名称] 镜像名称 (默认: flnxi-material)" "Gray"
Write-ColorOutput " -KeepCount [N] 保留的镜像数量 (默认: 5)" "Gray"
Write-ColorOutput " -DryRun 仅显示将要执行的操作,不实际删除" "Gray"
Write-ColorOutput " -Help 显示此帮助信息" "Gray"
exit 0
}
Write-ColorOutput "========================================" "Magenta"
Write-ColorOutput " Harbor 镜像清理脚本" "Magenta"
Write-ColorOutput "========================================" "Magenta"
Write-ColorOutput ""
# 1. 获取 Harbor API Token
Write-Info "正在获取 Harbor API Token..."
$authUrl = "https://$HarborUrl/api/v2.0/users/login"
$authBody = @{
principal = $HarborUsername
password = $HarborPassword
} | ConvertTo-Json
try {
$authResponse = Invoke-RestMethod -Uri $authUrl -Method Post -Body $authBody -ContentType "application/json" -SkipCertificateCheck
$token = $authResponse.token
Write-Success "Harbor 登录成功"
} catch {
Write-ErrorColored "Harbor 登录失败: $_"
exit 1
}
# 2. 获取项目ID
Write-Info "正在获取项目信息..."
$projectUrl = "https://$HarborUrl/api/v2.0/projects?name=$ProjectName"
$headers = @{
"Authorization" = "Bearer $token"
}
try {
$projects = Invoke-RestMethod -Uri $projectUrl -Method Get -Headers $headers -SkipCertificateCheck
if ($projects.Count -eq 0) {
Write-ErrorColored "项目 $ProjectName 不存在"
exit 1
}
$projectId = $projects[0].project_id
Write-Success "找到项目: $ProjectName (ID: $projectId)"
} catch {
Write-ErrorColored "获取项目信息失败: $_"
exit 1
}
# 3. 获取仓库信息
Write-Info "正在获取仓库信息..."
$repoUrl = "https://$HarborUrl/api/v2.0/projects/$ProjectName/repositories/$ImageName"
try {
$repo = Invoke-RestMethod -Uri $repoUrl -Method Get -Headers $headers -SkipCertificateCheck
Write-Success "找到仓库: $ProjectName/$ImageName"
} catch {
Write-ErrorColored "仓库 $ProjectName/$ImageName 不存在或无法访问: $_"
exit 1
}
# 4. 获取所有标签
Write-Info "正在获取镜像标签列表..."
$tagsUrl = "https://$HarborUrl/api/v2.0/projects/$ProjectName/repositories/$ImageName/artifacts"
try {
$artifacts = Invoke-RestMethod -Uri $tagsUrl -Method Get -Headers $headers -SkipCertificateCheck
Write-Info "找到 $($artifacts.Count) 个镜像版本"
# 按创建时间排序(最新的在前)
$sortedArtifacts = $artifacts | Sort-Object { [DateTime]::Parse($_.push_time) } -Descending
# 保留最新的几个
$toKeep = $sortedArtifacts | Select-Object -First $KeepCount
$toRemove = $sortedArtifacts | Select-Object -Skip $KeepCount
Write-Info "将保留 $($toKeep.Count) 个最新版本"
Write-Info "将删除 $($toRemove.Count) 个旧版本"
if ($toKeep.Count -gt 0) {
Write-ColorOutput "" "White"
Write-ColorOutput "保留的版本:" "Cyan"
foreach ($artifact in $toKeep) {
$tags = if ($artifact.tags) { ($artifact.tags | ForEach-Object { $_.name }) -join ", " } else { "<无标签>" }
$size = [Math]::Round($artifact.size / 1MB, 2)
Write-ColorOutput " - $tags (大小: ${size}MB, 创建时间: $($artifact.push_time))" "Gray"
}
}
if ($toRemove.Count -gt 0) {
Write-ColorOutput "" "White"
Write-ColorOutput "将删除的版本:" "Yellow"
foreach ($artifact in $toRemove) {
$tags = if ($artifact.tags) { ($artifact.tags | ForEach-Object { $_.name }) -join ", " } else { "<无标签>" }
$size = [Math]::Round($artifact.size / 1MB, 2)
Write-ColorOutput " - $tags (大小: ${size}MB, 创建时间: $($artifact.push_time))" "Gray"
}
if ($DryRun) {
Write-ColorOutput "" "Yellow"
Write-Warning "这是模拟运行,不会实际删除镜像"
Write-Info "使用不带 -DryRun 参数来实际执行删除"
} else {
Write-ColorOutput "" "White"
Write-Info "开始删除旧版本..."
$deletedCount = 0
$failedCount = 0
foreach ($artifact in $toRemove) {
$digest = $artifact.digest
$tags = if ($artifact.tags) { ($artifact.tags | ForEach-Object { $_.name }) -join ", " } else { "<无标签>" }
try {
$deleteUrl = "https://$HarborUrl/api/v2.0/projects/$ProjectName/repositories/$ImageName/artifacts/$digest"
Invoke-RestMethod -Uri $deleteUrl -Method Delete -Headers $headers -SkipCertificateCheck | Out-Null
Write-Success "已删除: $tags (digest: $digest)"
$deletedCount++
} catch {
Write-Warning "删除失败: $tags - $_"
$failedCount++
}
}
Write-ColorOutput "" "White"
if ($deletedCount -gt 0) {
Write-Success "成功删除 $deletedCount 个版本"
}
if ($failedCount -gt 0) {
Write-Warning "$failedCount 个版本删除失败"
}
# 5. 触发垃圾回收(清理未使用的层)
Write-ColorOutput "" "White"
Write-Info "建议在 Harbor Web UI 中手动触发垃圾回收以清理未使用的层"
Write-Info "路径: 项目 -> $ProjectName -> 仓库 -> $ImageName -> 垃圾回收"
}
} else {
Write-Info "没有需要删除的旧版本"
}
} catch {
Write-ErrorColored "获取标签列表失败: $_"
exit 1
}
Write-ColorOutput "" "Magenta"
Write-ColorOutput "========================================" "Magenta"
Write-ColorOutput " 清理完成" "Magenta"
Write-ColorOutput "========================================" "Magenta"