【干货集锦】从架构和实践,剖析KubeEdge+Volcano技术硬实力1
一、背景
近期由测试反馈的问题有点多,其中关于系统可靠性测试提出的问题令人感到头疼,一来这类问题有时候属于“偶发”现象,难以在环境上快速复现;二来则是可靠性问题的定位链条有时候变得很长,极端情况下可能要从 A 服务追踪到 Z 服务,或者是从应用代码追溯到硬件层面。
本次分享的是一次关于 MySQL 高可用问题的定位%过程,其中曲折,颇多但问题本身却比较有些代表性,遂将其记录以供参考。
架构
首先,本系统以 MySQL 作为主要的数据存储部件。整一个是典型的微服务架构(SpringBoot + SpringCloud),持久层则采用了如下几个组件:
mybatis,实现 SQL <-> Method 的映射
hikaricp,实现数据库连接池
mariadb-java-client,实现 JDBC 驱动
在 MySQL 服务端部分,后端采用~了双主架构,前端以 !keepalived 结合浮动IP(VIP)做一层高可用。如下:
image.png
说明
MySQL 部署两台实例,设定为互为主备的关系。
为每台 MySQL 实例部署一个 keepalived 进程,由 keepalived 提供 VIP 高可用的故障切换。
实际上,keepalived 和 MySQL 都实现@了容器化,而 VIP 端口#则映射到 VM 上的 nodePort 服务端口上。
业务服务一律使用 VIP 进行数据库访问。
Keepalived 是基于 VRRP 协议实现了路由层转换的,在同一时刻,VIP 只会指向其中的一个虚拟机(master)。当主节点发生¥故障时,其他的 keepalived 会检测到问题并重新选举出新的 master,此后 VIP 将切换到另%一个可用的 MySQL 实例节点上。这样一来,MySQL 数据库就拥有了基础的高可^用能力。
另外一点,Keepalived 还会对 MySQL 实例进行&定时的健康检查,一旦发现 MySQL 实例不可用会将自身进程杀死,进而再触发 VIP 的切换动作。
问题现象
本次的测试用例也是基于虚拟机故障的场景来设计的:
image.png
持续以较小的压力向业务服务发起访问,随后将其中一台 MySQL 的容器实例(master)重启。
按照原有的评估,业务可能会产生很小的抖动,但其中断时间应该保持在秒级。
然而经过多次的测试后发现,在重启 MySQL 主节点容器之后,有一定的概率会出现业务却再也无法访问的情况!
二、分析过程
在发生问题之后,开发同学的第一反应是&* MySQL 的高可用机制出了问题。由于此前曾经出现过由于 keepalived 配置不当导致 VIP 未能及时切换的问题,因此对其已经有所戒备。
先是经过一通的排查,然后并没有找到 keepalived 任何配置上的毛病。
然后在没有办法的情况下,重新测试了几次,问题又复现了。
紧接着,我们提出了几个疑点:
1.Keepalived 会根据 MySQL 实例的可达性进行判断,会不会是健康检查出了问题?
但在本次测试场景中,MySQL (容器)销毁会导致 keepalived 的端口探测产生失败,这同样会导致 keepalived 失效。如果 keepalived 也发生了中止,那么 VIP 应该能自动发生抢占。而通过对比两台虚拟机节点的信息后,发现 VIP 的确发生了切换。
- 业务进程所在的容器是否发生了网络不可达的问题?
尝试进入容器,对当前发生切换后的浮动IP、端口执行 telnet 测试,发现仍然能访问成功。
连接池
在排查前面两个疑点之后,我们只能将目光转向了业务服务的DB客户端上。
从日志上看,在产生故障的时刻,业务侧的确出现了一些异常,如下:
Unable to acquire JDBC Connection [n/a]
java.sql.SQLTransientConnectionException: HikariPool-1 - Connection is not available, request timed out after 30000ms.
at com.zaxxer.hikari.pool.HikariPool.createTimeoutException(HikariPool.java:669) ~[HikariCP-2.7.9.jar!/:?]
at com.zaxxer.hikari.pool.HikariPool.getConnection(HikariPool.java:183) ~[HikariCP-2.7.9.jar!/:?]
…
这里提示的是业务操作获取连接超时了(超过了30秒)。那么,会不会是连接数不够用呢?
业务接入采用的是 hikariCP 连接池,这也是市面上流行度很高的一款组件了。
我们随即检查了当前的连接池配置,如下:
//最小空闲连接数
spring.datasource.hikari.minimum-idle=10
//连接池最大大小
spring.datasource.hikari.maximum-pool-size=50
//连接最大空闲时长
spring.datasource.hikari.idle-timeout=60000
//连接生命时长
spring.datasource.hikari.max-lifetime=1800000
//获取连接的超时时长
spring.datasource.hikari.connection-timeout=30000
其中 注意到 hikari 连接池配置了 minimum-idle = 10,也就是说,就算在没有任何业务的情况下,连接池应该保证有 10 个连接。更何况当前的业务访问量-极低,不应该存在连接数不够使用的情况。
除此之外,另外一种可能性+则可能是出现了“僵尸连接”,也就是说在重启的过程中,连接池一直没有释放这些不可用的连接,最终造成没有可用连接的结果。
开发同学对"僵尸链接"的说法深信不疑,倾向性的认为这很可能是来自于 HikariCP 组件的某个 BUG…
于是开始走读 HikariCP 的源码,发现应用层向连接池请求连接的一处代码如下:
- 点赞
- 收藏
- 关注作者
评论(0)