205 lines
7.7 KiB
PowerShell
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"
|
|
|