使用TCP负载均衡和Galera Cluster扩展MySQL

NGINX | 2021年7月26日

[编者:本文最初发布于2016年,现已更新,以使用此后经过修订的NGINX功能。有关详细信息,请参阅使用NGINX JavaScript模块实现高级日志记录和NGINX Plus仪表板。]

我们在NGINX Plus R5中引入了TCP负载均衡,并在后续版本中持续增加相关功能,同时支持UDP负载均衡。本文将探讨TCP负载均衡的关键要求,以及NGINX Plus如何满足这些要求。

为了探索NGINX Plus的功能,我们将使用一个简单的测试环境,其中包含采用可扩展数据库后端的应用程序的关键组件。有关构建测试环境的完整说明,请参阅“附录”。

用于对MySQL服务器进行负载均衡的测试环境将NGINX Plus部署在MySQL客户端与Galera集群之间
MySQL节点负载均衡测试环境

在此环境中,NGINX Plus充当数据库服务器的反向代理,侦听默认的MySQL端口3306。这为客户端提供了简单的接口,同时可以横向扩展后端MySQL节点(甚至使其离线),而不会对客户端造成任何影响。我们使用MySQL命令行工具作为客户端,用于代表测试环境中的前端应用。

本文介绍的许多功能同时适用于NGINX Open Source和NGINX Plus。为简洁起见,下文将统一使用NGINX Plus,并明确指出NGINX Open Source不支持的功能。

我们将探讨以下用例:

TCP负载均衡

在为任何应用配置负载均衡之前,务必了解该应用如何连接数据库。在大多数测试中,我们使用MySQL命令行工具mysql(1)连接Galera集群,运行查询,然后关闭连接。不过,许多应用框架会使用连接池来尽可能降低延迟,并高效利用数据库服务器资源。

TCP负载均衡在stream配置上下文中进行配置,因此,我们在主stream文件中添加一个nginx.conf块,以创建基本的MySQL负载均衡配置。

正在加载代码示例…

这样可将TCP负载均衡配置与主配置文件分开。然后,在与stream.conf相同的目录中创建nginx.conf。请注意,默认情况下,conf.d目录专用于http配置上下文,因此无法将stream配置文件添加到该目录。

正在加载代码示例…

首先,定义一个名为upstream的组galera_cluster,其中包含Galera集群中的3个MySQL节点。在测试环境中,每个节点都可通过localhost上的唯一端口号访问。zone指令定义一块由所有NGINX Plus工作进程共享的内存,用于维护负载均衡状态。server{}块用于配置NGINX Plus处理客户端连接的方式。NGINX Plus侦听MySQL默认端口3306,并将所有流量转发到upstream块中定义的Galera集群。

要测试此基本配置是否正常工作,可以使用MySQL客户端返回所连接的Galera集群节点的主机名。

$ echo "SHOW VARIABLES WHERE Variable_name = 'hostname'" | mysql --protocol=tcp --user=nginx --password=plus -N 2> /dev/nullhostname	node1

要检查负载均衡是否正常工作,可以重复运行同一命令。

$ !!;!!;!!hostname	node2
hostname	node3
hostname	node1

这表明默认的轮询负载均衡算法运行正常。但是,如果应用程序使用连接池访问数据库(如上文所述),以轮询方式与集群建立连接可能会导致各节点的连接数不均衡。此外,连接不能等同于特定工作负载,因为连接可能处于空闲状态(等待应用程序发出查询),也可能正忙于处理查询。对于长连接TCP连接,更合适的负载均衡算法是最少连接数,可通过least_conn指令进行配置:

正在加载代码示例…

现在,当客户端与数据库建立新连接时,NGINX Plus会选择当前连接数最少的集群节点。

高可用性和健康检查

在集群中分担数据库工作负载的一大优势是,还可实现高可用性。采用上述配置时,如果新的TCP连接无法建立,NGINX Plus会将相应服务器标记为“已停机”,并停止向其发送TCP数据包。

除了以这种方式处理停机服务器外,还可以将NGINX Plus配置为执行自动主动健康检查,以便在向不可用服务器发送客户端请求之前将其检测出来(此功能仅限NGINX Plus)。此外,还可以通过应用层健康检查来测试服务器的可用性,即向每台服务器发送请求,并检查收到的响应是否表明服务器运行正常。配置扩展如下。

