Haproxy+PXC搭建Mysql集群

安裝環境

centos7, docker

集群方案介紹

大型網站數據庫需要應對高負載、實現高可用。而單節點數據庫在并發量大的情況下無法滿足性能要求,且一臺宕機,整個服務受影響。

  • Mysql集群:PXC
  • 負載均衡:Haproxy
  • 高可用:Keepalived
高可用Mysql集群方案

搭建pxc集群

安裝pxc鏡像

docker官方倉庫(https://hub.docker.com)搜索 percona-xtradb-cluster,拉取鏡像命令為: docker pull percona/percona-xtradb-cluster
終端執行:

// 安裝pxc鏡像
? docker pull percona/percona-xtradb-cluster
// 查看鏡像
? docker images
REPOSITORY                                        TAG                 IMAGE ID            CREATED             SIZE
docker.io/percona/percona-xtradb-cluster        latest              f3abd21f393a        3 days ago          72.1 MB
// 鏡像名稱太長,為方便使用,修改一下
? docker tag docker.io/percona/percona-xtradb-cluster pxc
// 刪除原鏡像
docker rmi docker.io/percona/percona-xtradb-cluster

創建Docker內部網絡

出于安全考慮,不要把pxc容器的IP直接暴露出去,而是創建docker內部網絡。

// 網段subnet自定義,網段名自定義為network20
? docker network create --subnet=172.20.0.0/16 network20
2e9196b0637074cad609be8e4e890aa3e889209af8c2b55ce8097981000bf53f
// 查看網段詳細信息
? docker network inspect network20

創建Docker卷

docker的使用原則之一:不要在容器內保存業務數據,而應該保存在宿主機中
實現方式:通過目錄映射,把宿主機的目錄映射到容器內,運行容器時,把數據保存在映射目錄中,也就是保存在宿主機中。當容器發生故障時,只需把故障容器刪除掉,重新啟動一個容器,把宿主機的目錄映射給新容器。
pxc運行在容器中,無法直接使用映射目錄,會發生閃退。需要借助Docker卷完成映射。

// 創建一個docker卷,自定義名稱為 volume-mysql-1
? docker volume create --name volume-mysql-1
// 可以查看數據卷在宿主機上當真實目錄
? docker volume inspect volume-mysql-1
[
    {
        "Driver": "local",
        "Labels": {},
        "Mountpoint": "/var/lib/docker/volumes/volume-mysql-1/_data", // 數據卷在宿主機真實的路徑
        "Name": "volume-mysql-1", // 數據卷名稱
        "Options": {},
        "Scope": "local"
    }
]

創建PXC容器

  • 命令分解
// -d 創建出的容器后臺運行
docker run -d
// -p 端口映射,[宿主機端口]:[容器端口]
-p 3311:3306
// -e 啟動參數,MYSQL_ROOT_PASSWORD數據庫root密碼
-e MYSQL_ROOT_PASSWORD=123456
// CLUSTER_NAME集群名稱
-e CLUSTER_NAME=pxc-test 
// XTRABACKUP_PASSWORD數據庫間節點同步密碼
-e XTRABACKUP_PASSWORD=123456 
// -v 目錄映射,[宿主機目錄(數據卷)]:[容器目錄]
-v volume-mysql-1:/var/lib/mysql
// 最高權限
--privileged
// --name 容器名稱
--name mysql-node1
// --net 給容器指定網段,指定IP
--net network20 --ip 172.20.0.2 
// 鏡像名
pxc

執行指令創建第一個pxc節點

// 創建第一個pxc節點 mysql-node1
? docker run -d -p 3311:3306 -e MYSQL_ROOT_PASSWORD=123456 -e CLUSTER_NAME=pxc-test -e XTRABACKUP_PASSWORD=123456 -v volume-mysql-1:/var/lib/mysql --privileged --name mysql-node1 --net network20 --ip 172.20.0.2 pxc

注意:容器創建很快就會完成,但是容器內mysql數據庫的初始化大概需要2分鐘左右,一定要等到mysql初始化完成后,再創建第2個容器,否則第2個容器會發生閃退! 判斷mysql-node1是否完成初始化,可以在宿主機終端執行指令:docker exec -it mysql-node1 mysql -uroot -p,看是否連接成功。

創建第2-5個pxc節點,與第1個節點略有區別:

  • 修改映射宿主機端口號(-p)
  • 修改容器名稱 (--name)
  • 修改映射的數據卷 (-v)
  • 修改分配的IP地址 (--ip)
  • 增加一個參數用于加入集群,和第一個pxc節點進行同步: -e CLUSTER_JOIN=mysql-node1
  • 除第1個容器以外,其余mysql初始化時間非常短,無需等待
// 創建pxc容器 mysql-node2
? docker run -d -p 3312:3306 -e MYSQL_ROOT_PASSWORD=123456 -e CLUSTER_NAME=pxc-test -e XTRABACKUP_PASSWORD=123456 -e CLUSTER_JOIN=mysql-node1 -v volume-mysql-2:/var/lib/mysql --privileged --name mysql-node2 --net network20 --ip 172.20.0.3 pxc
// 創建pxc容器 mysql-node3
? docker run -d -p 3313:3306 -e MYSQL_ROOT_PASSWORD=123456 -e CLUSTER_NAME=pxc-test -e XTRABACKUP_PASSWORD=123456 -e CLUSTER_JOIN=mysql-node1 -v volume-mysql-3:/var/lib/mysql --privileged --name mysql-node3 --net network20 --ip 172.20.0.4 pxc
// 創建pxc容器 mysql-node4
? docker run -d -p 3314:3306 -e MYSQL_ROOT_PASSWORD=123456 -e CLUSTER_NAME=pxc-test -e XTRABACKUP_PASSWORD=123456 -e CLUSTER_JOIN=mysql-node1 -v volume-mysql-4:/var/lib/mysql --privileged --name mysql-node4 --net network20 --ip 172.20.0.5 pxc
// 創建pxc容器 mysql-node5
? docker run -d -p 3315:3306 -e MYSQL_ROOT_PASSWORD=123456 -e CLUSTER_NAME=pxc-test -e XTRABACKUP_PASSWORD=123456 -e CLUSTER_JOIN=mysql-node1 -v volume-mysql-5:/var/lib/mysql --privileged --name mysql-node5 --net network20 --ip 172.20.0.6 pxc

檢測pxc集群的數據同步

可在宿主機上直接登陸某個容器的mysql客戶端;也可以使用mysql可視化工具,如Navicat等遠程登陸,遠程登陸的需要防火墻開放端口。
以下演示宿主機終端驗證方法:

// 登陸mysql-node1節點的mysql終端,創建db=demo,table=user
? docker exec -it mysql-node1 mysql -uroot -p
mysql> create database demo;
mysql> use demo
Database changed
Query OK, 1 row affected (0.02 sec)
mysql> create table student(id int unsigned not null auto_increment, name varchar(50) not null default '', primary key (id)) engine=innodb;
Query OK, 0 rows affected (0.04 sec)
mysql> exit
Bye

// 退出mysql-node1,登陸mysql-node2
? docker exec -it mysql-node2 mysql -uroot -p
mysql> use demo
Database changed
mysql> show tables;
+----------------+
| Tables_in_demo |
+----------------+
| student        |
+----------------+
1 row in set (0.01 sec)
// 其它節點同mysql-node2

任意節點寫入新數據,其它節點都會同步。至此,pxc集群搭建完成。

haproxy配置負載均衡

負載均衡簡單介紹

負載均衡可以使應用數據均勻的落在pxc集群的每一個節點上。如果不使用負載均衡,單節點處理所有請求,會導致該節點負載高、性能差;其它節點卻空閑浪費。
haproxy做負載均衡器,它不是數據庫,只是一個轉發件。


負載均衡器

Docker安裝Haproxy鏡像

// 拉取鏡像
? docker pull haproxy
// 查看鏡像
? docker images
REPOSITORY                  TAG                 IMAGE ID            CREATED             SIZE
docker.io/haproxy           latest              f3abd21f393a        3 days ago          72.1 MB
pxc                         latest              70b3670450ef        4 weeks ago         408 MB

創建haproxy配置文件

// 宿主機上創建haproxy配置文件,路徑自己指定,之后創建容器會用到
? touch /home/softconf/haproxy/haproxy.cfg
// haproxy.conf寫入下列內容,具體配置需要修改
global
    #工作目錄
    chroot /usr/local/etc/haproxy
    #日志文件,使用rsyslog服務中local5日志設備(/var/log/local5),等級info
    log 127.0.0.1 local5 info
    #守護進程運行
    daemon

defaults
    log global
    mode    http
    #日志格式
    option  httplog
    #日志中不記錄負載均衡的心跳檢測記錄
    option  dontlognull
    #連接超時(毫秒)
    timeout connect 5000
    #客戶端超時(毫秒)
    timeout client  50000
    #服務器超時(毫秒)
    timeout server  50000

#監控界面
listen  admin_stats
    #監控界面的訪問的IP和端口
    bind  0.0.0.0:8888
    #訪問協議
    mode        http
    #URI相對地址
    stats uri   /dbs
    #統計報告格式
    stats realm     Global\ statistics
    #登陸帳戶信息
    stats auth  admin:admin123456
#數據庫負載均衡
listen  proxy-mysql
    #訪問的IP和端口
    bind  0.0.0.0:3306
    #網絡協議
    mode  tcp
    #負載均衡算法(輪詢算法)
    #輪詢算法:roundrobin
    #權重算法:static-rr
    #最少連接算法:leastconn
    #請求源IP算法:source
    balance  roundrobin
    #日志格式
    option  tcplog
    #在MySQL中創建一個沒有權限的haproxy用戶,密碼為空。Haproxy使用這個賬戶對MySQL數據庫心跳檢測
    option  mysql-check user haproxy
    server  MySQL_1 172.20.0.2:3306 check weight 1 maxconn 2000
    server  MySQL_2 172.20.0.3:3306 check weight 1 maxconn 2000
    server  MySQL_3 172.20.0.4:3306 check weight 1 maxconn 2000
    server  MySQL_4 172.20.0.5:3306 check weight 1 maxconn 2000
    server  MySQL_5 172.20.0.6:3306 check weight 1 maxconn 2000
    #使用keepalive檢測死鏈
    option  tcpka

haproxy.cnf 與pxc相關配置 重點查看listen proxy-mysql

創建haproxy容器

// -p 端口號 8888 :haproxy后臺監控界面端口,與haproxy.cnf中的配置一致
// -p 端口號 3306 :haproxy對外提供負載均衡服務端口,可以直接通過該端口連接到pxc集群
// -v 目錄映射,把宿主機的配置文件所在目錄映射到容器的工作目錄中
// --name 容器名,建議帶數字編號1,高可用時haproxy需要配置成雙節點
// --net 網段需要和pxc集群在同一網段中
// --ip 可以指定IP,當不指定時docker虛擬機會默認分配一個
? docker run -it -d -p 4001:8888 -p 4002:3306 -v /home/softconf/haproxy:/usr/local/etc/haproxy --name mysql-haproxy1 --privileged --net=network20 haproxy
// 進入容器中,榮國配置文件啟動 haproxy
? docker exec -it mysql-haproxy1 bash
root@0672cb121dde:/# haproxy -f /usr/local/etc/haproxy/haproxy.cfg

創建mysql的haproxy用戶

進入pxc某個節點,在mysql中創建haproxy用戶。haproxy中間件需要使用該賬號登陸數據庫,發送心跳檢測。

// 登陸一個pxc節點的mysql終端
? docker exec -it mysql-node2 mysql -uroot -p
mysql> use mysql
Database changed
mysql> create user 'haproxy'@'%' identified by '';
mysql> flush privileges;

驗證haproxy

通過瀏覽器訪問haproxy監控頁面,遠程訪問需要開放端口(CentOS7使用firewalld打開關閉防火墻與端口)

  • 訪問地址:http://宿主機IP:4001/dbs
  • 訪問路徑配置文件中:stats uri /dbs
  • 用戶名、密碼在配置文件中:stats auth admin:admin123456
    haproxy監控頁面

在監控見面中可以看到5個節點都是啟動狀態,當暫停/停止一個pxc節點時,監控界面的down節點狀態(顏色)發生變化。由于haproxy會將請求發送至其它可用節點,此時pxc集群仍然可用。

// 終端執行命令,暫停一個節點
?  haproxy docker pause mysql-node2
haproxy監控界面 - node down

mysql客戶端連接haproxy

// 宿主機上連接mysql客戶端
// -P 端口號,haproxy的3306端口映射到宿主機的4002端口
// -p 密碼,pxc集群中的mysql密碼
?  haproxy mysql -h127.0.0.1 -P4002 -uroot -p
// 或通過navicat工具連接,-h為宿主機的公網IP

// 進入mysql后,操作的所有數據都同步到pxc集群的全部節點
...

TODO : 集群高可用

單節點的Haproxy不具備高可用,必須要有冗余設計。
keepalived與haproxy安裝在同一個容器中,搶占虛擬IP。
阿里云服務器不支持虛擬IP,keepalived實現高可用待實現

Tips

Keepalived+mysql 雙主一般來說,有幾個需要注意的點,原文地址:MySQL 高可用性keepalived+mysql雙主

1.采用 keepalived 作為高可用方案時,兩個節點最好都設置成 BACKUP模式,避免因為意外情況下(比如 腦裂)相互搶占導致往兩個節點寫入相同數據而引發沖突;
2.把兩個節點的 auto_increment_increment(自增步長)和 auto_increment_offset(自增起始值)設成不同值。其目的是為了避免 master 節點意外宕機時,可能會有部分 binlog 未能及時復制到slave上被應用,從而會導致slave新寫入數據的自增值和原先master上沖突了,因此一開始就使其錯開;當然了,如果有合適的容錯機制能解決主從自增 ID 沖突的話,也可以不這么做;
3.slave 節點服務器配置不要太差,否則更容易導致復制延遲。作為熱備節點的 slave 服務器,硬件配置不能低于 master 節點;
4.如果對延遲問題很敏感的話,可考慮使用 MariaDB 分支版本,或者直接上線 MySQL 5.7 最新版本,利用多線程復制的方式可以很大程度降低復制延遲;

參考資料

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容