1. 程式人生 > >Rainbond部署Mysql主從叢集應用詳解

Rainbond部署Mysql主從叢集應用詳解

Mysql主從同步原理

1)在Slave 伺服器上執行sart slave命令開啟主從複製開關,開始進行主從複製。

2)此時,Slave伺服器的IO執行緒會通過在master上已經授權的複製使用者許可權請求連線master伺服器,並請求從執行binlog日誌檔案的指定位置(日誌檔名和位置就是在配置主從複製服務時執行change master命令指定的)之後開始傳送binlog日誌內容。

3)Master伺服器接收到來自Slave伺服器的IO執行緒的請求後,其上負責複製的IO執行緒會根據Slave伺服器的IO執行緒請求的資訊分批讀取指定binlog日誌檔案指定位置之後的binlog日誌資訊,然後返回給Slave端的IO執行緒。返回的資訊中除了binlog日誌內容外,還有在Master伺服器端記錄的IO執行緒。返回的資訊中除了binlog中的下一個指定更新位置。

4)當Slave伺服器的IO執行緒獲取到Master伺服器上IO執行緒傳送的日誌內容、日誌檔案及位置點後,會將binlog日誌內容依次寫到Slave端自身的Relay Log(即中繼日誌)檔案(Mysql-relay-bin.xxx)的最末端,並將新的binlog檔名和位置記錄到master-info檔案中,以便下一次讀取master端新binlog日誌時能告訴Master伺服器從新binlog日誌的指定檔案及位置開始讀取新的binlog日誌內容。

5)Slave伺服器端的SQL執行緒會實時檢測本地Relay Log 中IO執行緒新增的日誌內容,然後及時把Relay LOG 檔案中的內容解析成sql語句,並在自身Slave伺服器上按解析SQL語句的位置順序執行應用這樣sql語句,並在relay-log.info中記錄當前應用中繼日誌的檔名和位置點。

Mysql主從同步注意事項

  • master節點和slave節點的uuid不同
  • master節點和slave節點的server_id不同
  • slave節點需要自動執行向master節點註冊的操作

製作Mysql容器映象

同一映象建立不同容器的uuid

用同一mysql映象建立mysql主從叢集時,發現每臺mysql服務的uuid都是相同的,是因為在資料初始化時將uuid寫在了/var/lib/mysql/auto.cnf檔案中,造成每個容器的uuid都是相同的。

為了解決不同容器的uuid不同問題,需要在mysql啟動生成配置檔案後並在啟動前 隨機生成一個uuid寫入到/var/lib/mysql/auto.cnf,這樣就可以確保同一映象生成的容器的uuid都不相同。

為了達成這一目標,我們修改了mysql映象自帶的啟動指令碼/usr/local/bin/docker-entrypoint.sh

if [ ! -d "$DATADIR/mysql" ]; then
		file_env 'MYSQL_ROOT_PASSWORD'
		if [ -z "$MYSQL_ROOT_PASSWORD" -a -z "$MYSQL_ALLOW_EMPTY_PASSWORD" -a -z "$MYSQL_RANDOM_ROOT_PASSWORD" ]; then
			echo >&2 'error: database is uninitialized and password option is not specified '
			echo >&2 '  You need to specify one of MYSQL_ROOT_PASSWORD, MYSQL_ALLOW_EMPTY_PASSWORD and MYSQL_RANDOM_ROOT_PASSWORD'
			exit 1
		fi

		mkdir -p "$DATADIR"

		echo 'Initializing database'
		"$@" --initialize-insecure
		echo 'Database initialized'
                # 位於mysql啟動指令碼90行,新增如下語句
                psd="/proc/sys/kernel/random/uuid"
                str=$(cat $psd)
                uuid="server-uuid="${str}
                echo "[auto]" > /var/lib/mysql/auto.cnf
                echo $uuid >> /var/lib/mysql/auto.cnf


同一服務不同例項的server_id處理

用同一MYSQL映象建立MYSQL主從叢集時,如何確保每個MYSQL服務的server_id不同?

