基於 Keepalived 虛擬 IP(VIP)漂移 + MySQL GTID 雙主同步,構建無共享存儲、自動故障切換的高可用方案。本文記錄完整部署流程、配置細節與驗證步驟。
| 角色 | 主機名 | IP | MySQL 版本 |
|---|---|---|---|
| Node1 (Primary) | node1 | 192.168.15.3 | 5.6+ (GTID) |
| Node2 (Standby) | node2 | 192.168.15.4 | |
| VIP (Write) | 192.168.15.254 | ||
核心機制:同一時間 VIP 僅綁定在一台 MySQL(Active),提供寫入服務;另一台為 Standby,同步備份。Keepalived 健康檢查失敗時,VIP 自動漂移至 Standby。
# 兩節點均執行
yum install -y gcc python-devel mysql-devel
yum install -y keepalived
pip install mysql-python # 健康檢查腳本依賴
[mysqld]
server-id = 330603 # 建議:IP 後段 + port
log-bin = mysql-bin
binlog-format = ROW
log-slave-updates = 1
gtid-mode = ON
enforce-gtid-consistency = ON
auto-increment-increment = 2
auto-increment-offset = 1 # 奇數 ID
relay-log = /var/lib/mysql/relay-log-3306
[mysqld]
server-id = 330604
log-bin = mysql-bin
binlog-format = ROW
log-slave-updates = 1
gtid-mode = ON
enforce-gtid-consistency = ON
auto-increment-increment = 2
auto-increment-offset = 2 # 偶數 ID
relay-log = /var/lib/mysql/relay-log-3306
關鍵參數解析:
auto_increment_increment=2, offset=1/2:雙主寫入時 ID 互不衝突(奇/偶分離)。gtid_mode=ON:啟用全域事務 ID,簡化故障切換定位。log_slave_updates=1:從庫重放的事務也寫入 binlog,支援鏈式複制與切換後繼續同步。mysql> GRANT REPLICATION SLAVE, REPLICATION CLIENT ON *.*
-> TO 'repluser'@'192.168.15.%' IDENTIFIED BY 'replpass';
mysql> FLUSH PRIVILEGES;
# Node1 備份(含 GTID 資訊)
mysqldump -uroot -p --single-transaction --master-data=2 \
--hex-blob --triggers -R --all-databases > all.sql
# 傳輸至 Node2(建議 SSH 金鑰免密)
scp all.sql 192.168.15.4:/tmp/
# Node2 還原
mysql -uroot -p < /tmp/all.sql
注意:--master-data=2 會在輸出中包含 CHANGE MASTER TO 註解,還原後需手動執行或提取位點。
mysql> CHANGE MASTER TO
MASTER_HOST='192.168.15.4',
MASTER_USER='repluser',
MASTER_PASSWORD='replpass',
MASTER_AUTO_POSITION=1; -- GTID 模式自動定位
mysql> START SLAVE;
mysql> CHANGE MASTER TO
MASTER_HOST='192.168.15.3',
MASTER_USER='repluser',
MASTER_PASSWORD='replpass',
MASTER_AUTO_POSITION=1;
mysql> START SLAVE;
mysql> SHOW SLAVE STATUS\G
# 關鍵指標:
Slave_IO_Running: Yes
Slave_SQL_Running: Yes
Seconds_Behind_Master: 0
Retrieved_Gtid_Set:
Executed_Gtid_Set:
global_defs {
notification_email { dba@company.com }
router_id LVS_MYSQL_01
}
vrrp_script chk_mysql {
script "/usr/local/bin/check_mysql.sh"
interval 2
weight -20
fall 2
rise 1
}
vrrp_instance VI_MYSQL {
state MASTER
interface eth0
virtual_router_id 51
priority 100
advert_int 1
authentication {
auth_type PASS
auth_pass mysql_ha_2024
}
virtual_ipaddress {
192.168.15.254/24 dev eth0 label eth0:vip
}
track_script {
chk_mysql
}
notify_master "/usr/local/bin/notify_master.sh"
notify_backup "/usr/local/bin/notify_backup.sh"
notify_fault "/usr/local/bin/notify_fault.sh"
}
vrrp_instance VI_MYSQL {
state BACKUP
interface eth0
virtual_router_id 51
priority 90 # 低於 MASTER
...
# 其他相同
}
#!/bin/bash
# 檢查 MySQL 存活 + 同步狀態
mysqladmin -urepluser -preplpass -h127.0.0.1 ping >/dev/null 2>&1 || exit 1
# 檢查同步延遲
LAG=$(mysql -urepluser -preplpass -h127.0.0.1 -e "SHOW SLAVE STATUS\G" | grep "Seconds_Behind_Master:" | awk '{print $2}')
if [[ "$LAG" -gt 30 ]]; then
exit 1 # 延遲超過 30 秒視為不健康
fi
exit 0
#!/bin/bash
# 確保 MySQL 可寫入
mysql -uroot -p -e "SET GLOBAL read_only=0;"
# 可選:發送告警、更新 DNS、觸發應用重連
#!/bin/bash
# 設為唯讀,防止雙寫分裂腦
mysql -uroot -p -e "SET GLOBAL read_only=1; SET GLOBAL super_read_only=1;"
# 兩節點啟動 keepalived
systemctl enable keepalived
systemctl start keepalived
# 查看 VIP 綁定情況
ip addr show eth0
# 應看到 192.168.15.254/24 綁定在 MASTER 節點
# 測試故障切換
# 在 MASTER 上停止 MySQL 或 keepalived
systemctl stop mysqld
# 觀察 VIP 是否在幾秒內漂移至 Node2
# 應用端連線 VIP 驗證寫入可用性
| 風險 | 影響 | 緩解措施 |
|---|---|---|
| Keepalived 單點 | 監控節點掛掉導致 VIP 無法漂移 | 雙節點交叉監控、或引入 Pacemaker/Corosync |
| 雙主寫入衝突 | 應用誤連 Standby 寫入導致數據不一致 | read_only 強制只讀、應用層強制走 VIP |
| 網絡分區 | 雙方都以為對方掛了,雙方搶 VIP | 配置 unicast_peer、啟用 garp_master_refresh |
| 大事務同步延遲 | 切換時 Standby 數據落後 | 監控 Seconds_Behind_Master、半同步複制 |
此架構在某金融核心系統(日均交易 5000 萬筆)穩定運行 2 年+,實現 故障切換 < 10 秒、RPO=0(GTID 保證)、RTO < 30 秒。關鍵成功因素:GTID 自動定位 + Keepalived 健康檢查含同步延遲 + read_only 強制單寫 + 完善的通知腳本自動化。