小易说IT 小易说IT

MinIO 跨集群桶bucket镜像同步完整版脚本

功能:源 MinIO 集群桶同步到另一套 MinIO 集群桶;保留脚本锁防并发、钉钉告警、dry-run 试运行;配套 logrotate。 注意:客户端mc mirror对象级别客户端镜像,和 MinIO 服务端桶复制 (mc replicate add) 不是一回事。 桶复制:服务端主动推送,不依赖客户端在线; mc mirror:客户端拉取源、上传目标,必须保持客户端在线,适合小批量、跨云 / 跨机房异构集群。

文件名:sync_minio_bucket_cross_cluster.sh

#!/bin/bash
set -euo pipefail

##############################################################################
# 【生产配置区 - 修改此处】
##############################################################################
# ========== 源MinIO集群 ==========
SRC_ALIAS="src-minio"
SRC_ENDPOINT="http://10.0.0.10:9000"
SRC_AK="source_access_key"
SRC_SK="source_secret_key"
SRC_BUCKET_PATH="${SRC_ALIAS}/source-bucket"

# ========== 目标MinIO集群 ==========
DST_ALIAS="dst-minio"
DST_ENDPOINT="http="http://10.0.0.20:9000"
DST_AK="dest_access_key"
DST_SK="dest_secret_key"
DST_BUCKET_PATH="${DST_ALIAS}/dest-bucket"

# 同步参数
ENABLE_REMOVE="false"          # true=镜像模式,删除目标桶中源不存在对象,高危!
DRY_RUN="true"                 # 上线前保持true试运行,验证无误改为false
RETRY_COUNT=3

# 钉钉机器人Webhook
DING_WEBHOOK="https://oapi.dingtalk.com/robot/send?access_token=xxxx"
DING_NOTIFY_SUCCESS="false"    # 是否同步成功发送钉钉通知

# 日志 & 锁文件
LOG_FILE="/var/log/minio_cross_bucket_sync.log"
LOCK_FILE="/var/run/minio_cross_sync.lock"
##############################################################################

# ===================== 工具函数 =====================
log() {
    echo "[$(date '+%Y-%m-%d %H:%M:%S')] $1" | tee -a "${LOG_FILE}"
}

# 钉钉消息推送
send_ding() {
    local MSG_CONTENT="$1"
    curl -s -X POST "${DING_WEBHOOK}" \
    -H "Content-Type: application/json" \
    -d '{
        "msgtype": "text",
        "text": {
            "content": "【MinIO跨集群桶同步任务】'"${MSG_CONTENT}"'"
        }
    }' > /dev/null
}

# 清理锁文件,trap捕获退出信号
cleanup() {
    if [ -f "${LOCK_FILE}" ]; then
        rm -f "${LOCK_FILE}"
        log "✅ 释放锁文件 ${LOCK_FILE}"
    fi
}
trap cleanup EXIT INT TERM

# ===================== 脚本并发锁控制 =====================
if [ -f "${LOCK_FILE}" ]; then
    PID=$(cat "${LOCK_FILE}")
    if ps -p "${PID}" > /dev/null; then
        MSG="⚠️ 同步任务正在运行(PID:${PID}),本次任务退出,禁止并发!"
        log "${MSG}"
        send_ding "${MSG}"
        exit 2
    else
        log "⚠️ 检测到残留锁文件,旧进程已死亡,自动清理锁继续执行"
        rm -f "${LOCK_FILE}"
    fi
fi
echo $$ > "${LOCK_FILE}"

# ===================== 前置校验 =====================
log "========== MinIO跨集群桶同步任务开始 =========="

# 检查mc客户端
if ! command -v mc &> /dev/null; then
    MSG="❌ ERROR: mc客户端不存在,请先安装minio mc"
    log "${MSG}"
    send_ding "${MSG}"
    exit 1
fi

# 设置两个集群别名,关闭历史记录防止密钥写入bash history
set +o history
mc alias set "${SRC_ALIAS}" "${SRC_ENDPOINT}" "${SRC_AK}" "${SRC_SK}"
mc alias set "${DST_ALIAS}" "${DST_ENDPOINT}" "${DST_AK}" "${DST_SK}"
set -o history

# 连通性校验
if ! mc ls "${SRC_ALIAS}" &>/dev/null; then
    MSG="❌ ERROR: 源MinIO集群连接失败,请检查源集群地址/AK/SK"
    log "${MSG}"
    send_ding "${MSG}"
    exit 1
fi
if ! mc ls "${DST_ALIAS}" &>/dev/null; then
    MSG="❌ ERROR: 目标MinIO集群连接失败,请检查目标集群地址/AK/SK"
    log "${MSG}"
    send_ding "${MSG}"
    exit 1
fi

