furniture-3d-system-Wu/scripts/backup-files.ps1

324 lines
10 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.

# ============================================
# 文件存储备份脚本 (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