ProxySQL高可用方案

ACMUG征集原创技术文章。详情请添加 A_CMUG或者扫描文末二维码关注我们的。有奖征稿,请发送稿件至:[email protected]。3306现金有奖征稿说明:知识无价,劳动有偿,ACMUG特约撰稿人有奖回报计划(修订版)

作者简介:周晓

13年开始TP-LINK做了2年Oracle DBA和系统运维工作。目前在六度人和科技任MySQL DBA,先后两次主导数据库整体迁移,帮助开发优化了大量SQL、提供结构设计建议,并组织培训。工作平时在工作中遇到的一些问题和处理经验,有空会写写放在个人博客上,也会在里面分享一些Python写的工具。

MySQL的高可用方案现在如 MHA, Galera, InnoDB Cluster,一旦在上游使用中间件之后,中间件本身可能成为单点。所以本文要介绍的是对于ProxySQL自身高可用的方案对比。首先ProxySQL自身是通过Angel进程的形式运行,即proxysql如果有崩溃,主进程会自动拉起来。但如果是无响应或者网络故障,则需要另外的机制去做到服务的高可用。本文总结了四种方法。

ProxySQL有关介绍,请参考: /2017/04/10/mysql-proxysql-install-config/

1. 与应用一起部署

所有部署应用的地方,都会部署proxysql节点,当这个proxysql挂掉之后,只影响本机的应用。而且不需要多经过一层网络。但带来的问题是,如果应用节点很多,proxy的数量也会增加:

会导致proxysql的配置不容易管理

proxysql对后端db健康检查的请求成倍增加

限制每个用户或后端db的 max_connections 特性用不了

2. 集中式部署,多ip引用

后端一个db集群,对应中间两个以上的 proxysql 节点,前端应用配置多个ip地址,随机挑选一个使用,完全无状态。仅需要多经过一次网络代理。这种方式的好处是,不需要再对数据库这种基础服务,多引入一个软件来实现高可用(如下节的keepalive或consul),由应用端获取数据库连接的代码逻辑处理。

但是因为proxysql访问地址是写在配置文件里面的,如果一个节点挂掉,随机挑选还是会落地这个失败的节点。所以优化方案是,ip列表里面默认取某一个,失败之后再选取下一个重试。

示例代码:

python proxysql_addr_list = [192.168.1.175, 192.168.1.176, 192.168.1.177] proxysql_addr_list_len = 3 hostname = this_hostname_for_hash_loadbalance def get_dbconnection():    list_index = hash(hostname) % proxysql_addr_list_len    dbconn = None    try:        dbconn = DBConnect(dbhost=proxysql_addr_list[ list_index ], dbport=3306)  # timeout 1000ms    except:        if (list_index + 1) == proxysql_addr_list_len:            list_index = -1  # like Circular Array        dbconn = DBConnec(dbhost=proxysql_addr_list[ list_index + 1 ], dbport=3306)  # if failed again, through exception    return dbconn

上述并不完美,比如可以改用环形数组轮巡,允许重试其它更多的ip。

能不能不进行多IP引用呢?

为了避免后端引用IP的单点,可以将上面第1种和这里的第2中结合起来,改进的部署方案:(见文后参考链接)即在原来的基础上,App上的proxysql后端,挂的还是ProxySQL集群。

我个人没有验证这样的方案,如果要用需要充分验证proxysql互连的时候,有没有bug。

3. 经典 keepalived

引入keepalived,通过VIP访问两个以上的proxysql节点,既可以减少一次网络传输,又可以实现proxysql自身的高可用,而且甚至不用关心脑裂的问题,因为proxysql配置完全一样,是无状态的,脑裂了也无妨。你也可能意识到,这种方式一次只能一个proxysql提供服务,另一个proxysql节点始终处于备用状态。如果配合LVS或haproxy做负载均衡,部署架构又会多出一层网络请求,而且越发复杂(VIP不在proxysql节点上漂,而是在两个lvs之间)。使用keepalived的前提是,局域网允许发组播包。这在阿里云ECS经典网络下是不允许的,如果有其它类似方式,如SLB也是可行的。目前测试环境采用是 haproxy + keepalived 的方式。

4. Consul服务发现

如果上面的方式都不适用,那么可以进一步考虑使用第三方的服务发现组件。

4.1 介绍

Consul用于实现分布式系统的服务发现与配置,我们将所有proxysql节点注册到consul上作为一个服务来提供,由 Consul agent Server 来判断proxysql节点的存活,每个应用节点上都安装 Consul agent Client 来供应用获取可用地址。这样部署架构的好处是:

不需要多一层负载均衡,多一层网络链路

不需要部署大量的proxysql节点

App或者ProxySQL节点上的Consul故障,不影响其它节点。Consul Server集群的天生具备高可用

ProxySQL故障会被Consul检查到,踢除故障节点,并通知给所有consul agent

可以利用Consul的DNS接口实现简单的负载均衡

其实consul所做的与本文第2节的类似:自动提出不可用的节点,只是一个是被动、手动,一个是主动、自动。下面简单演示一下。

4.2 部署示例

Consul Server节点的安装在此略过,网上有不少文章,直接进入到在ProxySQL节点安装配置Consul。

Consul agent:

apps-1: 10.0.201.168

apps-2: 10.0.201.220

apps-3: 10.0.201.156

ProxySQL node:每个节点上运行了两个proxysql进程 7033: crm0, 7133: crm1

proxysql-1 : 192.168.1.170

proxysql-2 : 192.168.1.171

配置 proxysql 节点上的Consul

配置 proxysql-1 节点上的Consul:

