使用HAProxy实现MySQL负载均衡

在CentOS7中部署HAProxy实践中介绍了如何安装HAProxy,本文则结合HAProxy和MySQL来实现一个简单的负载均衡机群。注意:

MySQL必须是Master-Master replication,这样才能确保数据库都能够写入。或者数据库都是slave模式并且限制外表访问只能读不能写,以实现只读模式负载均衡。

准备MySQL数据库

首先创建两个用户用于HAProxy

  • 第一个用户账号将用于HAProxy检查服务器状态

mysql -u root -p -e "grant usage on mysql.user to haproxy_check@'172.17.0.1' identified by 'password'; FLUSH PRIVILEGES;"
  • 创建一个MySQL用户从HAproxy访问MySQL机群,具有root权限。默认的root权限是只本地登陆,这里通过授权远程root访问,并且使用独立的root授权

mysql -u root -p -e "GRANT ALL PRIVILEGES ON *.* TO 'haproxy_root'@'172.17.0.1' IDENTIFIED BY 'password' WITH GRANT OPTION; FLUSH PRIVILEGES"

这里请使用实际的账号密码

这里172.17.0.1是HAProxy服务所在的服务器IP地址

在HAproxy服务器上安装MySQL客户端

  • HAProxy使用mysql客户端来侦测连接,所以需要安装客户端

sudo dnf install mariadb

CentOS 7安装mariadb来替代mysql - 如果是CentOS 6及以下版本使用yum安装mysql-client软件包

  • 在HAproxy服务器上测试连接MySQL数据库,正常情况应该输出mysql数据库信息

mysql -h 172.17.0.5 -u haproxy_root -p -e "SHOW DATABASES"

安装和配置HAProxy

  • 安装HAProxy软件包

sudo dnf install haproxy
  • 备份初始HAProxy配置文件

sudo mv /etc/haproxy/haproxy.cfg{,.original}
  • 编辑新配置文件

sudo vi /etc/haproxy/haproxy.cfg

首先是全局和默认配置部分:

global
    log 127.0.0.1 local0 notice
    user haproxy
    group haproxy

defaults
    log global
    retries 2
    timeout connect 3000
    timeout server 5000
    timeout client 5000

这里设置log 127.0.0.1 local0 notice表示HAProxy将日志发送127.0.0.1本地的rsyslog

然后是主要配置部分:

listen mysql-cluster
    bind 0.0.0.0:3306
    mode tcp
    option mysql-check user haproxy_check
    balance roundrobin
    server mysql-1 172.17.0.5:3306 check
    server mysql-2 172.17.0.6:3306 check

这个配置使得HAproxy监听在所有接口的3306端口上提供对外服务。此外,配置了服务器列表,通过balance roundrobin负载均衡方式转发到后端的2台MySQL服务器

注意:和HTTP负载均衡不同,HAProxy没有特定的MySQL模式,所以使用tcp

HAProxy支持多种检测,默认是TCP检测,但是,这个检测方式对于数据库是不充分的。HAProxy使用MySQL Handshake Initialisation数据包和错误包来判断MySQL会话是否正常,所以对于数据库只需要设置Host表授权就可以了,不需要密码。

USE mysql;
INSERT INTO user (Host,User) values ('<ip_of_haproxy>','<username>');
FLUSH PRIVILEGES;

最后配置负载均衡的状态信息输出,也就是在HAProxy服务器上,提供一个对外8080端口访问提供统计页面 http://:8080/ :

listen status
    bind 0.0.0.0:8080
    mode http
    stats enable
    stats uri /
    stats realm Strictly\ Private
    stats auth user1:User1_Password
    stats auth user2:User2_Password
  • 启动haproxy

sudo systemctl start haproxy
  • 设置HAProxy负载均衡host主机的防火墙

注意CentOS 7默认开启了firewall防火墙,需要允许访问端口设置:

先检查有那些dmz区

$ firewall-cmd --get-active-zones
FedoraServer
  interfaces: wlp3s0

然后针对dmz区对外开放访问

sudo firewall-cmd --zone=FedoraServer --add-port=3306/tcp --permanent
sudo firewall-cmd --zone=FedoraServer --add-port=8080/tcp --permanent
sudo firewall-cmd --reload

问题排查

在HAProxy服务器上无法查询数据库表(虽然前面直接连接服务器可以访问到3306端口远程查询)

$ mysql -h 127.0.0.1 -u haproxy_root -p -e "SHOW DATABASES"
Enter password: 
ERROR 2013 (HY000): Lost connection to MySQL server at 'reading initial communication packet', system error: 0 "Internal error/check (Not system error)"

此外,telnet <HAProxy>:3306 显示网络连接立即被断开:Connection closed by foreign host.

补充说明:这个测试环境是在Docker中部署,mysql/mariadb服务器是在Docker容器中运行,而HAProxy是在host主机上运行。

检查Docker容器中运行的mysql/mariadb服务日志,发现是因为HAProxy是通过172.17.0.1的客户端来访问但是不使用密码(晕倒),所以导致了服务器拒绝了连接:

2017-11-07T09:44:39.636704Z 3004 [Note] Access denied for user 'haproxy_check'@'172.17.0.1' (using password: NO)
2017-11-07T09:44:41.638068Z 3005 [Note] Access denied for user 'haproxy_check'@'172.17.0.1' (using password: NO)

当前MySQL创建账号时候设置了密码,所以导致无法访问。临时的解决方法是,去除掉haproxy_check从特定IP访问的时候的密码限制,但是这不是一个解决方法。(真正的解决是要改正HAProxy的mysql检测脚本,添加上数据库访问账号。待以后探索)

  • 临时取消密码访问

uninstall plugin validate_password;
SET PASSWORD FOR 'haproxy_check'@'172.17.0.1' = PASSWORD('');

警告:这是一个非常严重的安全隐患,仅在可靠的安全措施下才可以在测试环境使用,务必尽快恢复原有安全设置。

以上配置修订之后,MySQL服务器上不再出现'haproxy_check'@'172.17.0.1'连接报错,此时从 http://:8080 访问,可以看到后端mysql-1服务器状态恢复正常,并且也可以在自己的开发桌面电脑上远程mysql访问Docker容器中运行的MySQL数据库。

参考

Last updated