Categories
Uncategorized

Docker MySQL 8.0 主从复制集群完整搭建指南

概述

本文档详细介绍了如何使用 Docker Compose 搭建 MySQL 8.0 的主从复制集群,包含 1 个主节点(Master)和 2 个从节点(Slave),实现数据自动复制、读写分离和高可用。

系统要求

  • Docker Engine 19.03+
  • Docker Compose v2.0+
  • 至少 4GB 可用内存
  • 有足够的磁盘空间存储数据

文件结构

准备以下文件:

  • mysql.yml – Docker Compose 配置文件
  • setup-users.sql – 初始化用户脚本
  • init-replication.sh – 初始化复制脚本

步骤 1:创建 Docker Compose 配置文件

创建 mysql.yml 文件:

# MySQL 主从复制集群 Docker Compose 配置
# 📅 创建日期:2026-04-01
# 📌 端口:3306 (主), 3307 (从 1), 3308 (从 2)
# 🎯 架构:1 主节点 + 2 从节点
# 🔗 特性:数据自动复制、读写分离、高可用

version: '3.8'

services:
  # ==================== 主节点 (Master) ====================
  mysql-master:
    image: mysql:8.0
    container_name: mysql-master
    restart: always
    environment:
      MYSQL_ROOT_PASSWORD: root123456
      MYSQL_DATABASE: default_db
      MYSQL_USER: dbuser
      MYSQL_PASSWORD: dbpass123
      TZ: Asia/Shanghai
    ports:
      - "0.0.0.0:3306:3306"
    volumes:
      - ~/docker-data/mysql/master/data:/var/lib/mysql:z
      - ~/docker-data/mysql/master/logs:/var/log/mysql:z
      - ~/docker-data/mysql/master/conf:/etc/mysql/conf.d:z
      - ./setup-users.sql:/docker-entrypoint-initdb.d/setup-users.sql:ro
    command:
      - --server-id=1
      - --log-bin=mysql-bin
      - --binlog-format=ROW
      - --gtid-mode=ON
      - --enforce-gtid-consistency=ON
      - --character-set-server=utf8mb4
      - --collation-server=utf8mb4_unicode_ci
    networks:
      - mysql-network
    healthcheck:
      test: ["CMD", "mysqladmin", "ping", "-h", "localhost", "-u", "root", "-proot123456"]
      interval: 30s
      timeout: 10s
      retries: 3

  # ==================== 从节点 1 (Slave 1) ====================
  mysql-slave-1:
    image: mysql:8.0
    container_name: mysql-slave-1
    restart: always
    environment:
      MYSQL_ROOT_PASSWORD: root123456
      MYSQL_DATABASE: default_db
      MYSQL_USER: dbuser
      MYSQL_PASSWORD: dbpass123
      TZ: Asia/Shanghai
    ports:
      - "0.0.0.0:3307:3306"
    volumes:
      - ~/docker-data/mysql/slave-1/data:/var/lib/mysql:z
      - ~/docker-data/mysql/slave-1/logs:/var/log/mysql:z
      - ~/docker-data/mysql/slave-1/conf:/etc/mysql/conf.d:z
      - ./setup-users.sql:/docker-entrypoint-initdb.d/setup-users.sql:ro
    command:
      - --server-id=2
      - --relay-log=mysql-relay-bin
      - --read-only=ON
      - --gtid-mode=ON
      - --enforce-gtid-consistency=ON
      - --character-set-server=utf8mb4
      - --collation-server=utf8mb4_unicode_ci
    networks:
      - mysql-network
    depends_on:
      - mysql-master
    healthcheck:
      test: ["CMD", "mysqladmin", "ping", "-h", "localhost", "-u", "root", "-proot123456"]
      interval: 30s
      timeout: 10s
      retries: 3

  # ==================== 从节点 2 (Slave 2) ====================
  mysql-slave-2:
    image: mysql:8.0
    container_name: mysql-slave-2
    restart: always
    environment:
      MYSQL_ROOT_PASSWORD: root123456
      MYSQL_DATABASE: default_db
      MYSQL_USER: dbuser
      MYSQL_PASSWORD: dbpass123
      TZ: Asia/Shanghai
    ports:
      - "0.0.0.0:3308:3306"
    volumes:
      - ~/docker-data/mysql/slave-2/data:/var/lib/mysql:z
      - ~/docker-data/mysql/slave-2/logs:/var/log/mysql:z
      - ~/docker-data/mysql/slave-2/conf:/etc/mysql/conf.d:z
      - ./setup-users.sql:/docker-entrypoint-initdb.d/setup-users.sql:ro
    command:
      - --server-id=3
      - --relay-log=mysql-relay-bin
      - --read-only=ON
      - --gtid-mode=ON
      - --enforce-gtid-consistency=ON
      - --character-set-server=utf8mb4
      - --collation-server=utf8mb4_unicode_ci
    networks:
      - mysql-network
    depends_on:
      - mysql-master
    healthcheck:
      test: ["CMD", "mysqladmin", "ping", "-h", "localhost", "-u", "root", "-proot123456"]
      interval: 30s
      timeout: 10s
      retries: 3