正在加载代码示例…

在此示例中,match块定义了发起MySQL协议版本10握手所需的请求和响应数据。health_check指令在server{}块中应用此模式,确保NGINX Plus仅将MySQL连接转发到确实能够接受新连接的服务器。在本例中,系统每20秒执行一次健康检查;服务器一旦检查失败,便会从TCP负载均衡池中排除;连续2次健康检查成功后,再恢复向该服务器分配负载。

日志记录和诊断

NGINX Plus提供灵活的日志记录功能,可记录所有TCP/UDP处理活动,以便进行调试或离线分析。对于MySQL等TCP协议,NGINX Plus会在连接关闭时写入一条日志。log_format指令用于定义日志中显示的值。可以从Stream模块可用的任意变量中进行选择。日志格式在stream上下文中定义,位于stream.conf文件的顶部。

正在加载代码示例…

要启用日志记录,请在access_logserver{}块中添加指令,并指定日志文件的路径以及上一代码片段中定义的日志格式名称。

正在加载代码示例…

这会生成如下示例所示的日志条目。

$ tail -3 /var/log/nginx/galera_access.log192.168.91.1 [23/Jul/2021:17:42:18 +0100] TCP 200 369 1611 127.0.0.1:33063 0.000 0.003 12.614 12.614
192.168.91.1 [23/Jul/2021:17:42:18 +0100] TCP 200 369 8337 127.0.0.1:33061 0.001 0.001 11.181 11.181
192.168.91.1 [23/Jul/2021:17:42:19 +0100] TCP 200 369 1611 127.0.0.1:33062 0.001 0.001 10.460 10.460

使用NGINX JavaScript模块实现高级日志记录

NGINX JavaScript是“NGINX原生”编程式配置语言。它是专为NGINX和NGINX Plus打造的独特JavaScript实现,适用于服务器端用例和逐请求处理。

[编辑注:以下用例只是NGINX JavaScript模块众多用例之一。有关所有用例的列表,请参阅《NGINX JavaScript模块用例》。

本文已更新,改为使用在s中引入的Stream模块重构版会话(NGINX JavaScript 0.2.4)对象,以及在js_import中引入的NGINX JavaScript 0.4.0指令。]

在用于TCP/UDP负载均衡的Stream模块中,NGINX JavaScript可以访问请求和响应数据包的内容。这意味着我们可以检查与SQL查询对应的客户端请求,并提取SQL方法等有用元素,例如SELECT或UPDATE。随后,NGINX JavaScript可以将这些值作为常规NGINX变量提供。在本例中,我们将JavaScript代码放在/etc/nginx/sql_method.js中。

正在加载代码示例…

向getSqlMethod()函数传入一个JavaScript对象(s),该对象表示当前数据包。此对象的属性(例如fromUpstream和buffer)可提供所需的数据包及其上下文信息。

首先检查TCP数据包是否来自客户端,因为无需检查来自上游MySQL服务器的数据包。这里需要关注客户端发送的第3个数据包,因为前两个数据包包含握手和身份验证信息。客户端发送的第3个数据包包含SQL查询。然后,将此字符串的开头与methods数组中定义的某个SQL方法进行比较。找到匹配项后,将结果存储在全局变量$method中,并向错误日志写入一条记录。NGINX JavaScript日志以info严重级别写入错误日志,因此默认不会显示。

当对同名的NGINX变量求值时,将调用setSqlMethod()函数。此时,该变量的值由NGINX JavaScript全局变量$method填充,而该全局变量的值通过调用getSqlMethod()函数获得。

请注意,此NGINX JavaScript代码专为执行单个查询的MySQL命令行客户端而设计。它无法准确捕获复杂查询,也无法准确捕获通过长连接执行的多个查询,但可以调整代码以适应这些用例。有关安装和启用NGINX JavaScript模块的说明,请参阅《NGINX Plus管理指南》。

要在日志中包含SQL方法,请在“$sql_method”指令中加入“log_format”变量。

正在加载代码示例…

还需要扩展配置,指定NGINX Plus执行NGINX JavaScript代码的方式和时机。

正在加载代码示例…

