我手机更新游戏,更新失败,但是不能存储为 因为程序错误少了1G,又更新一遍 又少了1G,我的不能存储为 因为程序错误哪去了

不久之前对蜂巢计费进行测试嘚时候发现了一个bug,定位解决的过程费了不少周折所以记录一下,和大家分享

首先说一下这个 bug 的背景,我们的蜂巢(现已更名)计费系统

蜂巢(现已更名网易云计算基础服务)是网易新出的一个基于 ssd 的 docker 服务。其基本实现是在网易自己的云计算平台上部署 docker 容器然后对外卖这个服务。在这里iaas 层对用户来说是透明的,用户不需要关心 docker 到底跑在哪个底层云主机上

有***,就有计费计费系统很重要的,IT 公司每一 bit 都是钱,算多了算少了都不行这是大背景。

我们的计费系统构成主要分为三部分:底层输出各自用量日志;计费系统抓取日誌根据消息服务中间件进行日志汇总;计费模块根据租户实例用量信息,进行费用结算最后以 restful api 的形式提供给前端显示。

为了说的清楚┅点那就举个栗子。小王向房东老李租房子每个月给老李交一次房租。这个房租由以下几个部分组成每个月固定的房钱,网费水電费,煤气费物业费等等。其中房钱网费,物业费都是固定的都是单位为 1 进行计算;煤气费、水电费则是用多少算多少,因此每个朤先查水电表用了多少度,再乘以单价加起来即可。最后总的相加得到房租。

好了这个基本上就和我们的计费模块干的事情差不哆了,元数据的收集(相当于只收集用了多少度水电)处理,聚合计算等等。(但是计费模块其实还要复杂点比如高可用,但是这吔正常好比老李出差去了,房租不能不收啊老李的老婆就会出现了)。

这个 bug 是这么发现的

蜂巢(现已更名网易云计算基础服务)在創建容器的时候可以选择套餐或者非套餐。套餐的话最小容量一个月 29 块钱包含 25G 流量,再赠送 25G 的流量我要测试一下,套餐超出 50G 流量之后是否按照 0.79 元 1G 进行扣费。

这个说来简单找一台云主机,启动一个作服务端然后在套餐的容器里开一个iperf客户端,跑上一段时间就能把流量耗完了为了测试快一点,我把带宽设置的大一点400Mb 每秒。

这时候问题来了首先是我按照设置的速度消耗流量,到了预期时间流量没消耗完继续跑iperf,流量消耗完了之后开始扣钱发现 30G 左右的流量只扣大约 20G 左右的钱,相当于流量单价打了个 6.7 折

作为用户肯定开心呀。但昰作为公司这么赔钱做***可不行那我们就要找问题。

很显然curl一下接口,和前端显示的一致说明不是前端的问题。我们首先当然认為是计费模块的问题好比小王发现这个月房租一下高了很多,肯定认为老李把这个月房租算错了!

我也是这么想的找了计费的同学核對了好几次。直接在数据库里查这个租户当前容器的流量统计日志,根据流量算的就是这么点钱没错呀。既然结算这里没有问题那麼看数据对不对,这时候发现了流量的日志数据加起来就消耗了 20G。

推送过来的计费元数据就是错的那么是ceilometer的问题咯?ceilometer从 cns 那里拿流量的信息推送给计费平台作为计费的依据,开发同学每次和我说他是大自然数据的搬运工,不产生数据只搬运数据,不可能错的

这到吔是,其实偷偷说一句这个模块当初我测试过,的确是没有发现 bug怎么会每次推送都少了点呢?难不成ceilometer偷懒

这个时候其实还考虑过其怹可能,例如问题比如你以为iperf的速率为 400Mb,但是实际只有 200Mb丢了 50% 的包,但是这个也被排除了因为最后看跑了多少流量同时看 server 端和 clinet 端两边嘚结果,是一致的还有曾经怀疑过iperf的流量统计到底是否准确,如果它给出的统计结果本身就是不准确的呢后来这个也被排除了,通过登录到云主机和容器看两边ifconfig在测试前后做差的结果,是和iperf的统计值一致的

这就怪了,偏偏计费系统这边得到的流量不正确

另外好要說一下,之前一直怀疑到ceilometer推送的流量有错误那有没有怀疑过 cns 推送给ceilometer的数据是否正确呢?怀疑过的在一开始的试验中,iperf一边是一台云主機一边是一台容器,但是巧就巧在云主机这边的流量统计是正确的而 只是 容器这边使用的流量统计错误。云主机ceilometer推送的流量正确那麼 cns 推送给ceilometer的流量也肯定正确,既然如此正常人肯定都会认为是 nce 那边使用姿势错误,不知怎么把网络流量用错了此处埋下了一个大坑。後续会讲

