2025年12月27日 星期六

Docker 實戰:MySQL 8 全自動 GTID 主從複製架構指南

Docker 實戰:MySQL 8 全自動 GTID 主從複製架構指南

在傳統部署中,設定 MySQL 複製最煩瑣的就是「手動記錄位點(Log Position)」以及「手動執行 CHANGE MASTER TO」。透過 DockerGTID(全域事務 ID),我們可以實現**「一行指令,環境即建好」**的目標。

1. 專案目錄結構

我們需要明確的分層結構,確保設定檔與初始化腳本能被正確掛載。

Plaintext
mysql-replication/
├── source/
│   ├── my.cnf          # Source 設定檔
│   └── init.sql        # 自動建立複製帳號
├── replica/
│   ├── my.cnf          # Replica 設定檔
│   └── setup.sh        # 自動化同步腳本 (關鍵!)
└── docker-compose.yml  # 編排中心

2. 設定檔詳解:確保 GTID 啟用

Source 端 (source/my.cnf)

Ini, TOML
[mysqld]
server-id=1
log_bin=mysql-bin
binlog_format=ROW
gtid_mode=ON
enforce_gtid_consistency=ON
# 解決 MySQL 8 認證問題
default-authentication-plugin=mysql_native_password
# 權限修正:確保 MySQL 讀取此檔不報錯
skip-host-cache
skip-name-resolve

Replica 端 (replica/my.cnf)

Ini, TOML
[mysqld]
server-id=2
relay-log=relay-bin
read_only=1
gtid_mode=ON
enforce_gtid_consistency=ON
default-authentication-plugin=mysql_native_password

3. 自動化腳本:解決「權限」與「手動操作」

A. 建立複製帳號 (source/init.sql)

此檔案放在 Source 端,容器啟動時會自動執行。

SQL
-- 建立測試資料庫
CREATE DATABASE IF NOT EXISTS app_db;

-- 建立複製專用帳號,使用 native 插件避免連線失敗
CREATE USER 'repl'@'%' IDENTIFIED WITH mysql_native_password BY 'replpass';
GRANT REPLICATION SLAVE, REPLICATION CLIENT ON *.* TO 'repl'@'%';
FLUSH PRIVILEGES;

B. 自動化握手腳本 (replica/setup.sh)

這是最關鍵的一步。我們利用 Replica 啟動時自動執行腳本,去向 Source 請求同步。

Bash
#!/bin/bash
echo "等待 Source MySQL 啟動並就緒..."
# 確保 Source 可以連通才開始執行
until mysql -h mysql-source -urepl -preplpass -e "SELECT 1;" &> /dev/null; do
  sleep 2
done

echo "開始配置 GTID 主從同步..."
mysql -uroot -prootpassword <<EOSQL
STOP REPLICA;
CHANGE REPLICATION SOURCE TO
  SOURCE_HOST='mysql-source',
  SOURCE_USER='repl',
  SOURCE_PASSWORD='replpass',
  SOURCE_AUTO_POSITION=1;
START REPLICA;
EOSQL

echo "主從同步配置完成!"

4. 編排中心:docker-compose.yml

我們加入 Healthcheck 確保啟動順序正確,並解決掛載權限問題。

YAML
services:
  mysql-source:
    image: mysql:8.0
    container_name: mysql-source
    restart: always
    environment:
      MYSQL_ROOT_PASSWORD: rootpassword
    volumes:
      - ./source/my.cnf:/etc/mysql/conf.d/my.cnf
      - ./source/init.sql:/docker-entrypoint-initdb.d/init.sql
      - source_data:/var/lib/mysql
    healthcheck:
      test: ["CMD", "mysqladmin" ,"ping", "-h", "localhost", "-uroot", "-prootpassword"]
      interval: 5s
      timeout: 5s
      retries: 10

  mysql-replica:
    image: mysql:8.0
    container_name: mysql-replica
    restart: always
    depends_on:
      mysql-source:
        condition: service_healthy
    environment:
      MYSQL_ROOT_PASSWORD: rootpassword
    volumes:
      - ./replica/my.cnf:/etc/mysql/conf.d/my.cnf
      - ./replica/setup.sh:/docker-entrypoint-initdb.d/setup.sh
      - replica_data:/var/lib/mysql

volumes:
  source_data:
  replica_data:

5. 部署與權限修正

在執行啟動命令前,請務必在宿主機處理設定檔權限(這是 MySQL 容器最常見的 Permission Denied 原因):

Bash
# 修改設定檔權限,MySQL 要求不能是 777
chmod 644 ./source/my.cnf ./replica/my.cnf
# 確保 setup.sh 具備執行權限
chmod +x ./replica/setup.sh

# 一鍵啟動
docker compose up -d

6. 驗證與排錯

如何確認同步成功?

不需要進容器,直接執行:

Bash
docker exec -it mysql-replica mysql -uroot -prootpassword -e "SHOW REPLICA STATUS\G"

看這兩個 Yes 就對了:

常見權限與錯誤排查

  1. 認證錯誤 (Access Denied): 檢查 source/init.sql 是否確實建立了 repl@'%'

  2. 設定檔被忽略: 如果日誌顯示 World-writable config file is ignored,表示你的 my.cnf 權限太開(需為 644)。

  3. 連線被拒: 檢查 docker-compose 中的服務名稱是否為 mysql-source,Replica 是透過這個名稱找 Source 的。


總結

這套方案解決了你擔心的「手動操作」與「權限問題」:

  • 檔案權限: 透過宿主機 chmod 644Named Volumes 解決。

  • 手動設定: 透過 replica/setup.sh 自動完成 CHANGE REPLICATION SOURCE

  • 一致性: 採用 GTID,即使容器重啟或位點變動,系統也能自動找回同步點。

沒有留言:

張貼留言

[技術深度] 打造 Laravel 12 高併發即時客服系統:從 Octane Swoole 到 Reverb 擴展架構

[技術深度] 打造 Laravel 12 高併發即時客服系統:從 Octane Swoole 到 Reverb 擴展架構 🏎️ 1. 內存常駐化:Laravel Octane + Swoole 的性能飛躍 傳統 PHP-FPM 每個 Request 都要經歷一次框架加載( B...