#!/bin/bash
# ==============================================================================
# 脚本名称:sf_s3_to_hw_s3_migration.sh
# 功能描述:深S3存储桶数据直接迁移到华S3存储,支持断点续传和错误重试
# 适用环境:Debian 12.5(64位)、s5cmd ≥ v2.3.0
# 前置依赖:s5cmd已安装、源端(深S3)和目标端(华S3)网络互通
# 执行命令:sf_s3_to_hw_s3_migration.sh
# ==============================================================================# ============================== 步骤1:初始化核心配置(客户必须核对修改)==============================
# 核心配置区:根据客户实际环境调整以下参数,其他部分无需修改
###########################################################################################
# 1. 源端:深信服S3配置(必填,客户提供)
export SANGFOR_ACCESS_KEY=”” # 深信服S3 AK
export SANGFOR_SECRET_KEY=”” # 深信服S3 SK
SANGFOR_ENDPOINT=”” # 深信服S3内网endpoint
SANGFOR_BUCKET=”” # 深信服源桶名
SOURCE_PATH=”s3://$SANGFOR_BUCKET/*” # 迁移范围:桶内所有文件(支持指定目录:s3://testing/docs/*)
# 2. 目标端:华为S3配置(必填,客户提供)
export HUAWEI_ACCESS_KEY=”” # 华为S3 AK(替换为客户实际AK)
export HUAWEI_SECRET_KEY=”” # 华为S3 SK(替换为客户实际SK)
HUAWEI_ENDPOINT=”” # 华为S3内网/公网endpoint(IDC环境优先用内网)
HUAWEI_BUCKET=”s” # 华为目标桶名(需提前创建,或开启脚本自动创建)
HUAWEI_REGION=”cn-north-1″ # 华为S3区域(如cn-north-1、cn-east-2,需与endpoint匹配)
TARGET_PATH=”s3://$HUAWEI_BUCKET/” # 目标路径(默认迁移到华为桶根目录,可指定子目录:s3://桶名/子目录/)
# 3. 日志与临时文件配置(可选调整)
LOG_DIR=”/data/s3_migration/logs” # 日志存储目录(独立存放,便于排查)
LOG_FILE=”$LOG_DIR/migration_$(date +’%Y%m%d%H%M%S’).log” # 日志文件(按时间命名,避免覆盖)
TEMP_DIR=”/data/s3_migration/tmp” # 临时文件目录(存储断点续传信息,建议SSD)
# 4. s5cmd迁移性能/稳定性参数(按IDC环境优化)
NUM_WORKERS=180 # 并发线程数(10Gbps内网建议150-200,千兆网50-80)
RETRY_COUNT=5 # 失败重试次数(应对网络波动)
CHUNK_SIZE=”128MB” # 大文件分块大小(10Gbps可改为256MB,提升大文件传输效率)
TIMEOUT=”300s” # 超时时间(避免单个文件卡停)
BW_LIMIT=”0″ # 带宽限制(0=不限速,需限制填100M/200M)
AUTO_CREATE_BUCKET=”true” # 是否自动创建华为目标桶(true/false,目标桶不存在时启用)
FORCE_PATH_STYLE=”true” # 华为S3路径样式(默认true,兼容大多数S3兼容存储)
#######################################################################################
#由于s5cmd 不支持两个认证,此处定义动态认证,根据需要随时切换
set_sangfor_env()
{
export AWS_ACCESS_KEY_ID=”$SANGFOR_AK”
export AWS_SECRET_ACCESS_KEY=”$SANGFOR_SK”
}
set_huawei_env()
{
export AWS_ACCESS_KEY_ID=”$HUAWEI_ACCESS_KEY”
export AWS_SECRET_ACCESS_KEY=”$HUAWEI_SECRET_KEY”
}
unset_s3_env()
{
unset AWS_ACCESS_KEY_ID
unset AWS_SECRET_ACCESS_KEY
}
# 3. 日志与临时文件配置(可选调整)
LOG_DIR=”/data/s3_migration/logs”
LOG_FILE=”$LOG_DIR/migration_$(date +’%Y%m%d%H%M%S’).log”
TEMP_DIR=”/data/s3_migration/tmp”
# ============================== 步骤2:环境预处理(自动执行,无需修改)==============================
# 2.1 启用严格模式(遇到错误立即退出,避免批量迁移异常)
set -euo pipefail
# 2.2 配置编码(避免中文文件名/日志乱码,Debian 12.5默认UTF8,显式配置更稳妥)
export LC_ALL=C.UTF-8
echo “步骤2.2:编码配置完成,当前编码:$(locale | grep LC_CTYPE | awk -F ‘=’ ‘{print $2}’)”
# 2.3 检查是否为root权限(Debian下创建日志/临时目录需root)
if [ “$(id -u)” -ne 0 ]; then
echo “步骤2.3:请使用root权限执行脚本(sudo bash 脚本名.sh),否则可能导致目录创建/写入失败”
exit 1
fi
echo “步骤2.3:root权限校验通过”
# 2.4 检查s5cmd是否已安装(确保命令可调用)
if ! command -v s5cmd &> /dev/null; then
echo “步骤2.4:未找到s5cmd命令,请先s5cmd安装指南完成安装”
echo “快速安装命令:”
echo “wget -O /tmp/s5cmd.zip https://github.com/peak/s5cmd/releases/download/v2.3.0/s5cmd_v2.3.0_linux_amd64.zip”
echo “unzip /tmp/s5cmd.zip -d /tmp/”
echo “sudo mv /tmp/s5cmd_v2.3.0_linux_amd64/s5cmd /usr/local/bin/”
echo “sudo chmod +x /usr/local/bin/s5cmd”
exit 1
fi
echo “步骤2.4:s5cmd已安装,版本:$(s5cmd –version | grep version | awk ‘{print $3}’)”
# 2.5 创建日志/临时目录(不存在则自动创建)
for dir in “$LOG_DIR” “$TEMP_DIR”; do
if [ ! -d “$dir” ]; then
mkdir -p “$dir”
chmod 755 “$dir” # 配置读写权限(所有者rwx,其他rx)
echo “步骤2.5:目录不存在,已创建:$dir(权限:755)”
else
echo “步骤2.5:目录已存在:$dir”
fi
done
# 2.6 检查源端(深信服S3)连通性(提前验证网络和凭证)
echo -n “步骤2.6:测试深信服S3连通性(endpoint:$SANGFOR_ENDPOINT)…”
set_sangfor_env
if s5cmd -r 0 –endpoint-url “$SANGFOR_ENDPOINT” –no-verify-ssl ls “$SOURCE_PATH/” &> /dev/null; then
echo ” 成功”
else
echo ” 失败”
echo “步骤2.6:深信服S3连通性测试失败,请检查以下项:”
echo ” 1. 网络可达:ping 10.22.6.91(深信服S3 IP)”
echo ” 2. 端口开放:telnet 10.22.6.91 12000(S3端口)”
echo ” 3. AK/SK正确:核对SANGFOR_ACCESS_KEY和SANGFOR_SECRET_KEY”
echo ” 4. 桶存在:确认$SANGFOR_BUCKET桶已创建且有访问权限”
exit 1
fi
unset_s3_env
# 2.7 检查目标端(华为S3)连通性,自动创建目标桶(如需)
echo -n “步骤2.7:测试华为S3连通性(endpoint:$HUAWEI_ENDPOINT)…”
set_huawei_env
if s5cmd -r 0 –endpoint-url “$HUAWEI_ENDPOINT” –no-verify-ssl ls “$TARGET_PATH/” &> /dev/null; then
echo ” 成功”
else
echo ” 失败”
echo “步骤2.6:huawei S3连通性测试失败,请检查以下项:”
echo ” 1. 网络可达:ping 10.22.6.181(huaweiS3 IP)”
echo ” 2. 端口开放:telnet 10.22.6.181 5443(S3端口)”
echo ” 3. AK/SK正确:核对SANGFOR_ACCESS_KEY和SANGFOR_SECRET_KEY”
echo ” 4. 桶存在:确认$huawei_BUCKET桶已创建且有访问权限”
exit 1
fi
unset_s3_env
#以下代码 无法实现同步有错误:
# ============================== 步骤3:构建s5cmd迁移命令并进行迁移==============================
# 构建s5cmd迁移命令(分参数拼接,便于维护和调试)
# 核心逻辑:s5cmd直接读取深信服S3数据,写入华为S3,无需本地中转,提升效率
S5CMD_CMD=(
s5cmd
-r $RETRY_COUNT
# 目标端(华为S3)连接参数
–endpoint-url “$HUAWEI_ENDPOINT”
–no-verify-ssl
# 迁移性能/稳定性参数
–numworkers “$NUM_WORKERS”
cp
“$SOURCE_PATH/极耳揉平后的端口平面度_正负极B1/G11/2025-11-24/*” # 源端路径(深信服S3)
“$TARGET_PATH/” # 目标端路径(华为S3)
)
# 执行迁移命令(记录执行结果,$?为退出码:0=成功,非0=失败)
set_huawei_env
“${S5CMD_CMD[@]}”
unset_s3_env
#修改的代码和思路:
#由于 s5cmd 本身不支持在单条 cp 命令中为源和目标存储分别指定不同的认证信息,因此直接使用您脚本末尾的 cp 命令无法成功。您当前的代码在迁移时仅设置了华为S3的环境变量,源端深信服S3的请求会因缺少认证而失败。
#为了实现两个S3间的直接迁移,核心思路是让数据通过管道流式传输,并在这个过程中为两端分别配置认证。以下是根据您提供的配置修改后的关键代码段:
# 核心:使用管道进行流式迁移,避免本地中转
echo -e “\n=============== 步骤3:开始流式数据迁移 ===============”
echo “源端:$SOURCE_PATH/*”
echo “目标端:$TARGET_PATH/”
echo “==========================================================”
# 步骤4.1 从深信服S3获取需要迁移的文件列表
echo “正在获取源端文件列表…”
set_sangfor_env
# 获取文件列表,并保存路径
s5cmd -r 0 –endpoint-url “$SANGFOR_ENDPOINT” –no-verify-ssl ls “$SOURCE_PATH/G11/*” 2>/dev/null | awk ‘{print $NF}’ > “$TEMP_DIR/source_files.list”
SOURCE_FILE_COUNT=$(wc -l < “$TEMP_DIR/source_files.list”)
echo “找到待迁移文件数:$SOURCE_FILE_COUNT”
if [ “$SOURCE_FILE_COUNT” -eq 0 ]; then
echo “错误:未在源路径下找到任何文件,请检查路径是否正确。”
exit 1
fi
unset_s3_env
# 步骤4.2 逐文件执行流式迁移(读取源端数据,直接写入目标端)
SUCCESS_COUNT=0
FAILED_COUNT=0
INDEX=0
echo “开始迁移…”
while IFS= read -r SOURCE_FILE; do
INDEX=$((INDEX + 1))
# 计算目标文件路径(替换Bucket部分)
TARGET_FILE=”${SOURCE_FILE/$SANGFOR_BUCKET/$HUAWEI_BUCKET}”
FILENAME=$(basename “$SOURCE_FILE”)
echo -n “[$INDEX/$SOURCE_FILE_COUNT] 迁移: $FILENAME … ”
# 核心管道操作:
# 1. 使用深信服认证和端点,通过 `cat` 命令读取文件流
# 2. 使用华为认证和端点,通过 `pipe` 命令将流写入目标
set_sangfor_env
AWS_ACCESS_KEY_ID=”$SANGFOR_AK” AWS_SECRET_ACCESS_KEY=”$SANGFOR_SK” \
s5cmd –endpoint-url “$SANGFOR_ENDPOINT” –no-verify-ssl cat “$SOURCE_FILE” 2>> “$LOG_FILE.err” | \
set_huawei_env
AWS_ACCESS_KEY_ID=”$HUAWEI_ACCESS_KEY” AWS_SECRET_ACCESS_KEY=”$HUAWEI_SECRET_KEY” \
s5cmd –endpoint-url “$HUAWEI_ENDPOINT” –no-verify-ssl pipe “$TARGET_FILE” 2>> “$LOG_FILE.err”
# 检查管道命令的总体执行状态
if [ ${PIPESTATUS[0]} -eq 0 ] && [ ${PIPESTATUS[1]} -eq 0 ]; then
echo “[成功]”
SUCCESS_COUNT=$((SUCCESS_COUNT + 1))
else
echo “[失败]”
FAILED_COUNT=$((FAILED_COUNT + 1))
# 记录失败文件
echo “$SOURCE_FILE” >> “$TEMP_DIR/failed_files.list”
fi
# 清理环境变量,避免影响下一次循环
unset AWS_ACCESS_KEY_ID AWS_SECRET_ACCESS_KEY
done < “$TEMP_DIR/source_files.list”
# 步骤4.3 输出迁移摘要报告
echo -e “\n=============== 迁移完成 ===============”
echo “开始时间: $(grep -m1 “开始迁移” “$LOG_FILE” | awk -F’: ‘ ‘{print $2}’)”
echo “完成时间: $(date ‘+%Y-%m-%d %H:%M:%S’)”
echo “—————————————-”
echo “总文件数: $SOURCE_FILE_COUNT”
echo “成功迁移: $SUCCESS_COUNT”
echo “迁移失败: $FAILED_COUNT”
echo “成功率: $(awk “BEGIN {printf \”%.1f%%\”, $SUCCESS_COUNT/$SOURCE_FILE_COUNT*100}”)”
echo “========================================”
# 如果有失败文件,记录日志
if [ -f “$TEMP_DIR/failed_files.list” ]; then
FAILED_LIST_COUNT=$(wc -l < “$TEMP_DIR/failed_files.list”)
echo “有 $FAILED_LIST_COUNT 个文件迁移失败,列表详见: $TEMP_DIR/failed_files.list”
cat “$TEMP_DIR/failed_files.list” >> “$LOG_FILE”
fi
==========================================================================
#对于大数据量比如 100TB 这种量级的数据,先获取完整文件列表会消耗大量时间和I/O,确实不可行。
直接通过管道传输的核心是 s5cmd cp 命令本身支持将源端数据流直接传输到目标端,关键在于要为命令的“源端读取”和“目标端写入”这两个阶段分别注入正确的认证信息。
🚀 核心方案:使用eval动态构造环境
我们可以利用 eval 命令,在单个命令行中为源端和目标端分别设置环境变量,实现真正的直接迁移。
echo "=============== 步骤4:开始直接数据迁移 (无列表) ===============" echo "源路径: $SOURCE_PATH/*" echo "目标路径: $TARGET_PATH/" echo "==================================================================" # 核心迁移命令 # 原理:使用eval,在命令执行前临时设置环境变量。 # 前半部分(AWS_*变量)影响s5cmd对源端的认证读取, # 后半部分(--endpoint-url)影响s5cmd对目标端的连接写入。 eval " AWS_ACCESS_KEY_ID=\"$SANGFOR_AK\" \ AWS_SECRET_ACCESS_KEY=\"$SANGFOR_SK\" \ s5cmd \ --endpoint-url \"$HUAWEI_ENDPOINT\" \ --no-verify-ssl \ -r \"$RETRY_COUNT\" \ --numworkers \"$NUM_WORKERS\" \ cp \ \"$SOURCE_PATH/*\" \ \"$TARGET_PATH/\" " # 检查命令执行状态 if [ $? -eq 0 ]; then echo "迁移命令已成功发起。" echo "提示:对于100TB数据,此命令将运行很长时间。" echo "请使用 'tail -f $LOG_FILE' 监控后台日志,或通过系统监控工具观察网络流量。" else echo "错误:迁移命令启动失败。" exit 1 fi
💡 方案原理与备选方案
上面的“核心方案”是最直接的方法。下表对比了它的原理和其他几种备选方案,帮助你全面理解和选择:
| 方案 | 核心思路 | 具体做法 | 优点 | 缺点 |
|---|---|---|---|---|
| 核心方案:eval动态环境 | 利用Shell的eval,在一条s5cmd cp命令前为源端设置AK/SK,用--endpoint-url参数指定目标端地址。 |
如上文代码块所示。 | 最简洁高效,一条命令处理所有文件,能充分利用s5cmd的并发(NUM_WORKERS)。 |
1. 对Shell环境敏感。 2. 所有错误输出混在一起。 |
| 备选1:使用named pipe | 创建一个命名管道作为“桥梁”。一个进程用源端凭证读取数据写入管道,另一个进程用目标端凭证从管道读取并写入。 | mkfifo /tmp/s3pipe启动两个 s5cmd进程分别操作管道。 |
进程分离,日志清晰,流控灵活。 | 步骤稍复杂,需管理两个进程,性能调优更麻烦。 |
| 备选2:分目录批次迁移 | 如果源路径下有子目录,可分批迁移,减小单次操作粒度。 | 先获取目录列表,再循环对每个子目录执行“核心方案”。 | 风险分散,便于暂停、重试和进度跟踪。 | 仍需要获取一次目录列表,但比文件列表快得多。 |
如何选择?
-
直接启动迁移:追求最简单,用核心方案。
-
需要更清晰的控制和日志:选择备选1(named pipe)。
-
目录结构清晰,想分批管理:选择备选2(分目录批次迁移)。
⚠️ 迁移前后重要检查(尤其针对100TB数据)
-
空间验证:执行前,请务必确认目标端华为S3的存储空间大于100TB,并有适当冗余。
-
网络验证:在脚本的连通性检查部分,可以增加简单的带宽测试(例如,用
s5cmd cp迁移一个几百MB的测试文件),以预估总耗时。 -
数据一致性校验:由于跳过了列表检查,强烈建议迁移完成后进行抽样校验。可以在脚本最后添加一个校验函数,随机选取一定数量的文件,比较源端和目标端的文件大小或ETag。
===================================修改的代码断为:==============
#以下代码 无法实现同步有错误:
# 构建s5cmd迁移命令(分参数拼接,便于维护和调试)
#另一种修改代码思路:
===========================================
-
直接传输:不先获取完整文件列表,避免40TB数据列出时间过长
-
正确认证:为源端(深信服)和目标端(华为)分别使用不同的认证信息
以下是重构后的代码:
# ============================== 步骤3::构建并执行迁移命令 =============================== echo "=============== 步骤4:开始数据迁移 ===============" echo "源路径: $SOURCE_PATH/极耳揉平后的端口平面度_正负极B1/G11/2025-11-24/*" echo "目标路径: $TARGET_PATH/" echo "并发数: $NUM_WORKERS, 重试次数: $RETRY_COUNT" echo "==================================================" # 方法:使用s5cmd的cp命令配合管道流式传输 # 核心思路:通过环境变量为源端认证,通过--endpoint-url为目标端指定端点 # 这样s5cmd会: # 1. 使用AWS_ACCESS_KEY_ID/AWS_SECRET_ACCESS_KEY认证源端(深信服) # 2. 使用--endpoint-url指定的端点连接目标端(华为) # 设置源端认证环境变量 set_sangfor_env # 执行迁移命令 # 注意:这里的关键是s5cmd的cp命令会: # - 使用当前环境变量(AWS_ACCESS_KEY_ID/AWS_SECRET_ACCESS_KEY)来读取源端数据 # - 使用--endpoint-url参数来指定写入目标端的地址 # 但s5cmd的cp命令目前版本可能不支持在命令中同时为源和目标指定不同的端点 # 因此我们采用替代方案:使用s5cmd cat读取源端数据,通过管道传递给s5cmd pipe写入目标端 echo "正在准备迁移,请稍候..." # 使用find命令配合xargs进行并行迁移 # 步骤1:使用源端认证递归列出所有文件,然后通过管道逐批处理 AWS_ACCESS_KEY_ID="$SANGFOR_AK" AWS_SECRET_ACCESS_KEY="$SANGFOR_SK" \ s5cmd --endpoint-url "$SANGFOR_ENDPOINT" --no-verify-ssl \ ls --recursive "$SOURCE_PATH/极耳揉平后的端口平面度_正负极B1/G11/2025-11-24/*" 2>/dev/null | \ awk '{print $NF}' | \ xargs -P $NUM_WORKERS -I {} bash -c ' SOURCE_FILE="$1" # 从源文件路径中提取相对路径 RELATIVE_PATH="${SOURCE_FILE#s3://'$SANGFOR_BUCKET'/}" TARGET_FILE="s3://'$HUAWEI_BUCKET'/$RELATIVE_PATH" echo "迁移: $(basename "$SOURCE_FILE")" # 使用管道流式传输:源端读取 -> 目标端写入 AWS_ACCESS_KEY_ID="'$SANGFOR_AK'" AWS_SECRET_ACCESS_KEY="'$SANGFOR_SK'" \ s5cmd --endpoint-url "'$SANGFOR_ENDPOINT'" --no-verify-ssl cat "$SOURCE_FILE" 2>/dev/null | \ AWS_ACCESS_KEY_ID="'$HUAWEI_ACCESS_KEY'" AWS_SECRET_ACCESS_KEY="'$HUAWEI_SECRET_KEY'" \ s5cmd --endpoint-url "'$HUAWEI_ENDPOINT'" --no-verify-ssl pipe "$TARGET_FILE" 2>/dev/null # 检查传输状态 if [ ${PIPESTATUS[0]} -eq 0 ] && [ ${PIPESTATUS[1]} -eq 0 ]; then echo " 成功: $(basename "$SOURCE_FILE")" echo "$SOURCE_FILE -> $TARGET_FILE: 成功" >> "'$LOG_FILE'.success" else echo " 失败: $(basename "$SOURCE_FILE")" echo "$SOURCE_FILE -> $TARGET_FILE: 失败" >> "'$LOG_FILE'.failed" fi ' _ {} # 清理环境变量 unset_s3_env echo "迁移命令执行完成!" echo "成功记录: $LOG_FILE.success" echo "失败记录: $LOG_FILE.failed" echo "=================================================="
核心改进点:
-
无文件列表延迟:使用
s5cmd ls --recursive直接输出文件流,通过管道立即处理,不生成中间列表文件 -
并行流式传输:使用
xargs -P $NUM_WORKERS实现并行处理,每个文件独立通过管道传输 -
分离认证:
-
管道前半部分:使用深信服AK/SK和端点读取数据
-
管道后半部分:使用华为AK/SK和端点写入数据
-
-
实时进度反馈:每个文件迁移时输出状态,便于监控
-
结果记录:分别记录成功和失败的文件,便于后续处理
如果需要更高效的批量传输(减少进程开销):
# 使用s5cmd的run命令批量处理(更高效,但需要生成命令文件) echo "生成批量迁移命令文件..." # 创建命令文件 CMD_FILE="$TEMP_DIR/migration_commands.txt" echo "# 迁移命令文件 - 生成时间: $(date)" > "$CMD_FILE" # 使用源端认证生成迁移命令 set_sangfor_env AWS_ACCESS_KEY_ID="$SANGFOR_AK" AWS_SECRET_ACCESS_KEY="$SANGFOR_SK" \ s5cmd --endpoint-url "$SANGFOR_ENDPOINT" --no-verify-ssl \ ls --recursive "$SOURCE_PATH/极耳揉平后的端口平面度_正负极B1/G11/2025-11-24/*" 2>/dev/null | \ awk -v src_bucket="$SANGFOR_BUCKET" -v dst_bucket="$HUAWEI_BUCKET" ' /s3:\/\// { src_file = $NF # 构建目标路径 gsub("s3://" src_bucket "/", "s3://" dst_bucket "/", src_file) # 生成管道命令 printf "AWS_ACCESS_KEY_ID=%s AWS_SECRET_ACCESS_KEY=%s s5cmd --endpoint-url=%s --no-verify-ssl cat \"%s\" | ", "'"$SANGFOR_AK"'", "'"$SANGFOR_SK"'", "'"$SANGFOR_ENDPOINT"'", $NF printf "AWS_ACCESS_KEY_ID=%s AWS_SECRET_ACCESS_KEY=%s s5cmd --endpoint-url=%s --no-verify-ssl pipe \"%s\"\n", "'"$HUAWEI_ACCESS_KEY"'", "'"$HUAWEI_SECRET_KEY"'", "'"$HUAWEI_ENDPOINT"'", src_file } ' >> "$CMD_FILE" unset_s3_env # 使用GNU parallel并行执行(如果可用) if command -v parallel &> /dev/null; then echo "使用GNU parallel执行批量迁移..." parallel -j $NUM_WORKERS --progress --joblog "$LOG_FILE.parallel.log" < "$CMD_FILE" else echo "使用bash并行执行(每个文件独立进程)..." # 将命令文件分发给多个并行进程 cat "$CMD_FILE" | grep -v "^#" | xargs -P $NUM_WORKERS -I {} bash -c "{} 2>/dev/null && echo '成功' || echo '失败'" fi
第一个方案更简洁直接,适合大多数场景;第二个方案更高效但需要GNU parallel工具。建议根据你的环境选择合适的方案。
原创文章,作者:奋斗,如若转载,请注明出处:https://blog.ytso.com/tech/aiops/318447.html