故事背景 在Erda的技术架构中,我们使用了kong作为API网关的技术选型。因其具备高并发低延时的特性,同时结合了KubernetesIngressController,基于云原生的声明式配置方式,能够实现丰富的API策略。 在我们最早交付的集群中,kong还是较为早期的0。14版本,随着业务层面对安全的要求日益趋增,我们需要基于kong实现安全插件,帮助系统能够具备更好的安全能力。由于较为早期的0。14版本不能使用gopluginserver来扩展kong的插件机制,我们不得不在古老的集群中将kong升级为相对较新的2。2。0版本。 升级过程就不在此赘述了,基本就是照着官方文档一步步顺利的升级上去,但是在升级上去之后的几天里,我们的SRE团队收到了非常密集的咨询甚至是声讨,部署在该集群上的业务间歇性的无法访问,延迟非常高。一系列失败的尝试参数调优 最开始为了快速修复这个问题,我们对kong的NGINXWORKERPROCESSES、MEMCACHESIZE、DBUPDATEFREQUENCY、WORKERSTATEUPDATEFREQUENCY参数以及postgres的workmem、sharebuffers都进行了适当的调优。 但是,没有任何效果。清理数据 由于这个集群的历史原因,会频繁的注册或者删除api数据,因此集群中累计了大约5万多条route或者service数据。 我们怀疑是数据里量大导致的性能下降,于是结合erda中的数据对kong中的历史数据进行删除,在删除的过程中出现的删除较慢并且同时kong的性能急剧下降的现象。 反复测试了几次后我们确定了只要调用admin的接口导致kong性能下降这一结论,跟社区的这个问题非常相似,链接如下: https:github。comKongkongissues7543kong实例的读写分离 确定了是admin接口的原因后,我们决定将admin跟业务的kong实例分开,希望admin的调用不会影响到业务的正常流量访问,期待达到kong的admin接口慢就慢吧,但是不要影响业务的访问性能。 然而,没有任何效果。postgres迁移RDS kong层面的努力无果之后,我们在测试过程中同时观察到了当调用admin接口试,postgres的进程也增多了很多,CPU使用率也涨了起来,也是决定将pg迁移到更为专业的RDS中。 还是,没有任何效果。回滚 最终我们还是回滚到了0。14版本,追求暂时心灵的宁静。 至此,线上的尝试基本搞一段落,也大致摸清了问题复现的条件,于是我们决定在线下构造一个环境来继续找出问题的原因。问题的复现之路 我们将出问题的kong的postgres数据导一份到开发环境中,模拟调用admin接口是性能急剧下降的情况,并寻找解决之道。导入数据 我们将有问题的集群中的postgre的数据备份之后然后在一个新的集群中导入:psqlh127。0。0。1Ukongkong。sql 并且开启kong的prometheus插件,方便使用grafana来查看性能图标:curlXPOSThttp:10。97。4。116:8001pluginsdatanameprometheus现象一 调用admin服务的同样的慢,跟线上的现象吻合,当数据量大的时候调用admin的目录需要耗费更多的时间。curlhttp:10。97。4。116:8001 现象二 然后我们来模拟在线上遇到的调用admin接口后业务访问性能变差的现象,先调用admin接口创建个业务的api,以供测试,我们创建了一个service以及一个routes:curliXPOSThttp:10。97。4。116:8001servicesdnamebaidu2durlhttp:www。baidu。comcurliXPOSThttp:10。97。4。116:8001servicesbaidu2routesdnametest2dpaths〔1〕baidu2 后面可以使用curlhttp:10。97。4。116:8000baidu2来模拟业务接口进行测试。 准备admin接口的测试脚本,创建并删除一个serviceroute,中间穿插一个servicelist。!binbashcurliXPOSThttp:10。97。4。116:8001servicesdnamebaidudurlhttp:www。baidu。comcurliXPOSThttp:10。97。4。116:8001servicesbaiduroutesdnametestdpaths〔1〕baiducurlshttp:10。97。4。116:8001servicescurliXDELETEhttp:10。97。4。116:8001servicesbaiduroutestest 然后持续调用该脚本:foriinseq1100;dosh1。done 在持续调用该脚本的过程中去访问一个业务的接口,会发现非常慢,跟线上的现象完全吻合。curlhttp:10。97。4。116:8000baidu2 PS:精简脚本,后只触发一条写入,或者删除也会触发该现象伴随现象kong实例的cpu跟mem都持续上涨,且当admin接口调用结束后此现象依然没有结束。mem上涨到一定程度会到时nginxworker进程oom掉,然后重启,这个也许是访问慢的原因;我们设置了KONGNGINXWORKERPROCESSES为4,并且为pod的内存为4G的时候,pod的整体内存会稳定在2。3G,但是调用admin接口试,pod的内存就会一直上涨至超过4G,触发worker的OOM,于是我将pod的内存调整到了8G。再次调用admin接口,发现pod的内存依然一直上涨,只是上涨到了4。11G就结束了,这似乎意味着我们是要设置pod的内存为KONGNGINXWORKERPROCESSES两倍,这个问题就解决了(但是还有个重要的问题是为什么调用一次admin接口,会导致内存涨了那么多);另外,当我持续调用admin接口的时候,最终的内存会持续增长并且稳定到6。9G。 这个时候我们将问题抽象一下: 调用kongadmin接口导致内存一直上涨,进而触发oom导致worker被kill掉,最终导致业务访问慢。 继续调查内存是被什么占用了: 我使用pmapx〔pid〕前后两次查看了worker进程的内存分布,变化的是第二张如中框起来来的部分,从地址上看整块内存都被换过了,但是将内存数据导出并且字符串化之后,没有什么有效的信息可供进一步排查。 结论该问题跟kong的升级(0。142。2。0)没有关系,直接使用2。2。0版本也会有这个问题;kong每隔workerstateupdatefrequency时间后会在内存中重建router,一旦开始重建就会导致内存上涨,看了下代码问题出在了Router。new的方法这里,会申请lrucache但是使用后没有flushall,根据最新的2。8。1版本的lrucache进行了释放问题依然存在;也就是kong的Router。new方法里的其他逻辑到时的内存上涨; 这也就表明这个问题是kong存在的一个性能bug,及时在最新的版本中依然存在,当route跟service达到某个量级的时候会出现调用admin接口,导致kong的worker内存迅速上涨,使得oom进而引发业务访问性能变差的现象,临时的解决办法可以是减少NGINXWORKERPROCESSES的数量并且增加kongpod的内存量,保证调用admin接口后需要的内存足够使用不触发oom,才能保证业务的正常使用。 最后,我们会在https:github。comKongkongissues7543这个issue中将该现象补充进去,欢迎大家持续关注,一起讨论 更多技术干货请关注【尔达Erda】公众号,与众多开源爱好者共同成长