rhel 8.7 部署 keepalived+haproxy 实现 mysql 双主高可用场景 2

本文涉及的产品
RDS Agent(兼容OpenClaw),2核4GB
RDS DuckDB + QuickBI 企业套餐,8核32GB + QuickBI 专业版
RDS MySQL DuckDB 分析主实例,基础系列 4核8GB
简介: rhel 8.7 部署 keepalived+haproxy 实现 mysql 双主高可用场景

主从复制验证

  • 先使用 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_RunningSlave_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


相关实践学习
每个IT人都想学的“Web应用上云经典架构”实战
本实验从Web应用上云这个最基本的、最普遍的需求出发,帮助IT从业者们通过“阿里云Web应用上云解决方案”,了解一个企业级Web应用上云的常见架构,了解如何构建一个高可用、可扩展的企业级应用架构。
MySQL数据库入门学习
本课程通过最流行的开源数据库MySQL带你了解数据库的世界。 &nbsp; 相关的阿里云产品:云数据库RDS MySQL 版 阿里云关系型数据库RDS(Relational Database Service)是一种稳定可靠、可弹性伸缩的在线数据库服务,提供容灾、备份、恢复、迁移等方面的全套解决方案,彻底解决数据库运维的烦恼。 了解产品详情:&nbsp;https://www.aliyun.com/product/rds/mysql&nbsp;
目录
相关文章
|
9月前
|
运维 监控 关系型数据库
MySQL高可用方案:MHA与Galera Cluster对比
本文深入对比了MySQL高可用方案MHA与Galera Cluster的架构原理及适用场景。MHA适用于读写分离、集中写入的场景,具备高效写性能与简单运维优势;而Galera Cluster提供强一致性与多主写入能力,适合对数据一致性要求严格的业务。通过架构对比、性能分析及运维复杂度评估,帮助读者根据自身业务需求选择最合适的高可用方案。
|
9月前
|
SQL 监控 关系型数据库
MySQL主从复制:构建高可用架构
本文深入解析MySQL主从复制原理与实战配置,涵盖复制架构、监控管理、高可用设计及性能优化,助你构建企业级数据库高可用方案。
|
8月前
|
存储 关系型数据库 MySQL
MySQL Docker 容器化部署全指南
MySQL是一款开源关系型数据库,广泛用于Web及企业应用。Docker容器化部署可解决环境不一致、依赖冲突问题,实现高效、隔离、轻量的MySQL服务运行,支持数据持久化与快速迁移,适用于开发、测试及生产环境。
1118 4
|
8月前
|
Oracle 关系型数据库 MySQL
MySQL包安装 -- RHEL系列(离线RPM包安装MySQL)
本文详细介绍在Rocky、CentOS、AlmaLinux、openEuler等主流Linux系统上,通过离线RPM包安装MySQL 8.0和8.4版本的完整步骤,涵盖下载、依赖处理、rpm/yum安装、服务启动、密码设置等关键环节,适用于多种企业级环境部署需求。
2600 0
MySQL包安装 -- RHEL系列(离线RPM包安装MySQL)
|
8月前
|
存储 关系型数据库 MySQL
MySQL介绍和MySQL包安装 -- RHEL系列(Yum资源库安装MySQL)
MySQL是一款开源关系型数据库,高性能、易用、跨平台,支持多种存储引擎,广泛应用于Web开发、企业级应用等领域。本教程介绍其特点、架构及在主流Linux系统中的安装配置方法。
1374 0
MySQL介绍和MySQL包安装 -- RHEL系列(Yum资源库安装MySQL)
|
12月前
|
关系型数据库 MySQL 分布式数据库
Super MySQL|揭秘PolarDB全异步执行架构,高并发场景性能利器
阿里云瑶池旗下的云原生数据库PolarDB MySQL版设计了基于协程的全异步执行架构,实现鉴权、事务提交、锁等待等核心逻辑的异步化执行,这是业界首个真正意义上实现全异步执行架构的MySQL数据库产品,显著提升了PolarDB MySQL的高并发处理能力,其中通用写入性能提升超过70%,长尾延迟降低60%以上。
|
Java 关系型数据库 MySQL
在Linux平台上进行JDK、Tomcat、MySQL的安装并部署后端项目
现在,你可以通过访问http://Your_IP:Tomcat_Port/Your_Project访问你的项目了。如果一切顺利,你将看到那绚烂的胜利之光照耀在你的项目之上!
586 41
|
10月前
|
关系型数据库 MySQL 数据库
为什么 MySQL 不推荐用 Docker 部署?
本文探讨了MySQL是否适合容器化的问题,分析了Docker容器在数据安全、性能瓶颈、状态管理及资源隔离等方面的挑战,并指出目前主流分布式数据库如TDSQL和OceanBase仍倾向于部署在物理机或KVM上。
461 0
|
开发框架 Java 关系型数据库
在Linux系统中安装JDK、Tomcat、MySQL以及部署J2EE后端接口
校验时,浏览器输入:http://[your_server_IP]:8080/myapp。如果你看到你的应用的欢迎页面,恭喜你,一切都已就绪。
815 17
|
存储 关系型数据库 MySQL
美团面试:MySQL为什么 不用 Docker部署?
45岁老架构师尼恩在读者交流群中分享了关于“MySQL为什么不推荐使用Docker部署”的深入分析。通过系统化的梳理,尼恩帮助读者理解为何大型MySQL数据库通常不使用Docker部署,主要涉及性能、管理复杂度和稳定性等方面的考量。文章详细解释了有状态容器的特点、Docker的资源隔离问题以及磁盘IO性能损耗,并提供了小型MySQL使用Docker的最佳实践。此外,尼恩还介绍了Share Nothing架构的优势及其应用场景,强调了配置管理和数据持久化的挑战。最后,尼恩建议读者参考《尼恩Java面试宝典PDF》以提升技术能力,更好地应对面试中的难题。

推荐镜像

更多