$ sudo vim /etc/consul.d/config.json {    "data_dir": "/opt/consul",    "datacenter": "Office_test",    "log_level": "INFO",    "node_name": "proxysql-1",    "retry_join": [        "10.0.201.168",        "10.0.201.220",        "10.0.201.156"    ],    "telemetry": {        "statsd_address": "10.0.201.34:8125",        "statsite_prefix": "proxysql-1"    },    "dns_config": {        "only_passing": true    }    "acl_datacenter": "Office_test",    "acl_default_policy": "deny",    "encrypt": "XXXXxxxx==" } $ sudo vi /etc/consul.d/proxysql.json {"services": [    {        "id": "proxysql_crm0",        "name": "proxysql",        "address": "192.168.1.170",        "port": 7133,        "tags": ["test", "crm0"],        "check": {            "interval": "5s",            "tcp": "192.168.1.170:7033",            "timeout": "1s"        },        "enableTagOverride": false,        "token": "xxxxxxxx-xxxx"    },    {        "id": "proxysql_crm1",        "address": "192.168.1.170",        "name": "proxysql",        "port": 7133,        "tags": ["test", "crm1"],        "check": {            "interval": "5s",            "tcp": "192.168.1.170:7133",            "timeout": "1s"        },        "enableTagOverride": false,        "token": "xxxxxxxx-xxxx"    } ]}

启动consul

sudo consul agent -config-dir /etc/consul &

查看日志

proxysql-2 节点上的Consul配置根据上面的内容去改。

4.4 使用方式

先来看下效果:

[root@proxysql-1 ~]# dig @127.0.0.1 -p 8600 crm0.proxysql.service.consul SRV ; <<>> DiG 9.8.2rc1-RedHat-9.8.2-0.62.rc1.el6_9.2 <<>> @127.0.0.1 -p 8600 crm0.proxysql.service.consul SRV ; (1 server found) ;; global options: +cmd ;; Got answer: ;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 65293 ;; flags: qr aa rd; QUERY: 1, ANSWER: 2, AUTHORITY: 0, ADDITIONAL: 2 ;; WARNING: recursion requested but not available ;; QUESTION SECTION: ;crm0.proxysql.service.consul.    IN    SRV ;; ANSWER SECTION: crm0.proxysql.service.consul. 0    IN    SRV    1 1 7033 proxysql-1.node.office_test.consul. crm0.proxysql.service.consul. 0    IN    SRV    1 1 7033 proxysql-2.node.office_test.consul. ;; ADDITIONAL SECTION: proxysql-1.node.office_test.consul. 0 IN A    192.168.1.170 proxysql-2.node.office_test.consul. 0 IN A    192.168.1.171 ;; Query time: 3 msec ;; SERVER: 127.0.0.1#8600(127.0.0.1) ;; WHEN: Fri May 26 17:01:38 2017 ;; MSG SIZE  rcvd: 157

看到域名 crm0.proxysql.service.consul 解析出来有两个可用地址 192.168.1.170,192.168.1.171,SRV记录还带出了端口信息(其实这里的端口对每个proxysql是固定/已知的,所以可不用SRV记录搜索)。

在应用端想要连接proxysql使用的方式大致有3种:

DNS接口

需要将各自开发语言的DNS解析库嵌入到项目,指定 127.0.0.1:8600 为dns地址来解析上面的 crm0.proxysql.service.consul 域名。这种方式会增加开发的复杂度。

另一种方式是将应用服务器的默认DNS Server配置成本地consul的内置dns地址,并且consul设置 recursors 选项,来处理解析 consul. 以外的域名。这样对运维改动比较大。

"recursors": [    "10.143.22.116",    "10.143.22.118",    "114.114.114.114" ]

HTTP接口

通过 API 的方式获取services信息:

$ curl -X GET :8500/v1/health/service/proxysql?tag=crm1&passing=true

可以获取到 crm1 库的proxysql所有健康的 Node 和 Service 信息,然后任取一个使用。

但并不需要每一次访问proxysql都需要请求api,可以定时(如每隔10s)去请求,缓冲在本地或者变量里;在处理数据库连接的时候发现连接ProxySQL错误,则再主动触发一次向Consul请求新的地址,再重连。

需要考虑的是访问API的地址如果是IP,往往也是单点。另者,java这类jvm语言修改配置后往往需要重启,也不简单。

consul-template直接生成数据库连接的配置文件

consul-template通过事先定义好的模板,根据发现服务的健康状态,生成最新可用的配置文件,然后下发。

如果大但的想一下,各个服务或者语言的配置文件并不相同,直接生成一份 hosts 文件是最简单的,然后通过配置管理工具统一下发应用。也不需要关心是否需要重启应用。

consul watch

Consul watch 功能,可以检测到service变化之后,主动调用一个脚本,脚本可以去更新数据库配置信息,根据需要决定是否重启,后者生成hosts。与consul-template思想是相同的。

因为 watch 是通过阻塞式HTTP长连接请求的方式,实时获取到service的监控状态,所以有问题时反馈还比较及时。

"watches":[{  "type": "service",  "service": "proxysql",  "tag": "crm0",  "passingonly": true,  "handler": "sh /tmp/consul_watch_test.sh" }]

/tmp/consul_watch_test.sh 脚本里或者python,可以做一些更新数据库配置文件、发送邮件等工作。

参考

https://www.percona.com/blog/2016/09/16/consul-proxysql-mysql-ha/

https://www.percona.com/blog/2017/01/19/setup-proxysql-for-high-availability-not-single-point-failure/

https://www.slideshare.net/DerekDowney/proxysql-tutorial-plam-2016 (本文部分图片出自该PPT)

注:ACMUG收录技术文章版权属于原作者本人所有。如有疑问,请联系作者。看完转发,手留余香。关注我们,一起进步。

关注ACMUG,参与社区活动,交流开源技术,分享学习心得,一起共同进步。