一次非典型性 Redis 阻塞总结

南方逐渐进入一年中最好的时节,用户也开始骚动起来。看了眼数据,活跃用户已经double很远,马上triple了。

一日睡眼惺忪的清晨,正看着数据默默yy时候,线上开始告警…… MMP,用户早上骚动的增长比想象好快呢。同事第一时间打开立体监控瞥了一眼,结合服务的错误日志,很快把问题锁定到了一个Redis实例(事实上,自从立体监控上线以后,基本上处理流程从以前的 < 80%时间定位问题 + 20%解决问题 > 变成了 < 少量时间确认问题 + 解决问题 >)。团队处理效率还是挺快的,原因定位到AOF持久化:

这是当时的Redis配置:

127.0.0.1:6379> config get *append*
1) "no-appendfsync-on-rewrite"
2) "no"
3) "appendonly"
4) "yes"
5) "appendfsync"
6) "everysec"

从配置看,原因理论上就很清楚了:我们的这个Redis示例使用AOF进行持久化(appendonly),appendfsync策略采用的是everysec刷盘。但是AOF随着时间推移,文件会越来越大,因此,Redis还有一个rewrite策略,实现AOF文件的减肥,但是结果的幂等的。我们no-appendfsync-on-rewrite的策略是 no. 这就会导致在进行rewrite操作时,appendfsync会被阻塞。如果当前AOF文件很大,那么相应的rewrite时间会变长,appendfsync被阻塞的时间也会更长。

这不是什么新问题,很多开启AOF的业务场景都会遇到这个问题。解决的办法有这么几个:

  1. no-appendfsync-on-rewrite设置为yes. 这样可以避免与appendfsync争用文件句柄,但是在rewrite期间的AOF有丢失的风险。
  2. 给当前Redis实例添加slave节点,当前节点设置为master, 然后master节点关闭AOF,slave节点开启AOF。这样的方式的风险是如果master挂掉,尚没有同步到salve的数据会丢失。

我们采取了折中的方式:在master节点设置将no-appendfsync-on-rewrite设置为yes,同时添加slave节点。

理论上,问题应该解决了吧?啊蛤,的确是理论上。

修改后第一天,问题又出现了。惊不惊喜,意不意外?

于是,小伙伴又重新复习了一下当时出问题时候的Redis日志:

有两个点比较可以:

  1. 前几条AOF日志告警日志发生在晚上3~5点之间,而那个时候,我们整个系统负载是非常低的。
  2. 清晨的告警日志不是某一个Redis实例告警,而是该机器上的所有Redis实例都在告警。

在这种百思不得骑姐的情况下,结合历史上被坑的经验,我们99%断定是我们使用的云主机存在问题。

这个问题有可能是宿主机超售太多导致单个租户实际能使用到的云盘IO比标称值低,也有可能是租户隔离做得不好,导致坏邻居过度占用IO影响其他租户。

这个很好理解:我们使用的是阿里云的云SSD,而阿里云目前的架构还没有做到计算和存储分离,即计算和存储的网络IO是共享的。

当然目前这个问题还没有实锤,我们也还在跟阿里云积极沟通解决。同时为了避免给自己惹麻烦,我还是留了1%的其他可能性?

祝大家周末愉快!

参考资料

Redis相关—Redis持久化

微服务架构下的立体监控系统设计和实现

背景

GOPS全球运维大会(北京站)听到了不少干货。特别受益的是来自腾讯SNG事业部聂鑫分享的
《从0到1到N,腾讯监控体系全透视》

在他的主题分享中,他将腾讯这些年的监控系统的发展历程概括为点监控-->面监控-->深度监控

看到他这页幻灯片的时候,有一种醍醐灌顶的感觉。因为在听他分享的时候,我们的系统才刚刚完成架构微服务化没多久,我们上线了调用链:分布式追踪系统来解决在微服务分布式系统中排查跟踪特定问题,但我们的监控系统还没有针对架构微服务化后进行相应的进化。比如,大部分监控系统停留在点监控的层面,少数进行关联多个服务的面监控也做得比较初级,需要人工分析和干预。

点监控比较好理解,就是对系统布置监控点,根据阈值触发告警。

面监控则是对告警信息进行时间和空间关联,有效消除毛刺告警,使告警更加准确。因为告警本身有时效性,时效性源于告警延时,连续性可能是干扰,因此只进行时间关联是不够的。链路相关性(空间相关性)和时间相关性一起决定准确性。

