
MinIO 是一个高性能的分布式对象存储系统其站点复制Site Replication功能支持跨集群的数据和元数据同步。本文记录了如何使用 Docker Compose 部署 MinIO 站点复制集群配置两个 MinIO 实例并进行同步测试和故障恢复测试。测试环境基于 20T 磁盘适用于需要高可用性和灾难恢复的场景。MinIO 站点复制简介MinIO 的站点复制是一种集群级别的双向同步机制可同步数据桶、对象和元数据用户、策略、配置。其主要特性包括最终一致性对象数据近实时同步。严格一致性元数据保持完全一致。高可用性任一集群故障时另一个集群可继续提供服务。本文将部署两个 MinIO 实例SITE1 和 SITE2通过minio-mc容器配置站点复制并测试同步和故障恢复。环境与配置硬件与网络服务器两台服务器MinIO1:192.168.199.145MinIO2:192.168.199.147。磁盘每台服务器挂载 20T 磁盘。网络1Gbps 带宽RTT 20ms。操作系统Linux支持 Docker。Docker Compose 配置以下是 MinIO 和minio-mc服务的 Docker Compose 配置services: minio: image: ${MINIO_IMAGE} deploy: resources: limits: memory: 4096M networks: - middleware container_name: minio ports: - ${MINIO_PORT}:9000 - ${MINIO_CONSOLE_PORT}:9001 command: server /data --console-address :9001 environment: MINIO_ROOT_USER: ${MINIO_ROOT_USER} MINIO_ROOT_PASSWORD: ${MINIO_ROOT_PASSWORD} healthcheck: test: [CMD, curl, -f, http://localhost:9000/minio/health/live] interval: 30s timeout: 10s retries: 3 start_period: 10s volumes: - /etc/localtime:/etc/localtime:ro - ./data/minio/data:/data restart: always minio-mc: image: minio/mc:latest container_name: minio-mc networks: - middleware environment: SITE1_URL: ${SITE1_URL} SITE2_URL: ${SITE2_URL} MINIO_ROOT_USER: ${MINIO_ROOT_USER} MINIO_ROOT_PASSWORD: ${MINIO_ROOT_PASSWORD} volumes: - ./site-replication.sh:/site-replication.sh entrypoint: [/bin/bash, /site-replication.sh] healthcheck: test: [CMD-SHELL, mc --version /dev/null 21] interval: 30s timeout: 10s start_period: 10s retries: 3 restart: always networks: middleware: driver: bridge环境变量环境变量存储在.env文件中# MinIO MINIO_PORT9000 MINIO_CONSOLE_PORT9001 MINIO_ROOT_USERadmin MINIO_ROOT_PASSWORDAdmin123.com MINIO_IMAGEminio/minio:RELEASE.2025-04-22T22-12-26Z SITE1_URLhttp://192.168.145:9000 SITE2_URLhttp://192.168.147:9000站点复制脚本site-replication.sh用于配置站点复制#!/bin/bash # 严格模式错误、未定义变量、管道失败时退出 set -euo pipefail # 颜色定义 RED\033[0;31m GREEN\033[0;32m CYAN\033[1;36m YELLOW\033[1;33m BLUE\033[0;34m MAGENTA\033[0;35m RESET\033[0m # 更丰富的图标集 ICON_INFO ICON_SUCCESS✨ ICON_WARNING⚠️ ICON_ERROR ICON_DEBUG ICON_WAIT⏳ ICON_CLEAN ICON_CONFIG⚙️ ICON_NETWORK ICON_START ICON_READY✅ ICON_FINISH # 增强版日志函数优化颜色处理 log() { local level$1 message$2 local color icon timestamp timestamp$(date -u %H:%M:%S) case $level in INFO) color$GREEN icon$ICON_INFO ;; SUCCESS) color$GREEN icon$ICON_SUCCESS ;; WARN) color$YELLOW icon$ICON_WARNING ;; ERROR) color$RED icon$ICON_ERROR ;; DEBUG) color$BLUE icon$ICON_DEBUG ;; WAIT) color$MAGENTA icon$ICON_WAIT ;; *) color$CYAN icon$ICON_INFO ;; esac # 直接输出消息避免嵌套格式化问题 printf ${color}%s %-7s\t%s${RESET} $timestamp $icon [$level] $message } # 简洁标题样式改为 标题 形式 header() { if [ $2 MinIO 站点复制初始化 ]; then printf ${MAGENTA}███╗ ███╗██╗███╗ ██╗██╗ ██████╗ ███╗ ███╗ ██████╗ ████╗ ████║██║████╗ ██║██║██╔═══██╗ ████╗ ████║██╔════╝ ██╔████╔██║██║██╔██╗ ██║██║██║ ██║ ██╔████╔██║██║ ██║╚██╔╝██║██║██║╚██╗██║██║██║ ██║ ██║╚██╔╝██║██║ ██║ ╚═╝ ██║██║██║ ╚████║██║╚██████╔╝ ██║ ╚═╝ ██║╚██████╗ ╚═╝ ╚═╝╚═╝╚═╝ ╚═══╝╚═╝ ╚═════╝ ╚═╝ ╚═╝ ╚═════╝ %s %s${RESET} $1 $2 printf else printf ${CYAN}[ TASK %d ] ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ %s %s${RESET} $task_number $1 $2 printf task_number$((task_number 1)) fi } # 初始化任务计数器 task_number1 # 等待服务就绪合并为一个任务 wait_for_services() { local retries15 delay2 attempt1 local spinner(⣾ ⣽ ⣻ ⢿ ⡿ ⣟ ⣯ ⣷) local all_ready0 header $ICON_WAIT 等待 MinIO 服务就绪 for site in ${!SITES[]}; do local aliasHEALTHCHECK_$site local url${SITES[$site]} local attempt1 local all_ready0 log INFO 检查服务: $url while [ $attempt -le $retries ]; do if /usr/bin/mc alias set $alias $url $MINIO_ACCESS_KEY $MINIO_SECRET_KEY /dev/null 21 \ /usr/bin/mc ls $alias /dev/null 21; then log SUCCESS 服务已就绪: $url all_ready1 if [ $site SITE1 ]; then printf ${CYAN}%s${RESET} $(for i in $(seq 1 61); do printf -; done) fi break fi printf \r%-80s printf \r${YELLOW}%s 尝试 %2d/%-2d 检查服务 %s 中...${RESET} \ ${spinner[$((attempt % ${#spinner[]}))]} \ $attempt $retries $url sleep $delay ((attempt)) delay$((delay * 2 8 ? 8 : delay * 2)) done if [ $all_ready -eq 0 ]; then log ERROR 服务 $url 在 $retries 次尝试后仍未就绪 exit 1 fi done echo } # 配置别名优化颜色格式 configure_aliases() { header $ICON_CONFIG 配置 MinIO 站点别名 for site in ${!SITES[]}; do local url${SITES[$site]} /usr/bin/mc alias remove $site 2/dev/null || true if /usr/bin/mc alias set $site $url $MINIO_ACCESS_KEY $MINIO_SECRET_KEY /dev/null 21; then log SUCCESS 别名配置成功: ${site} → ${url} else log ERROR 无法配置别名: $site → $url return 1 fi done echo } # 检查复制配置状态 check_replication_status() { /usr/bin/mc admin replicate info SITE1 21 | grep -q SiteReplication enabled } # 清除原复制配置 cleanup_old_replication() { header $ICON_CLEAN 清理原有复制配置 for site in ${!SITES[]}; do log INFO 正在清理 $site 的复制配置... if /usr/bin/mc admin replicate rm --all $site --force 2/dev/null; then log SUCCESS 成功清理 $site 的配置 else log WARN $site 无配置可清理或清理失败 fi done sleep 2 if check_replication_status; then log ERROR 复制配置仍存在请手动检查 return 1 fi log SUCCESS 所有站点复制配置已清理完成 echo } # 可靠的bucket删除函数 delete_all_buckets() { local site$1 log WARN 即将清空 ${site} 的所有bucket... local buckets buckets$(/usr/bin/mc ls $site --json | jq -r .key | tr -d / | grep -v ^$) if [ -z $buckets ]; then log INFO 没有 bucket 需要删除 return 0 fi echo $buckets | while read -r bucket; do log INFO 正在删除 ${site}/${bucket}... if /usr/bin/mc rb --force ${site}/${bucket} /dev/null 21; then log SUCCESS 删除成功: ${site}/${bucket} else log ERROR 删除失败: ${site}/${bucket} return 1 fi done if /usr/bin/mc ls $site | grep -q .; then log ERROR ${site} 中仍有bucket存在 return 1 fi log SUCCESS ${site} 已完全清空 } # 设置复制配置 setup_replication() { header $ICON_NETWORK 设置站点复制 local retries5 delay2 attempt1 local site_list(${!SITES[]}) while [ $attempt -le $retries ]; do log WAIT 尝试配置复制 (${attempt}/${retries})... output$(/usr/bin/mc admin replicate add ${site_list[]} 21) if [ $? -eq 0 ]; then log SUCCESS 站点复制配置成功! echo return 0 fi if [[ $output *only one cluster may have data* ]]; then log WARN 检测到 SITE2 中已有数据违反复制要求 if ! delete_all_buckets SITE2; then log ERROR 清空 SITE2 失败无法继续 return 1 fi log INFO 等待 ${delay} 秒让系统完成清理... sleep $delay else log ERROR 复制配置失败: $(echo $output | head -n1) log DEBUG 完整错误: $output fi ((attempt)) delay$((delay * 2 8 ? 8 : delay * 2)) done log ERROR 经过 ${retries} 次尝试后仍无法配置复制 return 1 } # 优化状态验证表格 verify_status() { header $ICON_NETWORK 复制状态验证 for site in ${!SITES[]}; do printf ${GREEN}%s [SUCCESS] 查询成功: %s → %s${RESET} $ICON_SUCCESS $site ${SITES[$site]} printf ${CYAN}%s${RESET} $(for i in $(seq 1 160); do printf -; done) status_output$(/usr/bin/mc admin replicate info $site 21) if echo $status_output | grep -q SiteReplication enabled; then printf ${GREEN}%s${RESET} $status_output else printf ${RED}%s${RESET} $status_output fi echo done } # 主流程控制 main() { : ${SITE1_URL:?需要 SITE1_URL 环境变量} : ${SITE2_URL:?需要 SITE2_URL 环境变量} : ${MINIO_ACCESS_KEY:?需要 MINIO_ACCESS_KEY 环境变量} : ${MINIO_SECRET_KEY:?需要 MINIO_SECRET_KEY 环境变量} declare -A SITES([SITE1]$SITE1_URL [SITE2]$SITE2_URL) header $ICON_START MinIO 站点复制初始化 wait_for_services configure_aliases || exit 1 cleanup_old_replication || exit 1 setup_replication || exit 1 verify_status header $ICON_FINISH MinIO 站点复制已就绪 log SUCCESS 所有配置已完成服务运行中... log INFO 按 CtrlC 停止服务 exec tail -f /dev/null } trap log INFO 收到终止信号正在关闭...; exit 0 SIGTERM SIGINT main部署步骤准备环境创建目录mkdir -p /data/opt/installmiddleware/data/minio/data设置磁盘权限确保 20T 磁盘挂载到/data并赋予权限chown -R 1000:1000 ./data/minio/data保存配置保存docker-compose.yml和.env文件。保存site-replication.sh设置可执行权限chmod x site-replication.sh启动服务在 MinIO1 和 MinIO2 服务器上分别部署 MinIO 实例cd /data/opt/installmiddleware docker-compose up -d配置站点复制minio-mc容器自动运行site-replication.sh配置站点复制。检查日志docker logs --tail 100 minio-mc预期输出Configuring aliases for SITE1 and SITE2... Adding site replication between SITE1 and SITE2... Site replication configured successfully!同步测试测试目标验证 MinIO1 创建的桶和上传的对象是否自动同步到 MinIO2。测试步骤在 MinIO1 创建桶docker exec -it minio-mc bash mc mb SITE1/test-bucket上传测试文件创建文件echo Hello, MinIO! test.txt上传到 MinIO1mc cp test.txt SITE1/test-bucket验证 MinIO2 同步检查桶mc ls SITE2预期输出[2025-05-13 16:40:00 CST] 0B test-bucket/检查文件mc ls SITE2/test-bucket预期输出[2025-05-13 16:40:10 CST] 14B test.txt访问 MinIO2 控制台打开http://192.168.199.147:9000登录admin/Admin123.com。确认test-bucket和test.txt存在。测试结果桶和文件成功同步说明站点复制正常同步延迟通常在毫秒级RTT 20ms。故障恢复测试测试目标模拟 MinIO1 故障验证 MinIO2 的服务可用性并测试 MinIO1 恢复后的同步。测试步骤停止 MinIO1docker-compose stop minio检查站点复制状态在minio-mc容器中mc admin replicate status SITE2预期输出基于实际测试Bucket replication status: ● 0/1 Buckets in sync Bucket | SITE1 | SITE2 test | | ✗ in-sync ... Object replication status: Link: ● offline 5 seconds (total downtime: 4 minutes 30 seconds) Errors: 0 in last 1 minute; 1 in last 1hr验证 MinIO2 服务上传新文件echo MinIO2 test test2.txt mc cp test2.txt SITE2/test-bucket检查mc ls SITE2/test-bucket预期输出[2025-05-13 16:45:00 CST] 14B test.txt [2025-05-13 16:45:10 CST] 12B test2.txt恢复 MinIO1启动docker-compose start minio验证健康curl -f http://192.168.199.145:9000/minio/health/live重新同步触发增量同步mc admin replicate resync start SITE2 SITE1检查状态mc admin replicate status SITE2预期输出Site Replication Status: - Site: SITE1 (http://192.168.199.145:9000) - Online - Site: SITE2 (http://192.168.199.147:9000) - Online Replication Status: Active验证 MinIO1 同步检查mc ls SITE1/test-bucket预期输出[2025-05-13 16:45:00 CST] 14B test.txt [2025-05-13 16:45:10 CST] 12B test2.txt测试结果MinIO1 故障期间MinIO2 正常提供服务。MinIO1 恢复后增量数据test2.txt同步成功站点复制恢复正常。注意事项与优化建议磁盘与虚拟化20T 磁盘虚拟化后 18T可能受虚拟化影响建议直通磁盘volumes: - /dev/sdb:/data定期检查磁盘健康smartctl -a /dev/sdb网络确保 RTT 20ms带宽 ≥ 1Gbps。检查防火墙ufw allow from 192.168.199.0/24 to any port 9000安全性替换默认凭据admin/Admin123.com。启用 TLSenvironment: - MINIO_SERVER_URLhttps://192.168.199.145:9000监控启用 Prometheusenvironment: - MINIO_PROMETHEUS_AUTH_TYPEpublic使用 Grafana 监控节点状态minio_node_up和复制延迟minio_replication_last_hour_latency_millis。预算站点复制需要两台服务器约 14000-25000 元。若预算有限可考虑单向主从复制约 7000-13000 元。总结通过 Docker Compose 和site-replication.sh我们部署了 MinIO 站点复制集群实现了 MinIO1 和 MinIO2 的数据和元数据同步。同步测试验证了桶和对象的自动同步故障恢复测试确认了 MinIO2 的高可用性和 MinIO1 恢复后的增量同步。该方案结合 20T 磁盘的存储容量适用于高可用性场景。如需进一步优化如 webhook 通知、Flask API 集成请参考 MinIO 文档Site Replication。