324 lines
10 KiB
PowerShell
324 lines
10 KiB
PowerShell
# ============================================
|
||
# 文件存储备份脚本 (PowerShell)
|
||
# ============================================
|
||
# 功能:备份 MinIO 文件存储(支持本地和 Docker 环境)
|
||
# 使用:.\backup-files.ps1 [-BackupDir <目录>] [-DockerContainer <容器名>]
|
||
# ============================================
|
||
|
||
param(
|
||
[string]$BackupDir = ".\backups\files",
|
||
[string]$MinioDataDir = ".\minio-data",
|
||
[string]$DockerContainer = "furniture-minio"
|
||
)
|
||
|
||
$ErrorActionPreference = "Stop"
|
||
|
||
# 配置
|
||
$Timestamp = Get-Date -Format "yyyyMMdd-HHmmss"
|
||
$BackupFile = "files-backup-$Timestamp.tar.gz"
|
||
$LogFile = "backup-files-$Timestamp.log"
|
||
$global:UseDocker = $false
|
||
|
||
# 确保备份目录存在
|
||
if (-not (Test-Path $BackupDir)) {
|
||
New-Item -ItemType Directory -Path $BackupDir -Force | Out-Null
|
||
}
|
||
|
||
# 日志函数
|
||
function Write-Log {
|
||
param([string]$Message, [string]$Level = "INFO")
|
||
|
||
$TimeStamp = Get-Date -Format "yyyy-MM-dd HH:mm:ss"
|
||
$LogMessage = "[$TimeStamp] $($Level): $Message"
|
||
|
||
switch ($Level) {
|
||
"ERROR" { Write-Host $LogMessage -ForegroundColor Red }
|
||
"WARN" { Write-Host $LogMessage -ForegroundColor Yellow }
|
||
"SUCCESS" { Write-Host $LogMessage -ForegroundColor Green }
|
||
default { Write-Host $LogMessage }
|
||
}
|
||
|
||
Add-Content -Path "$BackupDir\$LogFile" -Value $LogMessage
|
||
}
|
||
|
||
# 检查依赖
|
||
function Test-Dependencies {
|
||
Write-Log "检查依赖..."
|
||
|
||
# 检查是否有 Docker 容器运行
|
||
if (Get-Command docker -ErrorAction SilentlyContinue) {
|
||
$containerRunning = docker ps --filter "name=$DockerContainer" --format "{{.Names}}" 2>$null
|
||
if ($containerRunning -eq $DockerContainer) {
|
||
$global:UseDocker = $true
|
||
Write-Log "检测到 Docker 容器: $DockerContainer,将使用 Docker 执行备份"
|
||
}
|
||
}
|
||
|
||
# 检查 7-Zip 或 tar(仅本地模式需要)
|
||
$global:CompressTool = $null
|
||
if (Get-Command 7z -ErrorAction SilentlyContinue) {
|
||
$global:CompressTool = "7z"
|
||
} elseif (Get-Command tar -ErrorAction SilentlyContinue) {
|
||
$global:CompressTool = "tar"
|
||
}
|
||
|
||
if (-not $global:UseDocker -and -not $global:CompressTool) {
|
||
Write-Log "未找到压缩工具(7z 或 tar)" "ERROR"
|
||
exit 1
|
||
}
|
||
|
||
Write-Log "✅ 依赖检查通过" "SUCCESS"
|
||
}
|
||
|
||
# 验证 MinIO 数据目录
|
||
function Test-MinioDirectory {
|
||
Write-Log "验证 MinIO 数据目录..."
|
||
|
||
if ($global:UseDocker) {
|
||
# Docker 模式:检查容器内的数据目录
|
||
$result = docker exec $DockerContainer ls /data 2>&1
|
||
if ($LASTEXITCODE -ne 0) {
|
||
Write-Log "MinIO 容器数据目录不存在或无法访问" "WARN"
|
||
} else {
|
||
Write-Log "✅ MinIO 容器数据目录验证通过" "SUCCESS"
|
||
}
|
||
return
|
||
}
|
||
|
||
if (-not (Test-Path $MinioDataDir)) {
|
||
Write-Log "MinIO 数据目录不存在: $MinioDataDir" "ERROR"
|
||
exit 1
|
||
}
|
||
|
||
# 检查目录是否为空
|
||
$items = Get-ChildItem -Path $MinioDataDir -ErrorAction SilentlyContinue
|
||
if ($items.Count -eq 0) {
|
||
Write-Log "MinIO 数据目录为空" "WARN"
|
||
}
|
||
|
||
Write-Log "✅ MinIO 数据目录验证通过" "SUCCESS"
|
||
}
|
||
|
||
# 创建备份目录
|
||
function New-BackupDirectory {
|
||
Write-Log "创建备份目录: $BackupDir"
|
||
|
||
if (-not (Test-Path $BackupDir)) {
|
||
New-Item -ItemType Directory -Path $BackupDir -Force | Out-Null
|
||
}
|
||
|
||
if (-not (Test-Path $BackupDir)) {
|
||
Write-Log "无法创建备份目录" "ERROR"
|
||
exit 1
|
||
}
|
||
|
||
Write-Log "✅ 备份目录已创建" "SUCCESS"
|
||
}
|
||
|
||
# 检查磁盘空间
|
||
function Test-DiskSpace {
|
||
Write-Log "检查磁盘空间..."
|
||
|
||
$drive = (Get-Item $BackupDir).PSDrive
|
||
$freeSpace = [math]::Round($drive.Free / 1MB, 2)
|
||
|
||
if ($global:UseDocker) {
|
||
# Docker 模式:估算容器内数据大小
|
||
$sizeResult = docker exec $DockerContainer du -sm /data 2>&1
|
||
if ($sizeResult -match '(\d+)') {
|
||
$minioSize = [int]$matches[1]
|
||
} else {
|
||
$minioSize = 100 # 默认估算 100MB
|
||
}
|
||
} else {
|
||
# 计算 MinIO 数据大小
|
||
$minioSize = [math]::Round((Get-ChildItem -Path $MinioDataDir -Recurse |
|
||
Measure-Object -Property Length -Sum).Sum / 1MB, 2)
|
||
}
|
||
|
||
$requiredSpace = [math]::Round($minioSize * 1.5, 2)
|
||
|
||
Write-Log "MinIO 数据大小: $minioSize MB"
|
||
Write-Log "可用空间: $freeSpace MB"
|
||
Write-Log "需要空间: $requiredSpace MB"
|
||
|
||
if ($freeSpace -lt $requiredSpace) {
|
||
Write-Log "磁盘空间不足" "ERROR"
|
||
exit 1
|
||
}
|
||
|
||
Write-Log "✅ 磁盘空间充足" "SUCCESS"
|
||
}
|
||
|
||
# 执行备份
|
||
function Start-Backup {
|
||
Write-Log "开始备份文件存储..."
|
||
Write-Log "备份文件: $BackupFile"
|
||
|
||
$startTime = Get-Date
|
||
$backupPath = Join-Path $BackupDir $BackupFile
|
||
|
||
try {
|
||
if ($global:UseDocker) {
|
||
# Docker 模式:使用 docker cp 复制数据,然后本地压缩
|
||
$tempDir = Join-Path $BackupDir "temp-minio-$Timestamp"
|
||
New-Item -ItemType Directory -Path $tempDir -Force | Out-Null
|
||
|
||
# 从容器复制数据目录
|
||
Write-Log "从容器复制 MinIO 数据..."
|
||
docker cp "${DockerContainer}:/data" $tempDir
|
||
|
||
if ($LASTEXITCODE -ne 0) {
|
||
throw "从容器复制数据失败"
|
||
}
|
||
|
||
# 本地压缩
|
||
Write-Log "压缩备份文件..."
|
||
if (Get-Command tar -ErrorAction SilentlyContinue) {
|
||
& tar -czf $backupPath -C $tempDir data
|
||
} elseif (Get-Command 7z -ErrorAction SilentlyContinue) {
|
||
& 7z a -ttar -so "$tempDir\data" | & 7z a -si -tgzip $backupPath | Out-Null
|
||
} else {
|
||
# 使用 PowerShell 压缩
|
||
Compress-Archive -Path "$tempDir\data" -DestinationPath "$backupPath.zip" -Force
|
||
Rename-Item "$backupPath.zip" $backupPath -Force
|
||
}
|
||
|
||
# 清理临时目录
|
||
Remove-Item -Path $tempDir -Recurse -Force
|
||
} else {
|
||
# 本地模式
|
||
if ($global:CompressTool -eq "7z") {
|
||
# 使用 7-Zip
|
||
& 7z a -ttar -so $MinioDataDir | & 7z a -si -tgzip $backupPath | Out-Null
|
||
} else {
|
||
# 使用 tar
|
||
$parentDir = Split-Path $MinioDataDir -Parent
|
||
$dirName = Split-Path $MinioDataDir -Leaf
|
||
& tar -czf $backupPath -C $parentDir $dirName
|
||
}
|
||
|
||
if ($LASTEXITCODE -ne 0) {
|
||
throw "备份失败"
|
||
}
|
||
}
|
||
|
||
$endTime = Get-Date
|
||
$duration = ($endTime - $startTime).TotalSeconds
|
||
|
||
Write-Log "✅ 备份完成,耗时: $([math]::Round($duration, 2)) 秒" "SUCCESS"
|
||
}
|
||
catch {
|
||
Write-Log "备份失败: $_" "ERROR"
|
||
exit 1
|
||
}
|
||
}
|
||
|
||
# 验证备份文件
|
||
function Test-BackupFile {
|
||
Write-Log "验证备份文件..."
|
||
|
||
$backupPath = Join-Path $BackupDir $BackupFile
|
||
|
||
if (-not (Test-Path $backupPath)) {
|
||
Write-Log "备份文件不存在" "ERROR"
|
||
exit 1
|
||
}
|
||
|
||
# 检查文件大小
|
||
$fileSize = (Get-Item $backupPath).Length
|
||
$fileSizeMB = [math]::Round($fileSize / 1MB, 2)
|
||
Write-Log "备份文件大小: $fileSizeMB MB"
|
||
|
||
# 生成 checksum
|
||
$hash = Get-FileHash -Path $backupPath -Algorithm SHA256
|
||
$checksum = $hash.Hash
|
||
$checksum | Out-File -FilePath "$backupPath.sha256" -Encoding ASCII
|
||
|
||
Write-Log "✅ 备份文件完整性验证通过" "SUCCESS"
|
||
Write-Log "✅ Checksum: $checksum" "SUCCESS"
|
||
}
|
||
|
||
# 清理旧备份
|
||
function Remove-OldBackups {
|
||
Write-Log "清理旧备份..."
|
||
|
||
$keepCount = 5
|
||
|
||
# 获取所有备份文件
|
||
$backupFiles = Get-ChildItem -Path $BackupDir -Filter "files-backup-*.tar.gz" |
|
||
Sort-Object LastWriteTime -Descending
|
||
|
||
$backupCount = $backupFiles.Count
|
||
|
||
if ($backupCount -gt $keepCount) {
|
||
$deleteCount = $backupCount - $keepCount
|
||
Write-Log "发现 $backupCount 个备份,删除最旧的 $deleteCount 个"
|
||
|
||
$backupFiles | Select-Object -Skip $keepCount | ForEach-Object {
|
||
Write-Log "删除: $($_.Name)"
|
||
Remove-Item $_.FullName -Force
|
||
$checksumFile = "$($_.FullName).sha256"
|
||
if (Test-Path $checksumFile) {
|
||
Remove-Item $checksumFile -Force
|
||
}
|
||
}
|
||
|
||
Write-Log "✅ 清理完成" "SUCCESS"
|
||
} else {
|
||
Write-Log "当前有 $backupCount 个备份,无需清理"
|
||
}
|
||
}
|
||
|
||
# 生成备份摘要
|
||
function New-BackupSummary {
|
||
Write-Log "生成备份摘要..."
|
||
|
||
$backupPath = Join-Path $BackupDir $BackupFile
|
||
$fileSize = (Get-Item $backupPath).Length
|
||
$checksum = Get-Content "$backupPath.sha256"
|
||
|
||
$summary = @{
|
||
timestamp = (Get-Date -Format "o")
|
||
backup_file = $BackupFile
|
||
source_directory = $MinioDataDir
|
||
size = $fileSize
|
||
checksum = $checksum
|
||
status = "success"
|
||
}
|
||
|
||
$summaryPath = Join-Path $BackupDir "backup-summary-$Timestamp.json"
|
||
$summary | ConvertTo-Json | Out-File -FilePath $summaryPath -Encoding UTF8
|
||
|
||
Write-Log "✅ 备份摘要已生成" "SUCCESS"
|
||
}
|
||
|
||
# 主函数
|
||
function Main {
|
||
Write-Host "========================================"
|
||
Write-Host " 文件存储备份脚本"
|
||
Write-Host "========================================"
|
||
Write-Host ""
|
||
|
||
Test-Dependencies
|
||
Test-MinioDirectory
|
||
New-BackupDirectory
|
||
Test-DiskSpace
|
||
Start-Backup
|
||
Test-BackupFile
|
||
Remove-OldBackups
|
||
New-BackupSummary
|
||
|
||
Write-Host ""
|
||
Write-Host "========================================"
|
||
Write-Log "🎉 备份成功完成!" "SUCCESS"
|
||
Write-Host "========================================"
|
||
Write-Host ""
|
||
Write-Host "备份文件: $BackupDir\$BackupFile"
|
||
Write-Host "日志文件: $BackupDir\$LogFile"
|
||
Write-Host ""
|
||
}
|
||
|
||
# 执行主函数
|
||
Main
|