networks:
  mysql-network:
    driver: bridge

步骤 2:创建用户初始化脚本

创建 setup-users.sql 文件:

-- 初始化脚本,确保root用户可以从任何主机连接
-- 并创建普通用户

-- 为root用户创建从任意主机访问的账户
CREATE USER IF NOT EXISTS 'root'@'%' IDENTIFIED BY 'root123456';
GRANT ALL PRIVILEGES ON *.* TO 'root'@'%' WITH GRANT OPTION;
FLUSH PRIVILEGES;

-- 确保dbuser在所有节点上都存在
CREATE USER IF NOT EXISTS 'dbuser'@'%' IDENTIFIED BY 'dbpass123';
GRANT SELECT, INSERT, UPDATE, DELETE ON *.* TO 'dbuser'@'%';
FLUSH PRIVILEGES;

-- 也保留localhost访问权限
ALTER USER 'root'@'localhost' IDENTIFIED BY 'root123456';
FLUSH PRIVILEGES;

步骤 3:创建复制初始化脚本

创建 init-replication.sh 文件:

#!/bin/bash
# MySQL 主从配置初始化脚本
# 📅 创建日期:2026-04-01
# 用途:配置主从复制关系

set -e

echo "🔧 开始配置 MySQL 主从复制..."
echo ""

# 等待主节点就绪
echo "⏳ 等待主节点就绪..."
for i in {1..30}; do
    if docker exec mysql-master mysql -u root -proot123456 -e "SELECT 1" > /dev/null 2>&1; then
        echo "✅ 主节点已就绪"
        break
    fi
    sleep 2
done

# 等待从节点就绪
echo "⏳ 等待从节点就绪..."
for i in {1..30}; do
    if docker exec mysql-slave-1 mysql -u root -proot123456 -e "SELECT 1" > /dev/null 2>&1; then
        echo "✅ 从节点1已就绪"
        break
    fi
    sleep 2
done

# 等待从节点2就绪
echo "⏳ 等待从节点2就绪..."
for i in {1..30}; do
    if docker exec mysql-slave-2 mysql -u root -proot123456 -e "SELECT 1" > /dev/null 2>&1; then
        echo "✅ 从节点2已就绪"
        break
    fi
    sleep 2
done


# 创建复制用户
echo "🔐 创建复制用户..."
docker exec mysql-master mysql -u root -proot123456 <<EOF
CREATE USER IF NOT EXISTS 'repl'@'%' IDENTIFIED BY 'repl123456';
GRANT REPLICATION SLAVE ON *.* TO 'repl'@'%';
FLUSH PRIVILEGES;
EOF

# 获取主节点位置
echo "📊 获取主节点位置..."
MASTER_STATUS=$(docker exec mysql-master mysql -u root -proot123456 -e "SHOW MASTER STATUS\G")
MASTER_FILE=$(echo "$MASTER_STATUS" | grep "File:" | awk '{print $2}')
MASTER_POS=$(echo "$MASTER_STATUS" | grep "Position:" | awk '{print $2}')

if [ -z "$MASTER_FILE" ] || [ -z "$MASTER_POS" ]; then
    echo "❌ 无法获取主节点状态"
    exit 1
fi

echo "✅ 主节点位置:$MASTER_FILE:$MASTER_POS"