首先,使用js_import指令指定NGINX JavaScript代码的位置,并使用js_set指令,让NGINX Plus在需要计算setSqlMethod()变量时调用$sql_method函数。然后,在server{}块中使用js_filter指令,指定每次处理数据包时调用的函数。还可以选择添加error_log指令并设置info选项,以启用NGINX JavaScript日志记录。

完成这些额外配置后,访问日志如下所示。

$ tail -3 /var/log/nginx/galera_access.log192.168.91.1 [23/Jul/2021:17:42:18 +0100] TCP 200 369 1611 127.0.0.1:33063 0.000 0.003 12.614 12.614 UPDATE
192.168.91.1 [23/Jul/2021:17:42:18 +0100] TCP 200 369 8337 127.0.0.1:33061 0.001 0.001 11.181 11.181 SELECT
192.168.91.1 [23/Jul/2021:17:42:19 +0100] TCP 200 369 1611 127.0.0.1:33062 0.001 0.001 10.460 10.460 UPDATE

NGINX Plus仪表板

[编辑注:本节已更新为介绍NGINX Plus API。该API取代了原文中单独介绍的扩展Status模块,后者现已弃用。]

除了详细记录MySQL活动外,还可以在NGINX Plus实时活动监控仪表板上查看实时指标和上游MySQL服务器的运行状况(NGINX Open Source仅通过Stub Status API提供较少的指标)。

NGINX Plus仪表板在NGINX Plus R7中引入,为NGINX Plus API提供Web界面。要启用此功能,请在单独的server{}文件中,为http上下文添加一个新的/etc/nginx/conf.d/dashboard.conf块:

正在加载代码示例…

还必须在server{}块(位于stream.conf)中添加status_zone指令,以便收集MySQL服务的监控数据。

正在加载代码示例…

完成此配置后,可通过端口8080访问NGINX Plus仪表板。在以下屏幕截图中,可以看到3台MySQL服务器,每台服务器都显示了大量当前连接的详细信息和当前运行状况。可以看到,监听端口33062的节点此前曾短暂中断18.97秒(显示在“DT”列中)。

使用NGINX Plus实时活动监控仪表板,可在对MySQL节点进行负载均衡时跟踪服务器的运行状况
NGINX Plus实时活动监控仪表板支持跟踪MySQL服务器的运行状况

并发写入注意事项

Galera Cluster将每个MySQL服务器节点作为主数据库提供读写服务。对于许多应用,读取量远高于写入量。与多主数据库集群带来的灵活性相比,同一表行被多个客户端同时更新的风险完全可以接受。如果发生并发写入的风险较高,则有两种选择。

  1. 创建两个独立的上游组,一个用于读取,另一个用于写入,并分别侦听不同的端口。指定集群中的一个或多个节点处理写入,同时将所有节点纳入读取组。必须更新客户端代码,以便为读取和写入操作选择相应的端口。我们的博客文章《使用NGINX Plus实现高级MySQL负载均衡》介绍了这种方法。该方法更适合包含大量MySQL服务器节点的大规模环境。
  2. 保留单个上游组,并修改客户端代码以检测写入错误。检测到写入错误后,代码会进行指数退避,待并发操作结束后再重试。我们的博客文章使用NGINX Plus和Galera Cluster实现MySQL高可用性介绍了这种方法。该方法更适合小型集群,因为在小型集群中指定专用节点执行写入操作会影响高可用性。

总结

本文探讨了对MySQL等TCP(或UDP)应用进行负载均衡时的几个关键方面。NGINX Plus提供功能完备的TCP/UDP负载均衡器,无论流量类型如何,都能帮助您交付兼具性能、可靠性、安全性和可扩展性的应用。

要试用NGINX Plus,请立即开始30天免费试用,或联系我们,讨论您的使用场景。

附录:创建测试环境

测试环境安装在虚拟机上,以实现隔离并确保环境可重复创建。不过,也可以将其安装在物理“裸机”服务器上。

安装 NGINX Plus

请参阅《NGINX Plus 管理指南》。

安装 Galera Cluster for MySQL

在本示例中,我们在一台主机上安装Galera Cluster,并为每个节点使用Docker容器。以下说明改编自《使用Docker快速入门Galera》,并假定已安装Docker Engine和MySQL命令行工具。

  1. 创建一个基本的MySQL配置文件(my.cnf),供Docker镜像将其复制到每个Galera容器中。

    正在加载代码示例…

  2. 拉取Galera基础Docker镜像。