k8s在建立容器時,會為每個容器建立建立一個主機名( 如:gr78648d-0),建立多個容器後面的數字會依次遞增,所以可以利用這一特性生成不同的server_id(主機名數字部分 + 指定數字),然後在maser和slave使用不同的數字即可。

從庫自動初始化

建立slave資料庫時,我們希望salve應用下的每個例項,在擴容後,會自動向主庫註冊。

這需要salve應用中例項初始化時,自動執行指定的SQL指令碼。這要藉助於官方MYSQL映象所提供的特定功能:資料庫初始化時,會自動讀取/docker-entrypoint-initdb.d/中*.sql 檔案並執行。

為了實現上述兩個目標,我們在映象的自定義啟動指令碼 /run/docker-entrypoint.sh進行指定:

# define server_id and anyother cluster configuration
# 通過環境變數來區分當前映象建立主庫或從庫
if [ ${MYSQL_ROLE} == "master" ];then
   # 藉助有狀態應用主機名特點,擷取其中的數字
   server_id=${HOSTNAME#*-}
   # 主庫ID設定為1
   MYSQLC_MYSQLD_SERVER_ID=`expr $server_id + 1`
   export MYSQLC_MYSQLD_SERVER_ID
   # 指定生成主庫特定的配置
   export MYSQLC_MYSQLD_binlog_ignore_db=mysql
   export MYSQLC_MYSQLD_log_bin=mysql-bin
else 
   # 藉助有狀態應用主機名特點,擷取其中的數字
   server_id=${HOSTNAME#*-}
   # 從庫各例項ID,從2開始排序
   MYSQLC_MYSQLD_SERVER_ID=`expr $server_id + 2`
   export MYSQLC_MYSQLD_SERVER_ID
   # 指定生成從庫特定配置
   export MYSQLC_MYSQLD_replicate_ignore_db=mysql
   export MYSQLC_MYSQLD_log_bin=mysql-bin
   # 將從庫所需要的初始化指令碼模版拷貝到指定目錄
   cp -a /tmp/init-slave.sql /docker-entrypoint-initdb.d/
   # 根據例項特定的環境變數,對初始化指令碼模版進行更改
   sed -i -r -e "s/MYSQL_ROOT_PASSWORD/${MYSQL_ROOT_PASSWORD}/g" \
             -e "s/MYSQL_USER/${MYSQL_USER}/g" /docker-entrypoint-initdb.d/init-slave.sql
fi

關於指令碼中通過環境變數生成指定配置,參考專案 env2file

製作映象的Dockerfile解析

FROM percona:5.7.23-stretch
LABEL creater="barnett"
ENV MYSQL_VERSION=5.7.23
ENV TZ=Asia/Shanghai

RUN sed -i 's/deb.debian.org/mirrors.ustc.edu.cn/g' /etc/apt/sources.list; \
    rm -rf /etc/apt/sources.list.d/percona.list && apt-get update; \
    apt-get install -y --no-install-recommends wget net-tools vim; \
    rm -rf /var/lib/apt/lists/*; \
    wget -O /usr/local/bin/env2file -q https://github.com/barnettZQG/env2file/releases/download/0.1.1/env2file-linux; \
    chmod +x /usr/local/bin/env2file;
# 自定義啟動指令碼
ADD docker-entrypoint.sh /run/docker-entrypoint.sh
# mysql官方啟動指令碼
ADD ./run/docker-entrypoint.sh /usr/local/bin/docker-entrypoint.sh
ADD ./run/mysqld.cnf /etc/mysql/percona-server.conf.d/mysqld.cnf
# 拷貝slave初始化指令碼模版到映象中,以備呼叫
ADD ./sql /tmp/
EXPOSE 3306
VOLUME ["/var/lib/mysql", "/var/log/mysql"]
ENV MYSQL_ROOT_PASSWORD=changeme
ENTRYPOINT [ "/run/docker-entrypoint.sh" ]
CMD [ "mysqld" ]

建立並配置mysql-master服務

建立mysql-master服務元件

程式碼地址:https://github.com/goodrain-apps/percona-mysql.git?dir=5.7

程式碼分支:cluster

通過Dockerfile建立服務 參考文件 基於Dockerfile原始碼建立服務

mysql-master服務 相關配置

  • 開啟3306埠對內服務,並更改使用別名為MYSQL
  • 配置關鍵環境變數
環境變數說明
MYSQL_ROOT_PASSWORDchangeme(預設)自行指定root密碼
MYSQL_USER自行指定,如adminmysql工作使用者
MYSQL_PASSWORD自行指定工作使用者密碼
MYSQL_DATABASE自行指定初始化生成資料庫
MYSQL_ROLEmaster指定角色

其中 除MYSQL_ROLE外,其他環境變數要在服務建立完成後,轉移到連線資訊中去。

  • 部署屬性中,修改應用型別為 有狀態應用

建立Slave服務

建立mysql-slave服務元件

建立方式同mysql-master服務元件一致。

mysql-slave服務 相關配置

區別於mysql-master服務元件,mysql-slave服務元件配置如下:

  • 開啟3307埠對內服務,並更改使用別名為 MYSQL_SLAVE
  • 配置環境變數
環境變數說明
MYSQLC_MYSQLD_PORT3307mysql-slave監聽3307埠
  • mysql-slave服務依賴於mysql-master服務

mysql-slave服務元件,可以隨意擴容,指令碼中寫好的邏輯會讓其自動向mysql-master註冊。

至此,一個基本的 MYSQL主從叢集就已經搭建完成,如需要釋出到應用市場供隨時下載使用,請參考應用分享與釋出

讀寫分離

機制

MYSQL主從叢集的一個好處就是,可以配置master庫負責寫入,slave庫負責查詢,slave自動從master同步資料的讀寫分離結構。

如果設定得當,這樣的結構可以大幅度提高資料庫效能的同時,降低主庫的壓力。

使用方法

如果使用者的業務程式已經支援讀寫分離,那麼只需要設定:

  • 資料庫寫入地址為mysql-master服務地址,如果使用Rainbond服務依賴,則可以用 ${MYSQL_HOST}:${MYSQL_PORT}的方式指定連線地址。
  • 資料庫查詢地址為mysql-slave服務地址,如果使用Rainbond服務依賴,則可以用${MYSQL_SLAVE_HOST}:${MYSQL_SLAVE_PORT}的方式指定連線地址。

如果使用者的業務程式不支援讀寫分離,那麼就要靠支援讀寫分離的中介軟體實現。

Atlas中介軟體

Atlas是由奇虎360開源的資料庫中介軟體,基於mysql官方提供的mysql-proxy改良而來。通過將mysql-proxy的LUA指令碼,用C語言重新實現,Atlas提供了比mysql-proxy更強大的效能。經由中介軟體的代理,使用者只需要配置資料庫連線地址為 Atlas 服務地址,對於資料庫的寫入和查詢,則由 Atlas 來管理。

詳細瞭解Atlas

我們提供的docker化的Atlas元件,使用者可以直接基於Dockerfile原始碼構建這個專案:

Atlas-docker專案地址

Rainbond已經發布的 Mysql主從叢集 應用,已經集成了該中介軟體。

高階實現

當前架構缺點

目前搭建的 MYSQL主從叢集,是一個master節點,對接多個slave節點。這樣的架構在小規模叢集下沒有問題。但是如果叢集規模很大、slave節點過多的時候,由master向所有slave節點同步資料這一過程將變成效能的瓶頸。

架構的優化

當用戶完全掌握瞭如何基於Rainbond搭建MYSQL主從集群后,可以自己嘗試,專門建立一個slave節點,作為資料同步節點使用。

該節點向上對接 master節點,來同步資料;向下對接slave叢集,分發由master節點同步來的資料。

這樣做的好處是,master節點只需要對接一個數據同步節點來同步資料,可以更加專注於資料的寫入。其他slave節點從資料同步節點來同步資料。

如果有使用者實現了這種優化,歡迎將其分享到應用市場中,供更