大文件仓库上传AWS Glacier时的压缩同步优化方案咨询
完全可行!针对你这种数百万文件、本地存储空间吃紧的备份场景,我们可以通过分目录压缩→上传验证→归档确认→即时清理的流程来实现——既减少了S3 API调用次数(压缩成单个文件比传数百万小文件成本低得多),又能避免压缩包长期占用本地空间。下面是两种具体的实现方案,你可以根据自己的技术栈选择:
方案一:Shell 脚本(基于 AWS CLI + 原生工具)
如果你熟悉命令行,这个方案零额外依赖,直接用系统自带的tar、find加上AWS CLI就能搞定,上手快。
完整脚本逻辑
#!/bin/bash set -euo pipefail BACKUP_BUCKET="backup" LOG_FILE="backup_sync.log" # 日志函数 log() { echo "[$(date '+%Y-%m-%d %H:%M:%S')] $1" >> "$LOG_FILE" } # 遍历当前目录下的一级子目录(可根据你的目录结构调整-depth参数) find . -type d -depth 1 -print0 | while IFS= read -r -d '' dir; do dir_name=$(basename "$dir") tarball="${dir_name}.tar.gz" log "开始处理目录:$dir_name" # 1. 压缩目录(用xz替换gz可获得更高压缩率,速度稍慢) log "正在压缩目录:$dir_name → $tarball" if ! tar -czf "$tarball" -C "$(dirname "$dir")" "$dir_name"; then log "压缩失败:$dir_name" continue fi # 2. 上传压缩包到S3 DEEP_ARCHIVE log "正在上传压缩包:$tarball" if ! aws s3 cp "$tarball" "s3://${BACKUP_BUCKET}/${tarball}" --storage-class DEEP_ARCHIVE; then log "上传失败:$tarball" continue fi # 3. 等待归档完成(DEEP_ARCHIVE是异步归档,需确认状态) log "等待归档完成:$tarball" archive_status="" while true; do archive_status=$(aws s3api head-object --bucket "$BACKUP_BUCKET" --key "$tarball" --query 'ArchiveStatus' --output text 2>/dev/null) if [ "$archive_status" = "COMPLETED" ]; then log "归档完成:$tarball" break elif [ "$archive_status" = "FAILED" ]; then log "归档失败:$tarball" break else log "归档中,10分钟后重试..." sleep 600 fi done # 4. 验证上传完整性(可选但强烈推荐) if [ "$archive_status" = "COMPLETED" ]; then log "验证上传完整性:$tarball" local_hash=$(sha256sum "$tarball" | awk '{print $1}') s3_hash=$(aws s3 cp "s3://${BACKUP_BUCKET}/${tarball}" - | sha256sum | awk '{print $1}') if [ "$local_hash" = "$s3_hash" ]; then log "验证通过,删除本地压缩包:$tarball" rm -f "$tarball" else log "验证失败,保留压缩包:$tarball" fi else log "归档未完成,保留压缩包:$tarball" fi done log "所有目录处理完毕"
使用说明
- 把脚本保存为
s3_glacier_backup.sh,赋予执行权限:chmod +x s3_glacier_backup.sh - 调整
BACKUP_BUCKET为你的S3桶名,根据需要修改压缩格式(-czf是gzip,换成-cJf是xz) - 运行脚本:
./s3_glacier_backup.sh,所有操作会记录到backup_sync.log中
方案二:Python 脚本(更灵活,适合复杂场景)
如果你的目录结构复杂,或者需要并行处理、精细的错误重试,用Python结合boto3库会更灵活。
核心代码示例
import os import tarfile import boto3 import time import hashlib from datetime import datetime def log(message): timestamp = datetime.now().strftime("%Y-%m-%d %H:%M:%S") print(f"[{timestamp}] {message}") def compress_directory(dir_path): dir_name = os.path.basename(dir_path) tarball_name = f"{dir_name}.tar.gz" try: with tarfile.open(tarball_name, 'w:gz') as tar: tar.add(dir_path, arcname=dir_name) log(f"压缩完成:{tarball_name}") return tarball_name except Exception as e: log(f"压缩失败:{dir_name} - {str(e)}") return None def upload_and_wait_archive(s3_client, bucket_name, tarball_path): tarball_name = os.path.basename(tarball_path) try: s3_client.upload_file( tarball_path, bucket_name, tarball_name, ExtraArgs={'StorageClass': 'DEEP_ARCHIVE'} ) log(f"上传完成:{tarball_name}") # 等待归档完成 while True: response = s3_client.head_object(Bucket=bucket_name, Key=tarball_name) archive_status = response.get('ArchiveStatus') if archive_status == 'COMPLETED': log(f"归档完成:{tarball_name}") return True elif archive_status == 'FAILED': log(f"归档失败:{tarball_name}") return False else: log(f"归档中,10分钟后重试...") time.sleep(600) except Exception as e: log(f"上传失败:{tarball_name} - {str(e)}") return False def verify_upload(s3_client, bucket_name, tarball_path): tarball_name = os.path.basename(tarball_path) try: # 计算本地文件哈希 with open(tarball_path, 'rb') as f: local_hash = hashlib.sha256(f.read()).hexdigest() # 计算S3文件哈希 s3_object = s3_client.get_object(Bucket=bucket_name, Key=tarball_name) s3_hash = hashlib.sha256(s3_object['Body'].read()).hexdigest() if local_hash == s3_hash: log(f"验证通过:{tarball_name}") return True else: log(f"验证失败:{tarball_name}(本地哈希:{local_hash} | S3哈希:{s3_hash})") return False except Exception as e: log(f"验证出错:{tarball_name} - {str(e)}") return False if __name__ == "__main__": BUCKET_NAME = "backup" s3 = boto3.client('s3') # 遍历当前目录下的子目录(跳过隐藏目录) for item in os.listdir('.'): item_path = os.path.join('.', item) if os.path.isdir(item_path) and not item.startswith('.'): log(f"开始处理目录:{item}") tarball = compress_directory(item_path) if not tarball: continue if upload_and_wait_archive(s3, BUCKET_NAME, tarball): if verify_upload(s3, BUCKET_NAME, tarball): os.remove(tarball) log(f"已删除本地压缩包:{tarball}") else: log(f"验证失败,保留压缩包:{tarball}") else: log(f"归档未完成,保留压缩包:{tarball}") log("所有目录处理完成")
使用说明
- 安装依赖:
pip install boto3 - 配置AWS凭证(可以用
aws configure或者环境变量) - 修改
BUCKET_NAME为你的S3桶名,运行脚本即可
关键注意事项
- 并行处理:如果服务器有足够的CPU和带宽,可以加入并行逻辑(比如shell用
xargs -P,Python用concurrent.futures),但要注意不要同时处理过多目录导致磁盘IO或网络拥堵。 - 错误重试:对于上传或归档失败的情况,建议添加重试逻辑(比如shell用
until循环,Python用tenacity库),避免单次失败就中断流程。 - 目录结构映射:如果需要在S3上保留原目录层级,上传时要把压缩包放到对应的路径下(比如
s3://backup/path/to/dir.tar.gz),可以在脚本中动态生成S3键。 - 磁盘空间监控:压缩前建议检查本地剩余磁盘空间,确保能放下当前目录的压缩包,避免中途磁盘满了导致失败。
- 成本优化:DEEP_ARCHIVE的检索成本较高,一定要确保压缩包上传完整,避免后续需要重新上传或检索。
内容的提问来源于stack exchange,提问作者Omroth
相关产品推荐
相关产品推荐