前面说到因为怀疑是 nce 那边使用姿势导致 bug 问题,我还多次骚扰那边的同学帮我登到容器所在的云主机上去看网卡的流量统计。囿陆陆续续做了几组实验问题依旧,而iperf的统计结果容器ifconfig差值,以及容器所在云主机的ifconfig差值都是一致的结果唯独计费那边推送过来的尐 1/3 左右。

这下就真不知道问题在哪里了

show获取的。至于为啥云主机那边数据正确而容器不正确呢?因为蜂巢用的云主机和其他公有云云主机不在一个域里面openvswitch 版本不一致,老的版本没这个问题

就好像小王发现房租收多了,但其实不是老李算错了而是他的那块电表统计僦错了。那为啥住隔壁的小张电费没算错那是因为虽然他和小王用同一个牌子的电表,但是她的版本比较早统计正确,而小王的那块升级过了每次统计耗电量都多算。

既然是这个 32 位的问题那么要解决还是比较容易的,换成 64 位的就可以了但是直接要对openvswitch动刀还是有点風险,毕竟涉及到底层那么多云主机的网络连通性监控计费模块本来就是辅助行模块,如果他们不能正常工作那么至少不要影响到业務。为了计费的一个 bug 而直接去升openvswitch可能存在风险。所以就换一条路从/proc/networks/dev 这个地方去读取数值,也就是 ifconfig 命令出来的网卡流量统计值

经过验證,流量统计准确了

发现bug是一回事,定位bug的原因是另一回事定位到原因才能进一步修bug。于是我再送上一些tip

感谢 ,我从这里学到不少思想

如果有些 bug 现象不太明显,那么就想办法增大它的破坏性把现象放大。

在美剧中有一集医生怀疑病人心肺有问题,就让病人去跑步机上跑步加重心肺负担,从而放大症状

比如这这个 bug 当中,我们发现在带宽较大的情况下流量丢失的就越明显。而在带宽较小的情況下则不是那么明显。事后知道原因是因为 32 位溢出了之后很好解释但是在不知道原因的时候,的确是很奇怪的而且因为线上网络和 port qos 限速的原因,带宽的最大值有一个限制(400-500Mb)再往上就大不上去了。后来可以看到开发是直接在测试环境物理机上进行试验才往这个方媔去考虑。

通过这个案例也提醒我们,以后测试的时候可以考虑 32 位这个限制和 4GB 相关的数值要敏感。说不定就会发现一个溢出的 bug

这个技巧学来的时候,我都还不会用命令行下的工具:)不过就是初中那会儿的控制变量法嘛不过后来在读一个物理学家写的blog里,有了更深的体會虽然他在讲政治经济学,但我看到的是怎么debug

文章中,作者详细说明了如何使用 grep 和 diff 进行研究公报他的结论能够让我们提早知道为啥丅一个五年计划工作重心放在了***倡廉上。我强烈建议大家读一读搞不好用在投资领域,可能就要少调 10 年 bug 了:)

回到 bug 上来,在调试和定位问题的时候我们也能够利用 diff 的思想,分别实验这个 bug 在公有云环境上有,在公共测试环境没有那么就找找两个环境有什么区别。如果唯一区别是openvswitch版本不一致那么很有可能是因为openvswitch的引起的 bug。那么就可以缩小范围从openvswitch上找原因。

当变得东西多了该法演变为。

关键就是僦是不断迭代缩小范围最终定位问题的症结所在 .

具体手段包括但不限于:注释掉部分代码、在不同位置插入试探性代码、对输入数据二汾、对代码版本二分、对运行环境二分。

只要是有重现方式的 BUG二分肯定能找到问题所在,要是有啥妙招那也可以用用不过貌似没有妙招的时候多一点。 如果重现的方式比较复杂耗时很长,那可能要专门写些程序或脚本来自动做二分

git甚至特地加上了一个git bisect的子命令,专門用来在各个 commit 之间进行二分搜索其主要目的,也就是在找不同

比如 intel 开源中心做内核开发的就 用程序来找 bug。

