主从复制验证
- 先使用 mysql root 用户创建一个库
create database music;
再创建一个 mysql 的普通用户,并且对 music 库有增改查的权限
CREATE USER 'vinci'@'%' IDENTIFIED BY 'DavVinci'; GRANT SELECT,UPDATE,INSERT,CREATE ON music.* TO 'vinci'@'%';
使用 vinci 用户登录到 172.72.0.116 的 mysql,进行建库建表
CREATE TABLE music.favorite( name char(10), music_name char(20) )charset=utf8;
插入几条数据
INSERT INTO music.favorite(name, music_name) VALUES ('Eason Chan','遥远的她'), ('Ronald','怪胎'), ('Hacken Lee','李香兰');
使用 vinci 用户登录到 172.72.0.137 的 mysql,查看数据是否存在
SELECT music_name FROM music.favorite WHERE name='Ronald' limit 5;
部署 haproxy
- 官网:http://www.haproxy.com
- HAProxy提供高可用性、负载均衡以及基于TCP和HTTP的应用代理,支持虚拟主机,它是免费、快速并且可靠的一种负载均衡解决方案。适合处理高负载站点的七层数据请求。类似的代理服务可以屏蔽内部真实服务器,防止内部服务器遭受攻击。
使用 yum 安装
yum install -y haproxy
创建 haproxy 配置文件
备份原来的配置文件
cp /etc/haproxy/haproxy.cfg{,.bak}
创建配置文件
cat << EOF > /etc/haproxy/haproxy.cfg global log 127.0.0.1 local3 info # 在本机记录日志 maxconn 65535 # 每个进程可用的最大连接数 chroot /data/haproxy # haproxy 安装目录 pidfile /data/haproxy/haproxy.pid # haproxy pid 文件目录 uid 994 # 运行 haproxy 的用户uid(id haproxy 命令可以查看) gid 991 # 运行 haproxy 的用户组id(id haproxy 命令可以查看) daemon # 以后台守护进程运行 defaults log global mode http # 运行模式 tcp、 http、 health retries 3 # 三次连接失败,则判断服务不可用 option redispatch # 如果后端有服务器宕机,强制切换到正常服务器 stats uri /haproxy # 统计页面 URL 路径 stats refresh 30s # 统计页面自动刷新时间 stats realm haproxy-status # 统计页面输入密码框提示信息 stats auth admin:YWJjMTIzCg== # 统计页面用户名和密码 stats hide-version # 隐藏统计页面上 HAProxy 版本信息 maxconn 65535 # 每个进程可用的最大连接数 timeout connect 5000 # 连接超时 timeout client 50000 # 客户端超时 timeout server 50000 # 服务器端超时 frontend http_haproxy mode http # 运行模式 tcp、 http、 health maxconn 65535 # 每个进程可用的最大连接数 bind :18080 # 监听 18080 端口 log global option httplog option httpclose # 每次请求完毕后主动关闭 http 通道 frontend mysqls # 自定义描述信息 mode tcp # 运行模式 tcp、 http、 health maxconn 65535 # 每个进程可用的最大连接数 bind *:13307 # 监听 13307 端口 default_backend mysql_backend backend mysql_backend mode tcp balance roundrobin # 设置负载均衡模式,source 保存 session 值,roundrobin 轮询模式 # rise 2是2次正确认为服务器可用 # fall 3是3次失败认为服务器不可用 server mysql01 172.72.0.116:13306 check inter 5s rise 2 fall 3 server mysql02 172.72.0.137:13306 check inter 5s rise 2 fall 3 EOF
给 haproxy 的 chroot 目录授权
mkdir /data/haproxy chown -R haproxy /data/haproxy
启动 haproxy
systemctl enable haproxy systemctl start haproxy
可以在浏览器输入 172.72.0.116:18080/haproxy
或者 172.72.0.137:18080/haproxy
来验证是否可以看到统计页面,这里的 ip,大家以自己实际的为准
验证 haproxy 登录到 mysql
haproxy 正确的情况下,两个地址都可以用 13307 端口来登录到 mysql 内
mysql -uvinci -pDavVinci -h172.72.0.116 -P13307 mysql -uvinci -pDavVinci -h172.72.0.137 -P13307
部署 keepalived
Keepalived 工作原理
- keepalived 是以 VRRP 协议为实现基础的,VRRP 全称 Virtual Router Redundancy Protocol ,即虚拟路由冗余协议。
虚拟路由冗余协议,可以认为是实现路由器高可用的协议。也就是说N台提供相同功能的路由器组成一个路由器组,这个组里面有一个 master 和多个 backup,master上面有一个对外提供服务的vip,master 不断向 backup 发送心跳信息,告诉 backup 自己还活着,当 backup 收不到心跳消息时就认为 master 已经宕机啦,这时就需要根据 VRRP 的优先级来选举一个 backup 当 master。从而保证高可用。
keepalived主要有三个模块,分别是 core、check 和 vrrp。
- core 模块为 keepalived 的核心,负责主进程的启动、维护、以及全局配置文件的加载和解析。
- check 负责健康检查,包括常见的各种检查方式。
- vrrp 模块是来实现 VRRP 协议的。
- vrrp 虚拟路由冗余协议
- 使用ip报文作为传输协议;
- 协议号112;
- 使用固定组播网络 224.0.0.18 进行发送;
- 只是用advertisement报文;
master路由器的选举:
- 比较每台路由器设备的优先级,优先级大的为 master。优先级相同的,比较接口IP地址,地址大的成为 master;
- master 会主动周期的发送 advertisement 报文,一秒发送一次;
- backup 如果 3 秒没有接到 master 发送的 advertisment 报文,就说明 master down;
抢占和非抢占模式
摘抄自:MySQL + Keepalived 双主热备高可用操作记录
keepalive 通过组播,单播等方式(自定义),实现 keepalive 主备推选。工作模式分为抢占和非抢占(通过参数 nopreempt 来控制)。
抢占模式
:- 主服务正常工作时,虚拟 IP 会在主上,备不提供服务,当主服务优先级低于备的时候,备会自动抢占虚拟 IP,这时,主不提供服务,备提供服务。
也就是说,工作在抢占模式下,不分主备,只管优先级。
不管 keepalived.conf 里的 state 配置成 master 还是 backup,只看谁的 priority 优先级高(一般而言,state 为 MASTER 的优先级要高于 BACKUP)。priority 优先级高的那一个在故障恢复后,会自动将 VIP 资源再次抢占回来!!
非抢占模式:
- 这种方式通过参数 nopreempt(一般设置在 advert_int 的那一行下面)来控制。不管 priority 优先级,只要 MASTER 机器发生故障,
- VIP 资源就会被切换到 BACKUP 上。
并且当 MASTER 机器恢复后,也不会去将 VIP 资源抢占回来,直至 BACKUP 机器发生故障时,才能自动切换回来。
千万注意
:
nopreempt 这个参数只能用于 state 为 backup 的情况,所以在配置的时候要把 master 和 backup 的 state 都设置成 backup,这样才会实现 keepalived 的非抢占模式!
也就是说:
- 当 state 状态一个为 master,一个为 backup 的时候,加不加 nopreempt 这个参数都是一样的效果。即都是根据 priority 优先级来决定谁抢占 vip 资源的,是抢占模式!
- 当 state 状态都设置成 backup,如果不配置 nopreempt 参数,那么也是看 priority 优先级决定谁抢占 vip 资源,即也是抢占模式。
当 state 状态都设置成 backup,如果配置 nopreempt 参数,那么就不会去考虑 priority 优先级了,是非抢占模式!即只有 vip 当前所在机器发生故障,另一台机器才能接管 vip。即使优先级高的那一台机器恢复 后也不会主动抢回 vip,只能等到对方发生故障,才会将 vip 切回来。
使用 yum 安装
yum install -y keepalived
在 172.72.0.116 创建 keepalived 配置文件
备份原来的配置文件
cp /etc/keepalived/keepalived.conf{,.bak}
创建配置文件,这里采用非抢占模式
cat << EOF > /etc/keepalived/keepalived.conf ! Configuration File for keepalived global_defs { # 每个 keepalived 要配置成不一样的 router_id mysql1 } # 作用:添加一个周期性执行的脚本。 ## 脚本的退出状态码会被调用它的所有的VRRP Instance记录。 ## 注意:至少有一个VRRP实例调用它并且优先级不能为0。 ## 优先级范围是1-254,用来做健康检查的,当检查失败时会将 vrrp_instance 的 priority 减少相应的值 vrrp_script chk_haproxy { # 监控服务脚本,脚本x执行权限; script "/data/script/chk_haproxy.sh" # 检测时间间隔(执行脚本间隔) interval 2 weight -2 # 解释 weight: ## 如果脚本执行成功(退出状态码为0),weight大于0,则priority增加。 ## 如果脚本执行失败(退出状态码为非0),weight小于0,则priority减少。 ## 其他情况下,priority不变。 } vrrp_instance VI_1 { state BACKUP # 指定虚拟 ip 的网卡接口,根据自己实际网卡名称修改 interface ens160 # 路由器标识,MASTER 和 BACKUP 必须是一致的 virtual_router_id 51 # 定义优先级,数字越大,优先级越高,在同一个 vrrp_instance 下,MASTER 的优先级必须大于 BACKUP 的优先级。这样 MASTER 故障恢复后,就可以将VIP资源再次抢回来 priority 100 advert_int 1 authentication { auth_type PASS auth_pass 1111 } virtual_ipaddress { 172.72.0.100 } # 添加此行表示非抢占模式 # nopreempt track_script { chk_haproxy } } EOF
在 172.72.0.137 创建 keepalived 配置文件
备份原来的配置文件
cp /etc/keepalived/keepalived.conf{,.bak}
创建配置文件,这里采用非抢占模式
cat << EOF > /etc/keepalived/keepalived.conf ! Configuration File for keepalived global_defs { # 每个 keepalived 要配置成不一样的 router_id mysql2 } # 作用:添加一个周期性执行的脚本。 ## 脚本的退出状态码会被调用它的所有的VRRP Instance记录。 ## 注意:至少有一个VRRP实例调用它并且优先级不能为0。 ## 优先级范围是1-254,用来做健康检查的,当检查失败时会将 vrrp_instance 的 priority 减少相应的值 vrrp_script chk_haproxy { # 监控服务脚本,脚本x执行权限; script "/data/script/chk_haproxy.sh" # 检测时间间隔(执行脚本间隔) interval 2 weight -2 # 解释 weight: ## 如果脚本执行成功(退出状态码为0),weight大于0,则priority增加。 ## 如果脚本执行失败(退出状态码为非0),weight小于0,则priority减少。 ## 其他情况下,priority不变。 } vrrp_instance VI_1 { state BACKUP # 指定虚拟 ip 的网卡接口,根据自己实际网卡名称修改 interface ens160 # 路由器标识,MASTER 和 BACKUP 必须是一致的 virtual_router_id 51 # 定义优先级,数字越大,优先级越高,在同一个 vrrp_instance 下,MASTER 的优先级必须大于 BACKUP 的优先级。这样 MASTER 故障恢复后,就可以将VIP资源再次抢回来 priority 100 advert_int 1 authentication { auth_type PASS auth_pass 1111 } virtual_ipaddress { 172.72.0.100 } # 添加此行表示非抢占模式 # nopreempt track_script { chk_haproxy } } EOF
配置 haproxy 健康检测脚本
- 所有 keepalived 节点都需要配置
- 因为非抢占模式,所以需要手动切换 VIP,这里脚本用停止 keepalived 服务来触发手动切换 VIP
- 脚本里面是探测 haproxy 的端口三次,如果返回状态码都是非0,就会触发停止 keepalived 服务
cat << EOF > /data/script/chk_haproxy.sh #!/bin/bash exitNum=0 while true do if ! nc -z 127.0.0.1 13307; then let exitNum++ [ ${exitNum} -lt 3 ] || systemctl stop keepalived fi done EOF
keepalived 的健康检测脚本必须要有执行权限
chmod +x /data/script/chk_haproxy.sh
启动 keepalived 服务
注意启动顺序,要先启动一个 keepalived 组件,并且有 VIP 了之后,再去启动剩下的 keepalived 组件,如果同时启动 keepalived 组件,那就都会有 VIP 地址了
systemctl enable keepalived systemctl start keepalived
验证 VIP 登录 mysql
能读取到数据,证明 keepalived 部署没有问题
mysql -uvinci -pDavVinci -h172.72.0.100 -P13307 -e "SELECT music_name FROM music.favorite WHERE name='Hacken Lee' limit 1;"
验证高可用是否有效
当一台 mysql 服务无进程
- 杀掉一个 mysql 的进程,模拟 mysql 出现故障,等 30秒后,看是否能写入 mysql 数据
- 杀掉一个 mysql 的进程后,可以从 haproxy 的统计页面看到显示为 down
cat /data/mysql/mysqld.pid | xargs kill -9 && \ sleep 30 && \ mysql -uvinci -pDavVinci -h172.72.0.100 -P13307 -e "INSERT INTO music.favorite(name, music_name) VALUES ('Beyond','冷雨夜');"
验证完成后,再次启动 mysql,在 haproxy 的统计页面看到显示为 up,并且进入 mysql 查看 SHOW SLAVE STATUS
确认 Slave_IO_Running
和 Slave_SQL_Running
都是 YES
验证 keepalived 是否为非抢占模式
存在 VIP 的节点,停止 keepalived 服务,模拟 keepalived 故障,看 VIP 是否会漂移到其他节点
systemctl stop keepalived
此时去其他节点,发现 VIP 已经漂移过来了,并且可以读取 mysql 数据
- 将停止的 keepalived 重新启动,看 VIP 是否会漂移回来
- 正常情况下,处于非抢占模式,VIP 是不会再次漂移的
systemctl start keepalived
当一台 haproxy 服务无进程,mysql 服务均正常
- 这里直接把有 VIP 节点的 haproxy 服务停止,模拟 haproxy 故障,并且触发 keepalived 的健康检测,进行 VIP 的漂移,同时验证是否能读取 mysql 数据
systemctl stop haproxy && \ sleep 10 && \ ip a | grep 172.72.0.100; \ mysql -uvinci -pDavVinci -h172.72.0.100 -P13307 -e "SELECT music_name FROM music.favorite WHERE name='Hacken Lee' limit 1;"
使用 tcpdump 命令观察地址的变化
tcpdump -i ens160 vrrp -nn
可以看到,VIP 从 172.72.0.116 机器切换到 172.72.0.137 机器上,并且也能正常读取 mysql 的数据
22:25:19.569090 IP 172.72.0.116 > 224.0.0.18: VRRPv2, Advertisement, vrid 51, prio 98, authtype simple, intvl 1s, length 20 22:25:20.569328 IP 172.72.0.116 > 224.0.0.18: VRRPv2, Advertisement, vrid 51, prio 98, authtype simple, intvl 1s, length 20 22:25:21.570001 IP 172.72.0.116 > 224.0.0.18: VRRPv2, Advertisement, vrid 51, prio 98, authtype simple, intvl 1s, length 20 22:25:21.578036 IP 172.72.0.116 > 224.0.0.18: VRRPv2, Advertisement, vrid 51, prio 0, authtype simple, intvl 1s, length 20 22:25:22.196273 IP 172.72.0.137 > 224.0.0.18: VRRPv2, Advertisement, vrid 51, prio 98, authtype simple, intvl 1s, length 20 22:25:23.196572 IP 172.72.0.137 > 224.0.0.18: VRRPv2, Advertisement, vrid 51, prio 98, authtype simple, intvl 1s, length 20 22:25:24.196795 IP 172.72.0.137 > 224.0.0.18: VRRPv2, Advertisement, vrid 51, prio 98, authtype simple, intvl 1s, length 20
基于二进制部署的 mysql 写了一个脚本来做启停和重启
#!/usr/bin/env bash baseDir="$(cd `dirname $0`;pwd)" envName="$1" envOpt="$2" pidFile="/data/mysql/mysqld.pid" MYSQL_HOME='/data/software/mysql-5.7.29-linux-glibc2.12-x86_64' function opt_usage () { echo "usage: bash $0 'start|stop|restart'" exit 1 } if [ $# -lt 1 ];then opt_usage exit 1 fi function start_mysql () { if [[ ! -f "${pidFile}" ]];then ${MYSQL_HOME}/bin/mysqld \ --defaults-file=/data/mysql/config/my.cnf --daemonize \ --plugin-dir=${MYSQL_HOME}/lib/plugin if [ $? -eq 0 ];then echo "mysql start success" fi else pidNum=$(ps -ef | grep -v grep | grep mysqld | awk '{print $2}') echo "pid: ${pidNum} is running for mysqld,please check it" exit 1 fi } function stop_mysql () { if [[ ! -f "${pidFile}" ]];then echo "pid file: ${pidFile} is not found" exit 1 else kill $(cat ${pidFile}) sleep 5 num=0 while true do ps -ef | grep -v grep | grep mysqld | awk '{print $2}' &> /dev/null if [ $? -eq 0 ];then echo "stop mysql success" break else echo "wating for mysql shutdown......" let num++ sleep 1 fi if [ ${num} -eq 3 ];then echo "stop mysql failed,please check it" exit 1 fi done fi } function status_mysql () { if [ -f "${pidFile}" ];then pidNum="$(ps -ef | grep -v grep | grep mysqld | awk '{print $2}')" if [ ${pidNum} -eq $(cat ${pidFile}) ];then echo "mysql is running" echo "mysql version is: $(mysql -V)" echo " mysql pid is: ${pidNum}" fi else echo "mysql is not running" fi } case $1 in 'start') start_mysql ;; 'stop') stop_mysql ;; 'restart') stop_mysql start_mysql ;; 'status') status_mysql ;; '*') opt_usage exit 1 esac