$ sudo docker pull erkules/galera:basic
  1. 创建第一个Galera节点(node1),并将默认MySQL端口公开为33061。
$ sudo docker run -p 33061:3306 --detach=true --name node1 -h node1 erkules/galera:basic --wsrep-cluster-name=local-test --wsrep-cluster-address=gcomm://
  1. 创建第二个Galera节点(node2)。MySQL端口公开为33062,并链接到node1,用于集群间通信。
$ sudo docker run -p 33062:3306 --detach=true --name node2 -h node2 --link node1:node1 erkules/galera:basic --wsrep-cluster-name=local-test --wsrep-cluster-address=gcomm://node1
  1. 按照创建node3的相同方式创建第3个(也是最后一个)Galera节点(node2)。MySQL端口公开为33063。
$ sudo docker run -p 33063:3306 --detach=true --name node3 -h node3 --link node1:node1 erkules/galera:basic --wsrep-cluster-name=local-test --wsrep-cluster-address=gcomm://node1
  1. 创建一个名为“nginx”的用户账户,以便从主机远程访问集群。为此,请在Docker容器内运行“mysql(1)”命令。
$ sudo docker exec -ti node1 mysql -e"GRANT ALL PRIVILEGES ON *.* TO 'nginx'@'172.17.0.1' IDENTIFIED BY 'plus'"
  1. 验证是否可以使用TCP协议从该主机连接到Galera集群。
$ mysql --protocol=tcp -P 33061 --user=nginx --password=plus -e "SHOW DATABASES"mysql: [Warning] Using a password on the command line interface can be insecure.
+--------------------+
| Database           |
+--------------------+
| information_schema |
| mysql              |
| performance_schema |
+--------------------+
  1. 最后,在集群的另一个节点上运行相同的命令,以确认nginx用户账户已复制,并且集群运行正常。
$ mysql --protocol=tcp -P 33062 --user=nginx --password=plus -e "SHOW DATABASES"mysql: [Warning] Using a password on the command line interface can be insecure.
+--------------------+
| Database           |
+--------------------+
| information_schema |
| mysql              |
| performance_schema |
+--------------------+


分享
标签: F5 NGINX, 2021, Tech

关于作者

相关博客文章

NGINX Plus R26 正式发布
NGINX | 2022/02/15

NGINX Plus R26 正式发布

NGINX Plus R26通过缓存JSON Web密钥集加快JWT验证,并强化TLS握手,从而提升安全性。NGINX JavaScript的新功能包括增强对异步函数的支持,以及实现WebCrypto API。

使用NGINX App Protect DoS保护gRPC应用,抵御严重的DoS攻击
NGINX | 2022/02/10

使用NGINX App Protect DoS保护gRPC应用,抵御严重的DoS攻击

gRPC具备开发人员设计和部署现代应用所需的速度与灵活性,但其开放性也使其极易受到第7层DoS攻击。NGINX App Protect DoS可保护gRPC应用免受此类破坏性攻击。

Ingress Controller 选型指南(第 2 部分):风险与面向未来的规划
NGINX | 2021/09/13

Ingress Controller 选型指南(第 2 部分):风险与面向未来的规划

了解选择错误的Ingress控制器可能带来的风险,以及如何从关键方面着手,让您的选择更好地适应未来需求。

NGINX Plus R19 正式发布
NGINX | 2019/09/03

NGINX Plus R19 正式发布

NGINX Plus R19扩展了监控功能,不仅提供更多指标,还新增了指标分析方式;增加试运行模式,用于测试限速效果;增强键值存储,以支持IP地址范围;并使限速更加动态。

NGINX现已正式加入F5
NGINX | 2019/05/09

NGINX现已正式加入F5

NGINX现已正式成为F5的一部分。Gus Robertson现任F5 NGINX业务部门负责人。他阐述了两家公司在人才、文化和愿景方面融合的价值,以及双方将如何携手增强NGINX的开源产品和商业产品。

使用NGINX和NGINX Plus实现高性能缓存
NGINX | 2016/08/24

使用NGINX和NGINX Plus实现高性能缓存

我将介绍NGINX在内容缓存和性能方面的工作原理,并分享一些控制NGINX内容缓存方式的技巧。