深度监控其实有点追深度学习的热点,从分享看,实际就是对面监控的链路相关性进一步完善,以及根据收集到的系统进行使用机器学习进行简单的分类。

参加会议回来以后,我们明确了自己监控系统的进化方向,根据自身系统的特点进行了一些取舍,确定了立体监控的方案。

立体监控方案目标

所谓「立体监控」即指在我们当前系统点监控为主的情况下,尽可能复用当前监控的探针,进行时间和空间(服务链路之间)维度上的扩展,实现对整个系统时空上的监控。

立体监控需要消除点监控带来的监控毛刺,如服务存在依赖情况下,级联告警通过立体监控分析融合后,应该只对最后一级进行告警。

立体监控可以快速的定位系统故障,定位粒度根据不同监控类型可以做到微服务级别、接口级别、数据库实例级别、缓存实例级别等。

立体监控方案设计和实现

对于微服务,我们通过data bus将需要进行监控的信息发送到kafka进行收集。这种方式在调用链分布式追踪系统也有使用。不得不说,data bus是架构微服务化后非常重要和实用的基础组件。

为了尽可能降低各个微服务集成监控组件的侵入性,我们通过修改基础库的方式进行集成。比如,微服务使用了MySQL数据库,那么我们就修改微服务使用的数据库驱动来对数据库进行监控,一旦发生错误或warning信息,将消息写入data bus;某个微服务需要调用腾讯的某个接口,我们就对修改我们的http客户端基础库,将错误和超时消息写入data bus实现对该接口的监控。

因此,我们的微服务监控集成几乎不需要研发人员的介入,只需要运维人员更新服务依赖库,然后重新发布上线即可。事实上,我们在微服务监控集成上就没有安排独立的发布上线时间,都是在研发上线feature或hotfix时搭车上线的,将近两百个微服务在2周内完成集成。需要注意的是,基础库的修改一定是由团队中相对资深的开发人员来做,并且测试一定要做到位,否则会引起大规模的问题。

对于k8s集群、数据库、redis等基础设施的监控沿用以前的点监控数据,只是将事件统一上报到了Event Collector事件收集服务。Event Collector除了收集事件数据,也对一些不符合通用规则的数据进行过滤。

Event Analyzer事件分析服务根据Event Collector收集到的事件,进行时间和空间上的分析监控结果,并发送告警通知。

在事件分析上,时间维度非常简单,选取一个时间窗口内的事件信息即可(我们当前根据经验设定的是1min)。空间维度方面,则相对麻烦一下。我们没有采用腾讯使用的全链路分析算法。主要是因为该算法需要预生成链路拓扑图。而微服务架构中,各个微服务的增加、减少和变更是非常频繁的,预生成拓扑图有点反模式,也会产生一定的成本。我推测腾讯之所以觉得预生成拓扑图不是问题,跟它当前架构没有完全微服务化以及内部严格的管理流程有关。

因为不想预生成链路拓扑图,分享中给出的链路面积计算公式也就无法使用。另一方面,因为腾讯给出的全链路分析算法其实没有完备的理论证明,在我们数据量没有腾讯庞大的情况下,我们是否能用该算法取得同样效果是缺乏信心的。

但是,思想是可以借鉴的。通过讨论,我们一致认为所谓的链路分析其实就是关联性分析,而关联性分析那就毫不犹豫的使用Google Page Rank算法:

如上图所示,我们将服务(1, 2, 3, 4)之间的告警事件作为一个超级链接指向,然后计算PR值,那么,PR值最大的是我们认为出现问题可能性最大的服务。理论证明这里略过,这个锅我们扔给Google背即可。

需要说明的是,任何一个复杂的系统在出现故障时,往往不是一个组件或服务出现问题,很可能是多个服务同时出现问题。那么在计算PR时,可能就是面对多个独立的PR有向图。这个时候独立图之间的关系处理就可以根据历史数据进行机器学习,以进一步给出故障原因,然后根据预案快速处理故障,恢复服务。

Event Analyzer也提供了一个页面,可以查看历史的告警信息链路:

也可以对链路流量(边越粗流量越大)进行监控和分析:

总结

立体监控上线后,运维方面以前只能从点入手排查问题转变为直接根据Event Analyzer的聚合告警信息联系对应服务的开发者解决问题。同时,发现了很多以前被经验标记为系统抖动没有重视的潜在问题。至此,我们的微服务架构在调用链和立体监控的双重加持下,又完成了一次进化。