Cloudflare R2备份脚本
📜 完整脚本源码
#!/bin/bash
# Cloudflare R2备份脚本
# 支持交互式选择和命令行参数调用
# 使用方法: sh r2_backup.sh [选项]
# 选项: 1 - 全量备份
# 颜色定义
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
BLUE='\033[0;34m'
NC='\033[0m' # No Color
# R2配置变量
R2_BUCKET_NAME="bucket_name"
R2_ENDPOINT="https://xxx.r2.cloudflarestorage.com"
R2_ACCESS_KEY_ID="xxx"
R2_ACCESS_KEY_SECRET="xxx"
R2_REGION="WNAM"
R2_REMOTE_NAME="cloudflare-r2"
# 备份配置
BACKUP_TARGET_DIR="/home/r2_backup_$(date +%Y%m%d_%H%M%S)" # 本地备份目录(带时间戳)
# 备份整个存储桶,保持完整目录结构
# 日志函数
log_info() {
echo -e "${BLUE}[INFO]${NC} $1"
}
log_success() {
echo -e "${GREEN}[SUCCESS]${NC} $1"
}
log_warning() {
echo -e "${YELLOW}[WARNING]${NC} $1"
}
log_error() {
echo -e "${RED}[ERROR]${NC} $1"
}
# 检查rclone是否已安装
check_rclone() {
log_info "检查rclone是否已安装..."
if command -v rclone &> /dev/null; then
local version=$(rclone version | head -n 1)
log_success "rclone已安装: $version"
return 0
else
log_warning "rclone未安装"
return 1
fi
}
# 安装rclone
install_rclone() {
log_info "开始安装rclone..."
# 检查系统类型
if [[ -f /etc/redhat-release ]] || [[ -f /etc/centos-release ]] || [[ -f /etc/alios-release ]]; then
# CentOS/RHEL/Alibaba Cloud Linux
log_info "检测到CentOS/RHEL/Alibaba Cloud Linux系统"
# 使用官方推荐的安装方法
curl https://rclone.org/install.sh | sudo bash
if [[ $? -eq 0 ]]; then
log_success "rclone安装成功"
else
log_error "rclone安装失败"
exit 1
fi
else
log_error "不支持的系统类型,请手动安装rclone"
exit 1
fi
}
# 配置rclone for Cloudflare R2
configure_rclone() {
log_info "配置rclone连接到Cloudflare R2..."
# 检查配置是否已存在,静默覆盖
if rclone listremotes | grep -q "^${R2_REMOTE_NAME}:$"; then
log_info "rclone配置 '${R2_REMOTE_NAME}' 已存在,将更新配置"
fi
# 创建rclone配置
log_info "创建rclone配置文件..."
# 确保配置目录存在
mkdir -p ~/.config/rclone
# 生成配置内容
cat > ~/.config/rclone/rclone.conf << EOF
[${R2_REMOTE_NAME}]
type = s3
provider = Cloudflare
access_key_id = ${R2_ACCESS_KEY_ID}
secret_access_key = ${R2_ACCESS_KEY_SECRET}
region = ${R2_REGION}
endpoint = ${R2_ENDPOINT}
acl = private
EOF
# 测试连接
log_info "测试rclone连接..."
if rclone lsd ${R2_REMOTE_NAME}: &> /dev/null; then
log_success "rclone配置成功,连接测试通过"
else
log_error "rclone配置失败,连接测试未通过"
exit 1
fi
}
# 全量备份函数
full_backup() {
log_info "开始执行全量备份..."
log_info "备份源: ${R2_REMOTE_NAME}:${R2_BUCKET_NAME}/ (整个存储桶)"
log_info "备份目标目录: ${BACKUP_TARGET_DIR}"
# 创建本地备份目录
mkdir -p "${BACKUP_TARGET_DIR}"
# 创建备份日志目录
local log_dir="/var/log/r2_backup"
sudo mkdir -p "$log_dir"
local log_file="$log_dir/backup_$(date +%Y%m%d_%H%M%S).log"
# 执行备份 - 备份整个存储桶,保持完整目录结构
log_info "开始从Cloudflare R2同步整个存储桶到本地..."
rclone sync "${R2_REMOTE_NAME}:${R2_BUCKET_NAME}/" "${BACKUP_TARGET_DIR}/" \
--progress \
--transfers 4 \
--checkers 8 \
--retries 3 \
--low-level-retries 10 \
--stats 30s \
--log-file "$log_file" \
--log-level INFO
if [[ $? -eq 0 ]]; then
log_success "全量备份完成"
log_info "备份日志: $log_file"
# 显示备份统计信息
log_info "获取备份统计信息..."
rclone size "${R2_REMOTE_NAME}:${R2_BUCKET_NAME}/${BACKUP_REMOTE_DIR}"
else
log_error "全量备份失败"
log_error "请查看日志文件: $log_file"
exit 1
fi
}
# 显示菜单
show_menu() {
echo
echo "=================================="
echo " Cloudflare R2 数据备份工具"
echo " (从R2下载数据到本地服务器)"
echo "=================================="
echo "1. 全量备份 (从R2同步到本地)"
echo "2. 查看备份状态"
echo "3. 测试R2连接"
echo "4. 删除R2目录"
echo "5. 重新配置rclone"
echo "0. 退出"
echo "=================================="
}
# 查看备份列表
list_backups() {
log_info "查看备份列表..."
echo
echo "=== 本地备份目录 ==="
if [[ -d "${BACKUP_TARGET_DIR}" ]]; then
ls -la "${BACKUP_TARGET_DIR}"
echo
echo "备份目录大小:"
du -sh "${BACKUP_TARGET_DIR}"
else
echo "本地备份目录不存在: ${BACKUP_TARGET_DIR}"
fi
echo
echo "=== R2存储桶内容 ==="
rclone lsd "${R2_REMOTE_NAME}:${R2_BUCKET_NAME}" --max-depth 2
}
# 测试连接
test_connection() {
log_info "测试Cloudflare R2连接..."
if rclone lsd "${R2_REMOTE_NAME}:" &> /dev/null; then
log_success "连接测试成功"
rclone about "${R2_REMOTE_NAME}:"
else
log_error "连接测试失败"
fi
}
# 删除R2存储桶中的指定顶层目录
delete_directory() {
log_info "删除R2存储桶中的顶层目录..."
echo
echo "=== R2存储桶顶层目录列表 ==="
# 获取顶层目录列表
local dirs=$(rclone lsd "${R2_REMOTE_NAME}:${R2_BUCKET_NAME}" --max-depth 1 2>/dev/null | awk '{print $5}' | grep -v '^$')
if [[ -z "$dirs" ]]; then
log_warning "存储桶中没有找到任何目录"
return 1
fi
# 显示目录列表
local dir_array=()
local index=1
while IFS= read -r dir; do
if [[ -n "$dir" ]]; then
echo "$index. $dir"
dir_array+=("$dir")
((index++))
fi
done <<< "$dirs"
echo "0. 取消操作"
echo
# 用户选择
read -p "请选择要删除的目录编号 [0-$((index-1))]: " choice
# 验证输入
if [[ ! "$choice" =~ ^[0-9]+$ ]] || [[ "$choice" -lt 0 ]] || [[ "$choice" -ge "$index" ]]; then
log_error "无效的选择"
return 1
fi
# 取消操作
if [[ "$choice" -eq 0 ]]; then
log_info "取消删除操作"
return 0
fi
# 获取选择的目录
local selected_dir="${dir_array[$((choice-1))]}"
# 二次确认
echo
log_warning "警告:即将删除目录 '${selected_dir}' 及其所有内容"
log_warning "此操作不可逆!"
read -p "确认删除? 请输入 'DELETE' 来确认: " confirm
if [[ "$confirm" != "DELETE" ]]; then
log_info "取消删除操作"
return 0
fi
# 执行删除
log_info "正在删除目录: ${selected_dir}"
if rclone purge "${R2_REMOTE_NAME}:${R2_BUCKET_NAME}/${selected_dir}" 2>/dev/null; then
log_success "目录 '${selected_dir}' 删除成功"
else
log_error "目录 '${selected_dir}' 删除失败"
return 1
fi
}
# 主函数
main() {
echo "Cloudflare R2备份脚本启动..."
# 处理命令行参数 - 直接执行模式
if [[ $# -gt 0 ]]; then
# 检查并安装rclone
if ! check_rclone; then
install_rclone
fi
# 配置rclone
configure_rclone
case $1 in
1)
full_backup
exit 0
;;
2)
list_backups
exit 0
;;
3)
test_connection
exit 0
;;
4)
delete_directory
exit 0
;;
5)
configure_rclone
exit 0
;;
*)
log_error "无效的参数: $1"
echo "使用方法: $0 [1|2|3|4|5]"
echo "1 - 全量备份"
echo "2 - 查看备份列表"
echo "3 - 测试连接"
echo "4 - 删除R2目录"
echo "5 - 重新配置rclone"
exit 1
;;
esac
fi
# 交互式菜单模式
while true; do
show_menu
read -p "请选择操作 [0-4]: " choice
case $choice in
1)
# 检查并安装rclone
if ! check_rclone; then
install_rclone
fi
# 配置rclone
configure_rclone
full_backup
;;
2)
# 检查并安装rclone
if ! check_rclone; then
install_rclone
fi
# 配置rclone
configure_rclone
list_backups
;;
3)
# 检查并安装rclone
if ! check_rclone; then
install_rclone
fi
# 配置rclone
configure_rclone
test_connection
;;
4)
# 检查并安装rclone
if ! check_rclone; then
install_rclone
fi
# 配置rclone
configure_rclone
delete_directory
;;
5)
# 检查并安装rclone
if ! check_rclone; then
install_rclone
fi
configure_rclone
;;
0)
log_info "退出程序"
exit 0
;;
*)
log_error "无效选择,请重新输入"
;;
esac
echo
read -p "按回车键继续..." -r
done
}
# 脚本入口
main "$@"
📋 项目介绍
这是一个功能完整的 Cloudflare R2 存储桶管理工具,支持数据备份、目录删除等操作。该脚本基于 rclone 工具开发,提供了友好的交互式界面和命令行参数支持。
✨ 功能特性
- 🔄 全量备份: 从 Cloudflare R2 存储桶同步数据到本地服务器
- 📊 状态查看: 查看本地备份和远程存储桶状态
- 🔗 连接测试: 测试与 Cloudflare R2 的连接状态
- 🗑️ 目录删除: 安全删除 R2 存储桶中的指定目录
- ⚙️ 配置管理: 自动安装和配置 rclone
- 📝 日志记录: 详细的操作日志和彩色输出
- 🎛️ 双模式支持: 交互式菜单 + 命令行参数
🛠️ 环境要求
系统支持
- CentOS 7+
- RHEL 7+
- Alibaba Cloud Linux
- 其他 Linux 发行版(需手动安装 rclone)
依赖工具
curl- 用于下载 rclonesudo- 管理员权限bash- Shell 解释器
📋 配置说明
R2 配置参数
在使用脚本前,需要修改以下配置变量:
R2_BUCKET_NAME="your-bucket-name" # R2 存储桶名称
R2_ENDPOINT="https://xxx.r2.cloudflarestorage.com" # R2 端点地址
R2_ACCESS_KEY_ID="your-access-key" # 访问密钥 ID
R2_ACCESS_KEY_SECRET="your-secret-key" # 访问密钥
R2_REGION="WNAM" # 区域设置
获取 R2 配置信息
- 登录 Cloudflare Dashboard
- 进入 R2 Object Storage
- 选择您的存储桶
- 在 Settings 中获取端点地址
- 在 API tokens 中创建并获取访问密钥
🚀 使用方法
交互式模式
# 启动交互式菜单
sh r2_backup.sh
命令行模式
# 全量备份
sh r2_backup.sh 1
# 查看备份状态
sh r2_backup.sh 2
# 测试连接
sh r2_backup.sh 3
# 删除R2目录
sh r2_backup.sh 4
# 重新配置rclone
sh r2_backup.sh 5
📖 功能详解
1. 全量备份
- 从 R2 存储桶完整同步数据到本地
- 保持原有目录结构
- 自动创建带时间戳的备份目录
- 支持断点续传和重试机制
2. 查看备份状态
- 显示本地备份目录内容和大小
- 展示 R2 存储桶结构
- 提供详细的统计信息
3. 测试连接
- 验证 rclone 配置正确性
- 测试网络连接状态
- 显示存储桶基本信息
4. 删除 R2 目录
- 列出存储桶顶层目录
- 交互式选择删除目标
- 二次确认防止误操作
- 安全删除指定目录及内容
5. 配置管理
- 自动检测和安装 rclone
- 生成 R2 连接配置
- 验证配置有效性
📂 目录结构
/home/r2_backup_YYYYMMDD_HHMMSS/ # 备份根目录
├── [存储桶内容] # 保持原有结构
/var/log/r2_backup/ # 日志目录
├── backup_YYYYMMDD_HHMMSS.log # 备份日志
~/.config/rclone/ # rclone配置
├── rclone.conf # 配置文件
⚠️ 注意事项
安全提醒
- 访问密钥安全: 确保 R2 访问密钥的安全性,不要泄露
- 删除确认: 删除操作不可逆,请谨慎操作
- 权限管理: 脚本需要 sudo 权限进行系统操作
性能优化
- 网络带宽: 大文件传输需要稳定的网络环境
- 存储空间: 确保本地有足够的存储空间
- 并发设置: 可根据服务器性能调整
--transfers和--checkers参数
备份策略
- 定期备份: 建议设置定时任务进行定期备份
- 监控日志: 定期检查备份日志确保操作成功
- 版本管理: 考虑实现备份版本管理机制
🛠️ 故障排除
常见问题
Q: rclone 安装失败
# 手动安装 rclone
curl https://rclone.org/install.sh | sudo bash
Q: 连接测试失败
- 检查网络连接
- 验证 R2 配置参数
- 确认访问密钥权限
Q: 备份中断
- 检查磁盘空间
- 查看网络状态
- 重新运行备份命令
Q: 权限不足
# 确保脚本有执行权限
chmod +x r2_backup.sh
日志分析
备份日志位于 /var/log/r2_backup/ 目录下,包含详细的操作记录:
# 查看最新日志
tail -f /var/log/r2_backup/backup_*.log
# 搜索错误信息
grep -i error /var/log/r2_backup/backup_*.log
🔧 高级配置
自定义 rclone 参数
可在脚本中修改以下参数以优化性能:
--transfers 4 # 并发传输数量
--checkers 8 # 并发检查器数量
--retries 3 # 重试次数
--low-level-retries 10 # 底层重试次数
--stats 30s # 统计更新间隔
定时备份设置
使用 crontab 设置定时备份:
# 编辑 crontab
crontab -e
# 每天凌晨 2 点执行全量备份
0 2 * * * /path/to/r2_backup.sh 1 >> /var/log/r2_backup/cron.log 2>&1
📜 完整脚本源码
#!/bin/bash
# Cloudflare R2备份脚本
# 支持交互式选择和命令行参数调用
# 使用方法: sh r2_backup.sh [选项]
# 选项: 1 - 全量备份
# 颜色定义
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
BLUE='\033[0;34m'
NC='\033[0m' # No Color
# R2配置变量
R2_BUCKET_NAME="twenhub-bucket"
R2_ENDPOINT="https://xxx.r2.cloudflarestorage.com"
R2_ACCESS_KEY_ID="xxx"
R2_ACCESS_KEY_SECRET="xxx"
R2_REGION="WNAM"
R2_REMOTE_NAME="cloudflare-r2"
# 备份配置
BACKUP_TARGET_DIR="/home/r2_backup_$(date +%Y%m%d_%H%M%S)" # 本地备份目录(带时间戳)
# 备份整个存储桶,保持完整目录结构
# 日志函数
log_info() {
echo -e "${BLUE}[INFO]${NC} $1"
}
log_success() {
echo -e "${GREEN}[SUCCESS]${NC} $1"
}
log_warning() {
echo -e "${YELLOW}[WARNING]${NC} $1"
}
log_error() {
echo -e "${RED}[ERROR]${NC} $1"
}
# 检查rclone是否已安装
check_rclone() {
log_info "检查rclone是否已安装..."
if command -v rclone &> /dev/null; then
local version=$(rclone version | head -n 1)
log_success "rclone已安装: $version"
return 0
else
log_warning "rclone未安装"
return 1
fi
}
# 安装rclone
install_rclone() {
log_info "开始安装rclone..."
# 检查系统类型
if [[ -f /etc/redhat-release ]] || [[ -f /etc/centos-release ]] || [[ -f /etc/alios-release ]]; then
# CentOS/RHEL/Alibaba Cloud Linux
log_info "检测到CentOS/RHEL/Alibaba Cloud Linux系统"
# 使用官方推荐的安装方法
curl https://rclone.org/install.sh | sudo bash
if [[ $? -eq 0 ]]; then
log_success "rclone安装成功"
else
log_error "rclone安装失败"
exit 1
fi
else
log_error "不支持的系统类型,请手动安装rclone"
exit 1
fi
}
# 配置rclone for Cloudflare R2
configure_rclone() {
log_info "配置rclone连接到Cloudflare R2..."
# 检查配置是否已存在,静默覆盖
if rclone listremotes | grep -q "^${R2_REMOTE_NAME}:$"; then
log_info "rclone配置 '${R2_REMOTE_NAME}' 已存在,将更新配置"
fi
# 创建rclone配置
log_info "创建rclone配置文件..."
# 确保配置目录存在
mkdir -p ~/.config/rclone
# 生成配置内容
cat > ~/.config/rclone/rclone.conf << EOF
[${R2_REMOTE_NAME}]
type = s3
provider = Cloudflare
access_key_id = ${R2_ACCESS_KEY_ID}
secret_access_key = ${R2_ACCESS_KEY_SECRET}
region = ${R2_REGION}
endpoint = ${R2_ENDPOINT}
acl = private
EOF
# 测试连接
log_info "测试rclone连接..."
if rclone lsd ${R2_REMOTE_NAME}: &> /dev/null; then
log_success "rclone配置成功,连接测试通过"
else
log_error "rclone配置失败,连接测试未通过"
exit 1
fi
}
# 全量备份函数
full_backup() {
log_info "开始执行全量备份..."
log_info "备份源: ${R2_REMOTE_NAME}:${R2_BUCKET_NAME}/ (整个存储桶)"
log_info "备份目标目录: ${BACKUP_TARGET_DIR}"
# 创建本地备份目录
mkdir -p "${BACKUP_TARGET_DIR}"
# 创建备份日志目录
local log_dir="/var/log/r2_backup"
sudo mkdir -p "$log_dir"
local log_file="$log_dir/backup_$(date +%Y%m%d_%H%M%S).log"
# 执行备份 - 备份整个存储桶,保持完整目录结构
log_info "开始从Cloudflare R2同步整个存储桶到本地..."
rclone sync "${R2_REMOTE_NAME}:${R2_BUCKET_NAME}/" "${BACKUP_TARGET_DIR}/" \
--progress \
--transfers 4 \
--checkers 8 \
--retries 3 \
--low-level-retries 10 \
--stats 30s \
--log-file "$log_file" \
--log-level INFO
if [[ $? -eq 0 ]]; then
log_success "全量备份完成"
log_info "备份日志: $log_file"
# 显示备份统计信息
log_info "获取备份统计信息..."
rclone size "${R2_REMOTE_NAME}:${R2_BUCKET_NAME}/${BACKUP_REMOTE_DIR}"
else
log_error "全量备份失败"
log_error "请查看日志文件: $log_file"
exit 1
fi
}
# 显示菜单
show_menu() {
echo
echo "=================================="
echo " Cloudflare R2 数据备份工具"
echo " (从R2下载数据到本地服务器)"
echo "=================================="
echo "1. 全量备份 (从R2同步到本地)"
echo "2. 查看备份状态"
echo "3. 测试R2连接"
echo "4. 删除R2目录"
echo "5. 重新配置rclone"
echo "0. 退出"
echo "=================================="
}
# 查看备份列表
list_backups() {
log_info "查看备份列表..."
echo
echo "=== 本地备份目录 ==="
if [[ -d "${BACKUP_TARGET_DIR}" ]]; then
ls -la "${BACKUP_TARGET_DIR}"
echo
echo "备份目录大小:"
du -sh "${BACKUP_TARGET_DIR}"
else
echo "本地备份目录不存在: ${BACKUP_TARGET_DIR}"
fi
echo
echo "=== R2存储桶内容 ==="
rclone lsd "${R2_REMOTE_NAME}:${R2_BUCKET_NAME}" --max-depth 2
}
# 测试连接
test_connection() {
log_info "测试Cloudflare R2连接..."
if rclone lsd "${R2_REMOTE_NAME}:" &> /dev/null; then
log_success "连接测试成功"
rclone about "${R2_REMOTE_NAME}:"
else
log_error "连接测试失败"
fi
}
# 删除R2存储桶中的指定顶层目录
delete_directory() {
log_info "删除R2存储桶中的顶层目录..."
echo
echo "=== R2存储桶顶层目录列表 ==="
# 获取顶层目录列表
local dirs=$(rclone lsd "${R2_REMOTE_NAME}:${R2_BUCKET_NAME}" --max-depth 1 2>/dev/null | awk '{print $5}' | grep -v '^$')
if [[ -z "$dirs" ]]; then
log_warning "存储桶中没有找到任何目录"
return 1
fi
# 显示目录列表
local dir_array=()
local index=1
while IFS= read -r dir; do
if [[ -n "$dir" ]]; then
echo "$index. $dir"
dir_array+=("$dir")
((index++))
fi
done <<< "$dirs"
echo "0. 取消操作"
echo
# 用户选择
read -p "请选择要删除的目录编号 [0-$((index-1))]: " choice
# 验证输入
if [[ ! "$choice" =~ ^[0-9]+$ ]] || [[ "$choice" -lt 0 ]] || [[ "$choice" -ge "$index" ]]; then
log_error "无效的选择"
return 1
fi
# 取消操作
if [[ "$choice" -eq 0 ]]; then
log_info "取消删除操作"
return 0
fi
# 获取选择的目录
local selected_dir="${dir_array[$((choice-1))]}"
# 二次确认
echo
log_warning "警告:即将删除目录 '${selected_dir}' 及其所有内容"
log_warning "此操作不可逆!"
read -p "确认删除? 请输入 'DELETE' 来确认: " confirm
if [[ "$confirm" != "DELETE" ]]; then
log_info "取消删除操作"
return 0
fi
# 执行删除
log_info "正在删除目录: ${selected_dir}"
if rclone purge "${R2_REMOTE_NAME}:${R2_BUCKET_NAME}/${selected_dir}" 2>/dev/null; then
log_success "目录 '${selected_dir}' 删除成功"
else
log_error "目录 '${selected_dir}' 删除失败"
return 1
fi
}
# 主函数
main() {
echo "Cloudflare R2备份脚本启动..."
# 处理命令行参数 - 直接执行模式
if [[ $# -gt 0 ]]; then
# 检查并安装rclone
if ! check_rclone; then
install_rclone
fi
# 配置rclone
configure_rclone
case $1 in
1)
full_backup
exit 0
;;
2)
list_backups
exit 0
;;
3)
test_connection
exit 0
;;
4)
delete_directory
exit 0
;;
5)
configure_rclone
exit 0
;;
*)
log_error "无效的参数: $1"
echo "使用方法: $0 [1|2|3|4|5]"
echo "1 - 全量备份"
echo "2 - 查看备份列表"
echo "3 - 测试连接"
echo "4 - 删除R2目录"
echo "5 - 重新配置rclone"
exit 1
;;
esac
fi
# 交互式菜单模式
while true; do
show_menu
read -p "请选择操作 [0-4]: " choice
case $choice in
1)
# 检查并安装rclone
if ! check_rclone; then
install_rclone
fi
# 配置rclone
configure_rclone
full_backup
;;
2)
# 检查并安装rclone
if ! check_rclone; then
install_rclone
fi
# 配置rclone
configure_rclone
list_backups
;;
3)
# 检查并安装rclone
if ! check_rclone; then
install_rclone
fi
# 配置rclone
configure_rclone
test_connection
;;
4)
# 检查并安装rclone
if ! check_rclone; then
install_rclone
fi
# 配置rclone
configure_rclone
delete_directory
;;
5)
# 检查并安装rclone
if ! check_rclone; then
install_rclone
fi
configure_rclone
;;
0)
log_info "退出程序"
exit 0
;;
*)
log_error "无效选择,请重新输入"
;;
esac
echo
read -p "按回车键继续..." -r
done
}
# 脚本入口
main "$@"
注意: 使用前请务必修改脚本中的 R2 配置参数,并确保访问密钥的安全性。