# 配置从节点 1
echo "🔗 配置从节点 1 (mysql-slave-1)..."
docker exec mysql-slave-1 mysql -u root -proot123456 <<EOF
STOP SLAVE;
RESET SLAVE;
CHANGE MASTER TO
  MASTER_HOST='mysql-master',
  MASTER_USER='repl',
  MASTER_PASSWORD='repl123456',
  MASTER_LOG_FILE='$MASTER_FILE',
  MASTER_LOG_POS=$MASTER_POS,
  GET_MASTER_PUBLIC_KEY=1;
START SLAVE;
EOF

# 配置从节点 2
echo "🔗 配置从节点 2 (mysql-slave-2)..."
docker exec mysql-slave-2 mysql -u root -proot123456 <<EOF
STOP SLAVE;
RESET SLAVE;
CHANGE MASTER TO
  MASTER_HOST='mysql-master',
  MASTER_USER='repl',
  MASTER_PASSWORD='repl123456',
  MASTER_LOG_FILE='$MASTER_FILE',
  MASTER_LOG_POS=$MASTER_POS,
  GET_MASTER_PUBLIC_KEY=1;
START SLAVE;
EOF

# 等待同步
echo "⏳ 等待复制同步..."
sleep 5

# 验证复制状态
echo ""
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
echo "📊 验证主从状态"
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"

echo ""
echo "📌 主节点状态:"
docker exec mysql-master mysql -u root -proot123456 -e "SHOW MASTER STATUS\G"

echo ""
echo "📌 从节点 1 状态:"
docker exec mysql-slave-1 mysql -u root -proot123456 -e "SHOW SLAVE STATUS\G" | grep -E "Slave_IO_Running|Slave_SQL_Running|Seconds_Behind_Master|Master_Host"

echo ""
echo "📌 从节点 2 状态:"
docker exec mysql-slave-2 mysql -u root -proot123456 -e "SHOW SLAVE STATUS\G" | grep -E "Slave_IO_Running|Slave_SQL_Running|Seconds_Behind_Master|Master_Host"

echo ""
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
echo "✅ 主从配置完成!"
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"

步骤 4:设置文件权限

chmod +x init-replication.sh

步骤 5:启动集群

# 创建数据目录
mkdir -p ~/docker-data/mysql/{master,slave-1,slave-2}/{data,logs,conf}
chmod -R 777 ~/docker-data/mysql

# 启动集群
docker compose -f mysql.yml up -d

步骤 6:初始化主从复制

./init-replication.sh

连接信息

账户信息

  • Root 用户密码root123456
  • 普通用户dbuser / dbpass123

端口映射

  • 主节点 (读写): 3306
  • 从节点1 (只读): 3307
  • 从节点2 (只读): 3308

连接示例

# 主节点(写操作)
mysql -h 127.0.0.1 -P 3306 -u root -proot123456

# 从节点(读操作)
mysql -h 127.0.0.1 -P 3307 -u root -proot123456
mysql -h 127.0.0.1 -P 3308 -u root -proot123456

# 普通用户连接
mysql -h 127.0.0.1 -P 3306 -u dbuser -pdbpass123

验证复制

检查主节点状态

docker exec mysql-master mysql -u root -proot123456 -e "SHOW MASTER STATUS;"

检查从节点状态

docker exec mysql-slave-1 mysql -u root -proot123456 -e "SHOW SLAVE STATUS\G;"

测试数据同步

在主节点创建表并插入数据,然后在从节点验证数据是否同步。

常见问题

1. Public Key Retrieval 错误

如果应用程序连接时出现 “Public Key Retrieval is not allowed” 错误,请在连接字符串中添加 allowPublicKeyRetrieval=true 参数。

2. 从节点无法连接

检查复制用户权限和网络连接,确保主节点的复制用户允许从从节点IP连接。

3. 复制延迟

使用 SHOW SLAVE STATUS\G; 检查 Seconds_Behind_Master 字段确认延迟。

管理命令

停止集群

docker compose -f mysql.yml down

查看日志

docker logs mysql-master
docker logs mysql-slave-1
docker logs mysql-slave-2

备份数据

docker exec mysql-master mysqldump -u root -proot123456 --all-databases > backup.sql

注意事项

  1. 安全性: 生产环境请使用更强的密码
  2. 备份: 定期备份主节点数据
  3. 监控: 监控复制延迟和错误
  4. 容量: 确保从节点有足够存储空间
  5. 网络: 保证主从节点间网络稳定

这个配置提供了高可用的 MySQL 主从复制集群,适合读多写少的应用场景,如 WordPress 博客等。