背景
專案中使用的mariadb+gelera叢集模式部署,之前一直用的是mysql的master/slave方式部署資料庫的,這種叢集模式以前沒怎麼搞過,這裡研究並記錄一下。
MariaDB Galera Cluster 介紹MariaDB 叢集是 MariaDB 同步多主機叢集。它僅支援 XtraDB/ InnoDB 儲存引擎(雖然有對 MyISAM 實驗支援 - 看 wsrep_replicate_myisam 系統變數)。
主要功能:
同步複製真正的 multi-master,即所有節點可以同時讀寫資料庫自動的節點成員控制,失效節點自動被清除新節點加入資料自動複製真正的並行複製,行級使用者可以直接連線叢集,使用感受上與MySQL完全一致優勢:
因為是多主,所以不存在Slavelag(延遲)不存在丟失事務的情況同時具有讀和寫的擴充套件能力更小的客戶端延遲節點間資料是同步的,而 Master/Slave 模式是非同步的,不同 slave 上的 binlog 可能是不同的技術:
Galera 叢集的複製功能基於 Galeralibrary 實現,為了讓 MySQL 與 Galera library 通訊,特別針對 MySQL 開發了 wsrep API。
Galera 外掛保證叢集同步資料,保持資料的一致性,靠的就是可認證的複製,工作原理如下圖:
mariadb_galera_cluster
當客戶端發出一個 commit 的指令,在事務被提交之前,所有對資料庫的更改都會被write-set收集起來,並且將 write-set 紀錄的內容傳送給其他節點。
write-set 將在每個節點進行認證測試,測試結果決定著節點是否應用write-set更改資料。
如果認證測試失敗,節點將丟棄 write-set ;如果認證測試成功,則事務提交。
MariaDB Galera Cluster搭建我這裡實驗時使用的作業系統是CentOS7,使用了3臺虛擬機器,IP分別為10.211.55.6、10.211.55.7、10.211.55.8
關閉防火牆及selinux為了先把MariaDB Galera Cluster部署起來,不受防火牆、selinux的干擾,先把3臺虛擬機器上這倆關閉了。如果防火牆一定要開啟,可參考這裡設定防火牆規則。
systemctl disable firewalld.servicesystemctl stop firewalld.servicesetenforce 0sed -i 's/^SELINUX=.*$/SELINUX=disabled/' /etc/selinux/config
新增mariadb的yum源
在3臺虛擬機器上執行以下命令
# 已使用國內yum映象,原映象地址是http://yum.mariadb.orgecho '[mariadb]name = MariaDBbaseurl = http://mirrors.ustc.edu.cn/mariadb/yum/10.1/centos7-amd64gpgkey=http://mirrors.ustc.edu.cn/mariadb/yum/RPM-GPG-KEY-MariaDBgpgcheck=1' > /etc/yum.repos.d/MariaDB.repo
安裝軟體包
在3臺虛擬機器上執行以下命令
1 yum install -y mariadb mariadb-server mariadb-common galera rsync
資料庫初始化在10.211.55.6上執行以下命令
systemctl start mariadbmysql_secure_installation # 注意這一步是有互動的,需要回答一些問題,做一些設定systemctl stop mariadb
修改galera相關配置在3臺虛擬機器上均開啟/etc/my.cnf.d/server.cnf進行編輯,修改片段如下:
...[galera]wsrep_on=ONwsrep_provider=/usr/lib64/galera/libgalera_smm.sowsrep_cluster_name=galera_clusterwsrep_cluster_address="gcomm://10.211.55.6,10.211.55.7,10.211.55.8"wsrep_node_name=10.211.55.6 # 注意這裡改成本機IPwsrep_node_address=10.211.55.6 # 注意這裡改成本機IPbinlog_format=rowdefault_storage_engine=InnoDBinnodb_autoinc_lock_mode=2...
啟動MariaDB Galera Cluster服務
先在第1臺虛擬機器執行以下命令:
sudo -u mysql /usr/sbin/mysqld --wsrep-new-cluster &> /tmp/wsrep_new_cluster.log &disown $!tail -f /tmp/wsrep_new_cluster.log
出現 ready for connections ,證明啟動成功,繼續在另外兩個虛擬機器裡執行命令:
1 systemctl start mariadb
等後面兩個虛擬機器裡mariadb服務啟動後,再到第1臺虛擬機器裡執行以下命令:
(ps -ef|grep mysqld|grep -v grep|awk '{print $2}'|xargs kill -9) &>/dev/nullsystemctl start mariadb
驗證MariaDB Galera Cluster服務
在任意虛擬機器裡執行以下命令:
mysql -e "show status like 'wsrep_cluster_size'" # 這裡應該顯示集群裡有3個節點mysql -e "show status like 'wsrep_connected'" # 這裡應該顯示ONmysql -e "show status like 'wsrep_incoming_addresses'" # 這裡應該顯示10.211.55.7:3306,10.211.55.8:3306,10.211.55.6:3306mysql -e "show status like 'wsrep_local_state_comment'" # 這裡節點的同步狀態
檢視叢集全部相關狀態引數可執行以下命令:
1 mysql -e "show status like 'wsrep_%'"
至此,MariaDB Galera Cluster已經成功部署。
MariaDB Galera Cluster的自啟動在實際使用中發現一個問題,Galera叢集啟動時必須按照一個特定的規則啟動,研究了下,發現規則如下:
如果叢集從來沒有啟動過(3個節點上都沒有/var/lib/mysql/grastate.dat檔案),則必要由其中一個節點以--wsrep-new-cluster引數啟動,另外兩個節點正常啟動即可如果叢集以前啟動過,則參考/var/lib/mysql/grastate.dat,找到safe_to_bootstrap為1的節點,在該節點上以--wsrep-new-cluster引數啟動,另外兩個節點正常啟動即可如果叢集以前啟動過,但參考/var/lib/mysql/grastate.dat,找不到safe_to_bootstrap為1的節點(一般是因為mariadb服務非正常停止造成),則在3個節點中隨便找1個節點,將/var/lib/mysql/grastate.dat中的safe_to_bootstrap修改為1,再在該節點上以--wsrep-new-cluster引數啟動,另外兩個節點正常啟動即可從以上3種場景可知,正常情況下很難保證mariadb galera cluster可以無人值守地完成開機自啟動。國外論壇上也有人反映了這個問題,但好像官方的人員好像說設計上就是這樣,怎麼可以這樣。。。
最後寫了個指令碼,放在3個虛擬機器上面,解決了這個問題。指令碼如下:
cat /usr/local/bin/mariadb_cluster_helper.sh#!/bin/bashGRASTATE_FILE=/var/lib/mysql/grastate.datWSREP_NEW_CLUSTER_LOG_FILE=/tmp/wsrep_new_cluster.log# 如果啟動mariadb超過10秒還沒返回0,則認為失敗了START_MARIADB_TIMEOUT=10# 以--wsrep-new-cluster引數啟動,超過5次檢查,發現仍沒有其它節點加入叢集,則認為此路不通SPECIAL_START_WAIT_MAX_COUNT=5# 得到本機IPMY_IP=$(grep 'wsrep_node_address' /etc/my.cnf.d/server.cnf | awk -F '=' '{print $2}')# 殺掉mysqld程序function kill_mysqld_process() { (ps -ef|grep mysqld|grep -v grep|awk '{print $2}'|xargs kill -9) &>/dev/null}# 正常啟動mariadbfunction start_mariadb_normal(){ # 首先確保safe_to_bootstrap標記為0 sed -i 's/^safe_to_bootstrap.*$/safe_to_bootstrap: 0/' $GRASTATE_FILE timeout $START_MARIADB_TIMEOUT systemctl start mariadb &> /dev/null return $?}# 以--wsrep-new-cluster引數啟動mariadbfunction start_mariadb_special(){ # 首先確保safe_to_bootstrap標記為1 sed -i 's/^safe_to_bootstrap.*$/safe_to_bootstrap: 1/' $GRASTATE_FILE # 以--wsrep-new-cluster引數啟動mariadb /usr/sbin/mysqld --user=mysql --wsrep-new-cluster &> $WSREP_NEW_CLUSTER_LOG_FILE & disown $! try_count=0 # 迴圈檢查 while [ 1 ]; do # 如果超過SPECIAL_START_WAIT_MAX_COUNT次檢查,仍沒有其它節點加入叢集,則認為此路不通,嘗試正常啟動,跳出迴圈 if [ $try_count -gt $SPECIAL_START_WAIT_MAX_COUNT ] ; then kill_mysqld_process start_mariadb_normal return $? fi new_joined_count=$(grep 'synced with group' /tmp/wsrep_new_cluster.log | grep -v $MY_IP|wc -l) exception_count=$(grep 'exception from gcomm, backend must be restarted' $WSREP_NEW_CLUSTER_LOG_FILE | wc -l) # 如果新加入的節點數大於0,則認為叢集就緒了,可正常啟動了,跳出迴圈 # 如果執行日誌中發現了異常(兩個節點都以--wsrep-new-cluster引數啟動,其中一個會報錯),則認為此路不通,嘗試正常啟動,跳出迴圈 if [ $new_joined_count -gt 0 ] || [ $exception_count -gt 0 ] ; then kill_mysqld_process start_mariadb_normal return $? else try_count=$(( $try_count + 1 )) fi sleep 5 done}# 首先殺掉mysqld程序kill_mysqld_processret=-1# 如果safe_to_bootstrap標記為1,則立即以--wsrep-new-cluster引數啟動if [ -f $GRASTATE_FILE ]; then safe_bootstrap_flag=$(grep 'safe_to_bootstrap' $GRASTATE_FILE | awk -F ': ' '{print $2}') if [ $safe_bootstrap_flag -eq 1 ] ; then start_mariadb_special ret=$? else start_mariadb_normal ret=$? fielse start_mariadb_normal ret=$?fi# 隨機地按某種方式啟動,直到以某種方式正常啟動以止;否則殺掉mysqld程序,隨機休息一會兒,重試while [ $ret -ne 0 ]; do kill_mysqld_process sleep_time=$(( $RANDOM % 10 )) sleep $sleep_time choice=$(( $RANDOM % 2 )) ret=-1 if [ $choice -eq 0 ] ; then start_mariadb_special ret=$? else start_mariadb_normal ret=$? fidone# 使上述指令碼開機自啟動chmod +x /usr/local/bin/mariadb_cluster_helper.shchmod +x /etc/rc.d/rc.localecho '/usr/local/bin/mariadb_cluster_helper.sh &> /var/log/mariadb_cluster_helper.log &' >> /etc/rc.d/rc.local
然後3個節點終於可以開機自啟動自動組成叢集了。
搭配keepalived+haproxy+clustercheck為了保證mariadb galera叢集的高可用,可以使用haproxy進行請求負載均衡,同時為了實現haproxy的高可用,可使用keepalived實現haproxy的熱備方案。keepalived實現haproxy的熱備方案可參見之前的博文。這裡重點說一下haproxy對mariadb galera叢集的請求負載均衡。
這裡使用了 https://github.com/olafz/percona-clustercheck 所述方案,使用外部指令碼在應用層檢查galera節點的狀態。
首先在mariadb裡進行授權:
1 GRANT PROCESS ON *.* TO 'clustercheckuser'@'%' IDENTIFIED BY 'clustercheckpassword!'
下載檢測指令碼:
wget -O /usr/bin/clustercheck https://raw.githubusercontent.com/olafz/percona-clustercheck/master/clustercheckchmod +x /usr/bin/clustercheck
準備檢測指令碼用到的配置檔案:
MYSQL_USERNAME="clustercheckuser"MYSQL_PASSWORD="clustercheckpassword!"MYSQL_HOST="$db_ip"MYSQL_PORT="3306"AVAILABLE_WHEN_DONOR=0
測試一下監控指令碼:
# /usr/bin/clustercheck > /dev/null# echo $?0 # synced1 # un-synced
使用xinetd暴露http介面,用於檢測galera節點同步狀態:
cat > /etc/xinetd.d/mysqlchk << EOF# default: on# description: mysqlchkservice mysqlchk{ disable = no flags = REUSE socket_type = stream port = 9200 wait = no user = nobody server = /usr/bin/clustercheck log_on_failure += USERID only_from = 0.0.0.0/0 per_source = UNLIMITED}EOFservice xinetd restart
測試一下暴露出的http介面:
curl http://127.0.0.1:9200Galera cluster node is synced. # syncedGalera cluster node is not synced # un-synced
最後在/etc/haproxy/haproxy.cfg裡配置負載均衡:
...frontend vip-mysql bind $vip:3306 timeout client 900m log global option tcplog mode tcp default_backend vms-mysqlbackend vms-mysql option httpchk stick-table type ip size 1000 stick on dst balance leastconn timeout server 900m server mysql1 $db1_ip:3306 check inter 1s port 9200 backup on-marked-down shutdown-sessions maxconn 60000 server mysql2 $db2_ip:3306 check inter 1s port 9200 backup on-marked-down shutdown-sessions maxconn 60000 server mysql2 $db3_ip:3306 check inter 1s port 9200 backup on-marked-down shutdown-sessions maxconn 60000...
搭配galera仲裁服務
官方也提到gelera叢集最少要三節點部署,但每增加一個節點,要付出相應的資源,因此也可以最少兩節點部署,再加上一個galera仲裁服務。
The recommended deployment of Galera Cluster is that you use a minimum of three instances. Three nodes, three datacenters and so on. In the event that the expense of adding resources, such as a third datacenter, is too costly, you can use Galera Arbitrator. Galera Arbitrator is a member of the cluster that participates in voting, but not in the actual replication
這種部署模式有兩個好處:
使叢集剛好是奇數節點,不易產生腦裂。可能透過它得到一個一致的資料庫狀態快照,可以用來備份。這種部署模式的架構圖如下:
mage-20180401214224
部署方法也比較簡單:
# 假設已經構建了一個兩節點的galera叢集,在第3個節點部署garbd服務echo 'GALERA_NODES="10.211.55.6:4567 10.211.55.7:4567" # 這裡是兩節點的地址GALERA_GROUP="galera_cluster" # 這裡的group名稱保持與兩節點的wsrep_cluster_name屬性一致LOG_FILE="/var/log/garb.log"' > /etc/sysconfig/garbsystemctl start garb # 啟動garbd服務
測試一下效果。
首先看一下兩節點部署產生腦裂的場景。
# 首先在第3個節點停止garb服務systemctl stop garb# 然後在第2個節點drop掉去住第1個節點和仲裁節點的資料包iptables -A OUTPUT -d 10.211.55.6 -j DROPiptables -A OUTPUT -d 10.211.55.9 -j DROP# 這時檢查前兩個節點的同步狀態,發生腦裂了,都不是同步狀態了mysql -e "show status like 'wsrep_local_state_comment'"+---------------------------+-------------+| Variable_name | Value |+---------------------------+-------------+| wsrep_local_state_comment | Initialized |+---------------------------+-------------+
再試驗下有仲裁節點參與的場景。
# 首先在第3個節點啟動garb服務systemctl start garb# 在前兩個節點檢視叢集節點數,發現是3個,說明包括了仲裁節點mysql -e "show status like 'wsrep_cluster_size'"+---------------------------+-------------+| Variable_name | Value |+---------------------------+-------------+| wsrep_cluster_size | 3 |+---------------------------+-------------+# 然後在第2個節點drop掉去住第1個節點和仲裁節點的資料包iptables -A OUTPUT -d 10.211.55.6 -j DROPiptables -A OUTPUT -d 10.211.55.9 -j DROP# 這時檢查第1個節點的同步狀態,仍然是同步狀態mysql -e "show status like 'wsrep_local_state_comment'"+---------------------------+-------------+| Variable_name | Value |+---------------------------+-------------+| wsrep_local_state_comment | Synced |+---------------------------+-------------+# 再在第1個節點檢視叢集節點數,發現是2個mysql -e "show status like 'wsrep_cluster_size'"+---------------------------+-------------+| Variable_name | Value |+---------------------------+-------------+| wsrep_cluster_size | 2 |+---------------------------+-------------+# 這時檢查第2個節點的同步狀態,發現是未同步的mysql -e "show status like 'wsrep_local_state_comment'"+---------------------------+-------------+| Variable_name | Value |+---------------------------+-------------+| wsrep_local_state_comment | Initialized |+---------------------------+-------------+
以前試驗說明採用了仲裁節點後,因為叢集節點數變為了奇數,有效地避免了腦裂,同時將真正有故障的節點隔離出去了。
參考https://segmentfault.com/a/1190000002955693https://github.com/olafz/percona-clustercheckhttp://galeracluster.com/documentation-webpages/arbitrator.html