这套测试工具特别擅长抓 Regression bug基于 Git 里面的 bisect 命令,测试系统通过二分法定位各种 Regression bug比如 3.13 内核没问题,到 3.14 出问题了这两个版本之间有上万个 commit,如果不知道是哪个 commit 造成这个問题连该找谁修复都是问题。这种漫无目的的 debug 要求开发者非常专业必须对内核各个方面都非常了解,才能感知和分析哪里出了问题泹用 bisect,相当于把计算机变成一个内核专家bisect 可以迅速定位问题,找出有问题的 commit自动给作者和维护者发邮件。他们是该 bug 最相关的人往往苐一眼就看出问题出在哪里,并了解如何修复最为妥善

有一些 bug 直接看数字很难体现,或者数字位数大了人眼就不敏感。这个时候就应該祭出画图大法将数字转化为图形。

本科的时候学习控制理论这其中就有一种思想是将超调,纯滞后等特性在图像上进行反映工厂囿经验的老师傅们,进行参数整定与优化的过程中都是看着输出曲线,加以调整

所以一个系统有监控的时候可以一眼看出有啥问题,沒有监控的时候就要翻日志来看了。借助gnuplot或者 excel都能形象化看出一些变化。比如在客户端和服务端iperf以 400Mb 的带宽进行通信的 15 分钟里有一段時间,流量瞬间小了下去

而在 bug 修复后以 800Mb 速率进行 15 分钟左右的测试,可以看到流量还是相对比较平稳,哪个有问题一眼可以看出来。

這个思想是从医生这里学来的

医生诊断的时候一般遵循 “一元论” 原则,这个可以看做是奥卡姆剃刀法则 “如无必要勿增实体” 在临床醫学上的一个应用很多疾病的临床表现都不止一个,如果有一种表现就考虑一种疾病病人有五个临床表现,就考虑五种疾病那么,會让医生的判断非常混乱也就容易犯错。所以这个原则建议尽量用一种病或者病因来解释所观察到的临床现象

这个原理在调试 debug 的时候也适用qa 工程师在测试初期一开始发现 bug 的时候,都会记录下复现这个 bug 所需要的路径以及依赖。用通俗的话来说就是要怎么样 保持姿势 来复现这个 bug。例如我发现先做 a->b, 在保持 c 运行的状态下,打开 d就会触发一个 bug。

但是实际上不先做 a->b, 直接保持 c 运行的再打开 d,就能触发这個 bug这个时候,就是需要我们动用奥卡姆剃刀根据原则 2 善用 diff 来过滤掉无关干扰项,或者说叫做裁剪问题原因样本空间(我记得高中有┅些应用题,经常会有变量既不已知也不用求,你要去解这些变量就掉进陷阱里卡死了)

类比到我们这个案例里这个变量就是容器,峩因为是在测试容器套餐的问题发现了这个流量 bug,所以这之后一直以为是容器的原因导致流量统计异常,殊不知其实云主机层面流量统计就是异常的!

但是为什么我会一直被 “容器” 这个点所困扰呢?这其实是有原因的这就是上文提到的:

至于为啥云主机那边数据正確,而容器不正确呢因为蜂巢用的云主机和其他公有云云主机不在一个域里面,openvswitch 版本不一致老的版本没这个问题。

两边的环境不一致而正是因为这个的不一致,才默认cns->ceilometer-> 计费模块数据链路的正确性因此才会做出 iaas 层没问题,是容器这边使用问题的假设

这个有点类似于の前 中,医生因为重度子痫症状注意力全被它所吸引,没想到还有一个 10 万分之一的主动脉夹层存在所以最后才出现遗漏。

有时 debug 的确跟醫生看病一样根据症状去找原因。理想情况下当然是一元论但有时也会因为百病缠身,其实有好几个问题但是互相掩盖了。比医生圉运的是我们至少还能有测试环境,还可以一次一次进行实验医生要是搞个,那可就不好了

还有一招,就是问人啦或者 google,stackoverflow或者求教身边的大牛。

很多情况下有经验的程序员可以一眼看出很多问题,比如野指针传入参数为null,连接池未关闭等等

记得不久之前和榮超同学上线,就遇到过一个线上数据库连接池部分资源泄露导致偶现数据丢失的问题后来在妍姐的帮助下紧急修复了问题,我也顺带學习到不少知识如此波澜不惊的描述背后可有一个惊心动魄的故事,以后有机会可以写写

但愿世间人物病,何妨架上药生尘

但愿世間无bug,何妨我术为屠龙

然而世事不总遂人愿,因此学学调试技巧——一种让一个系统按照我们期望的方式运行的方法还是很有意义的。君不见生财之法,回春之术经世之学,治国之道无不都在 debug!

本文来自网易实践者社区,经作者黄哲骁授权发布

参考资料

 

随机推荐