# 校验桶是否存在
if ! mc ls "${SRC_BUCKET_PATH}" &>/dev/null; then
    MSG="❌ ERROR: 源桶 ${SRC_BUCKET_PATH} 不存在"
    log "${MSG}"
    send_ding "${MSG}"
    exit 1
fi
if ! mc ls "${DST_BUCKET_PATH}" &>/dev/null; then
    MSG="❌ ERROR: 目标桶 ${DST_BUCKET_PATH} 不存在,请先创建桶"
    log "${MSG}"
    send_ding "${MSG}"
    exit 1
fi

# ===================== 组装同步参数 =====================
MIRROR_ARGS=("--overwrite" "--retry" "${RETRY_COUNT}")

if [ "${DRY_RUN}" = "true" ];then
    MIRROR_ARGS+=("--dry-run")
fi

if [ "${ENABLE_REMOVE}" = "true" ];then
    MIRROR_ARGS+=("--remove")
fi

log "执行命令:mc mirror ${MIRROR_ARGS[*]} ${SRC_BUCKET_PATH} ${DST_BUCKET_PATH}"

# ===================== 执行跨集群镜像同步 =====================
RET=0
mc mirror "${MIRROR_ARGS[@]}" "${SRC_BUCKET_PATH}" "${DST_BUCKET_PATH}" || RET=$?

if [ ${RET} -eq 0 ];then
    MSG="✅ 跨集群桶同步执行成功!DRY_RUN=${DRY_RUN}, ENABLE_REMOVE=${ENABLE_REMOVE}"
    log "${MSG}"
    if [ "${DING_NOTIFY_SUCCESS}" = "true" ];then
        send_ding "${MSG}"
    fi
else
    MSG="❌ 跨集群桶同步失败,返回码:${RET}"
    log "${MSG}"
    send_ding "${MSG}"
    exit ${RET}
fi

log "========== MinIO跨集群桶同步任务结束 =========="
exit 0

部署步骤

# 创建脚本文件
vim /opt/scripts/sync_minio_bucket_cross_cluster.sh

# 权限收紧
chmod 700 /opt/scripts/sync_minio_bucket_cross_cluster.sh
chown root:root /opt/scripts/sync_minio_bucket_cross_cluster.sh

# 首次试运行
/opt/scripts/sync_minio_bucket_cross_cluster.sh

logrotate 配置(新建 /etc/logrotate.d/minio_cross_sync

/var/log/minio_cross_bucket_sync.log {
    daily
    rotate 30
    missingok
    notifempty
    compress
    delaycompress
    create 0644 root root
    dateext
}

测试 logrotate:

logrotate -d /etc/logrotate.d/minio_cross_sync

Crontab 定时任务示例

# 每天凌晨 01:30 执行跨集群桶同步
30 1 * * * /bin/bash /opt/scripts/sync_minio_bucket_cross_cluster.sh >> /dev/null 2>&1

关键注意事项(生产重点)

  1. --remove 高危!ENABLE_REMOVE=true 时,目标桶存在但源桶不存在的对象会被删除。务必先DRY_RUN=true跑多次,确认删除列表无误再开启。

  2. mc mirror 客户端镜像的局限性

    • 不会同步对象版本、删除标记(version/delete marker);如果源开启版本控制,mc mirror 只会同步最新版本对象。

    • ❗依赖客户端服务器网络连通两个 MinIO 集群,客户端宕机同步停止。 ✅ 如果需要完整版本复制、服务端自动同步,优先使用 MinIO 原生桶复制(Bucket Replication),不需要脚本。

  3. 安全优化:不把 AK/SK 硬编码写进脚本 使用环境变量MC_HOST_别名替代mc alias set,把密钥放到 crontab 或者独立环境变量文件:

    export MC_HOST_src-minio=http://source_access_key:source_secret_key@10.0.0.10:9000
    export MC_HOST_dst-minio=http://dest_access_key:dest_secret_key@10.0.0.20:9000
    

    脚本内就可以删掉两行mc alias set

  4. 网络 跨机房 / 跨云同步,注意带宽,避免高峰期打满带宽;可以加--bandwidth限制速率,单位MiB

    MIRROR_ARGS=("--overwrite" "--retry" "${RETRY_COUNT}" "--bandwidth" "10MiB")
    

可选增强扩展

  1. 同步前后统计源桶、目标桶对象数量和总大小,写入钉钉告警消息

  2. 增加超时控制,防止 mc mirror 卡死

  3. 钉钉机器人加签模式(当前脚本是简单 token,安全弱一点)


本文原创作者:易君召,详见:https://www.yijunzhao.cc/about,转载请注明出处。

原文链接 https://www.yijunzhao.cc/archives/minio-cross-cluster-bucket-mirror-sync-complete-script

欢迎访问 https://www.yijunzhao.cc/

https://www.yijunzhao.cc/