安装环境
centos7, docker
集群方案介绍
大型网站数据库需要应对高负载、实现高可用。而单节点数据库在并发量大的情况下无法满足性能要求,且一台宕机,整个服务受影响。
- Mysql集群:PXC
- 负载均衡:Haproxy
- 高可用:Keepalived
搭建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
在监控见面中可以看到5个节点都是启动状态,当暂停/停止一个pxc节点时,监控界面的down节点状态(颜色)发生变化。由于haproxy会将请求发送至其它可用节点,此时pxc集群仍然可用。
// 终端执行命令,暂停一个节点
➜ haproxy docker pause mysql-node2
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
- 异常退出,如何恢复PXC集群
- 脑裂
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 最新版本,利用多线程复制的方式可以很大程度降低复制延迟;
参考资料
- Haproxy安装部署文档及多配置文件管理方案