以服务网格实现微服务的高级Traffic-shadowing模式

3个月前

像Twitter、亚马逊一样,将生产流量镜像到测试集群,降低新部署风险。

本文获得blog.christianposta授权翻译发表,转载需要注明来自公众号EAWorld。


作者:Christian Posta 

译者:月满西楼 

原题:Advanced Traffic-shadowing Patterns for Microservices With Istio Service Mesh


全文5000字,阅读约需要12分钟


这两年, 微服务架构火了,它在实现某些功能上速度更快,为我们节省了不少宝贵时间[1]。然而,我们不能只是简单地追求速度,破旧立新[2]。还要设法降低变革带来的风险,更安全地将微服务引入生产。一个强有力的模式就可以做到,它能将有关生产的shadow traffic流量引入到测试集群(test cluster)或者软件的新版本中,并在生产流量正式实时传输前对其进行测试。如此一来,我们可以借此提前对真实案例和尚未确定的用法(obscure useage)进行测试,这是传统的模拟测试无法实现的。我之前在博客中写过一篇“ 服务网格是怎样有效实现镜像流量(how Istio Service Mesh has a nice feature for mirroring traffic)”的文章。我的博文中实际上还有很多关于Istio和服务网格的内容,大家可关注我的Twitter(@christianposta),参与最新讨论,查阅最新的技术文章。言归正传:


一、Shadowing Traffic存在的问题


当与生产相关,实时产生的shadow traffic流量引入到测试集群(test cluster),或是正在从事生产工作的dark cluster中时,我们就会面临一些挑战。首先,如何在不影响线上服务关键路径的前提下,将流量引入集群?这些请求中的个人信息需不需要过滤出来?如何控制测试集群(test cluster)不去干扰实时协作服务?如果新服务更改了数据,如何防止这些变动影响正常生产?


这些都是实实在在的挑战,可能也是大家不愿意尝试Shadowing traffic的原因。IMHO shadowing提供了一项非常重要且功能强大的技术,可以实现新服务的安全发布。让我们来看看解决这些问题的一些方法,例如:


  • 在不影响关键路径的前提下,将流量引入测试集群 

  • 将流量注解为shadow traffic 

  • 在完成Shadowing后,将测试集群与实时服务流量进行对比 

  • 删除某些测试配置文件的协作服务 

  • 合成事务(Synthetic transactions) 

  • 虚拟化(Virtualizing)测试集群数据库 

  • 具体化(Materializing )测试集群数据库


下面让我们继续深入探讨。


二、在不影响关键路径的前提下,

将流量引入测试集群


这可以说是最关键的部分。如果不能实现这一步,那么后续的操作也就无法继续进行。纵然我们关于微服务的奇思妙想有多么吸引人,也不能牺牲生产的可靠性和可用性。通常我们会利用代理来引入这些流量。Envoy Proxy[3]就是一款不错的代理。Istio则是一款可以将Envoy设为默认即用代理,从而引入流量的服务网格(service mesh),详见Istio Mirroring Task[4]。基本上,服务网格 (Istio)本身位于生产流量中的关键路径上,这是为了实现整个系统的伸缩性、安全性、策略执行、路由控制等,同时还可以将流量引入到测试集群(test cluster)中。这也是我上一篇技术博客[5]中深入探讨过的内容。重要是,流量是异步镜像过去的,并且不受线上流量的影响。任何响应都将被忽略掉。


对于熟悉所谓“企业集成模式(enterprise integration patterns)”(在此要感谢Gregor Hophe!)的读者来说,你会发现这种“镜像(mirroring)”操作其实是一个很好的应用,还可以支持绑定到EIP[6]


三、将流量注解为shadow traffic


另一个需要注意的关键点是识别已经被镜像的流量。实时的线上流量和用于测试目的流量需要被区分开,通过Istio/Envoy,被镜像/复制(shadow)的流量会自动附加文本(context),用以识别。例如,当Istio对流量进行镜像时,它会在Host 或Authority 的数据头上添加[7]一个<-shadow>。对于某些实现来说,这会有些问题,因为<-shadow>将被添加到Host的末端,所以 foobar:8080 的Host 数据头将类似 :foobar:8080-shadow 来结尾,但这是个无效的HTTP 1.x。Envoy修复[8]了这个问题,-shadow 这个后缀被添加到主机名中,因此 foobar:8080 就变成了 foobar-shadow:8080。



四、在完成Shadowing后,

将测试流量与实时服务流量进行对比


一旦我们能够可靠地shadow流量,就可以开始做一些有趣的事情。我们可能希望将引入测试集群的流量与生产集群中的预期行为进行比较。例如,我们可能想比较请求结果与预期结果间的偏差,或是API协议中的数据损坏情况,以便更好地兼容。此处,插入一个代理就可以负责此类流量的协调,并对其进行有趣的比较。Twitter Diffy [9]就是一款这样的代理工具,在Twitter和其他软件产品中运用[10]已经有一段时间。它基本上就可以镜像流量(这点我们通过运用Istio和Envoy已经做到了),调用实时服务和新服务,并对结果进行比较。它还能够检测结果中的“噪音(noise)”,并通过先调用两个实时服务的实例来忽略它们(例如时间戳,单调递增计数器等提示),总结来说就是检测,然后在测试服务中忽略掉这部分。



Diffy 这个工具有一个不错的页面可以用来查看调用结果、对比情况、和基于某些特征的过滤。它还有一个很好的管理控制台,可以查看有关调用比较结果的功能指标(metrics)和统计数据(statistics)。


在这我有一个演示[11],感谢 Prashant Khandur[12]i,Puneet Khanduri[13]和 Alex Soto[14]。这个演示视频还在继续完善中,你还可以持续关注我。


五、删除某些测试配置文件的

协作服务


当新版本服务和镜像流量部署到测试集群时,需要注意它们对其他服务的影响。新服务通常需要与其他服务协作(例如数据查询、数据更新等)。如果新服务与其他服务的协作仅限于读取或GET请求,而协同方能承担额外负载,那么这就不是问题。 


一旦新服务影响到了协作方的数据,那就需要确保这些调用被引导到测试替身(test double),而不是真正的生产流量。在部署过程中,你可以创建不同的安装配置。例如,在下游服务中,用test.prod.com来代替live.prod.com。如果在Kubernetes上部署,你还可以使用不同的Config Maps[15](译者注:Kubernetes外挂配置管理功能)来控制这一点。另一种比较有意思的方法是用Hoverfly[16]Microcks[17]这类工具来部署虚拟测试替身(virtualized test double)。有了这些服务虚拟化工具,我们就创建预期的请求-响应对(request/response pairs),将变异值直接导入这些代理,并获取预期的反馈。



六、合成事务

(synthetic transaction)


多数情况下,新服务需要在本地数据存储中更改数据。它可能会调用协作服务来进行更改,但也许我们不能或者说不应该用以前类似服务虚拟化这样的技术来完成这些调用。还有一种方法是对这些调用进行更为明确的注释,比如前文提到的添加<-shadow>的方法,提示这些调用最终可能会导致“合成事务”, 而这些都不是真实的处理,请求结束时应该对它们进行补充说明。我们可以为请求添加一个数据头,甚至可以将其作为请求的一部分,以表示某个处理事物是“合成”的。这样操作时,其实是在引导参与的服务以正常方式处理请求,包括所有数据操作,然后在提交之前回滚事务。注意,这在事务性数据存储中很有用,但不适用于其他数据存储。因此,如果已经有了一个工作单元的概念,可以将合成语义附加进去。否则的话,在无法取消更改之类的操作时,最好还是不要尝试合成事务。



这种方法对执行请求的完整路径非常有效,包括数据存储,它能更好地处理时间设置、数据干扰或不匹配的问题,而这些问题可能无法通过测试替身(test double)来察觉。


此方法最大的缺点是,它是按照规则(convention)来实现的,并且执行起来有难度。它可以在现有的服务中实现,但可能无法扩展到更多的协作方。你可能也不愿意在所有服务中都强制执行这个规则,而且一旦有某个服务不能正确地执行回滚功能,就会弄乱所有数据。该模式只能在严格控制和协调部署的前提下执行。


七、将测试集群的数据库虚拟化


针对镜像流量的测试开始后,我们就会面临数据处理相关的问题。通常来说,如果测试集群调用了数据存储,且测试服务以某种形式对数据进行更新、插入或修改,那就得隔离这些变更。只有在数据头或嵌入的标志有信号时,我们才可以进行回滚更改的操作,但总这么做也不是个办法。


处理镜像流量相关数据问题的另一种方法,是为测试集群准备一个可替换的数据存储。这个数据存储可以是空的,往里导入测试数据之后,再开始做镜像等操作。然而,如果用上文提及的Diffy这类自动化测试工具,可能最终我们会得到大量的错误结果,这是因为测试集群中使用的是测试数据,而实时服务使用的是生产数据。一个比较好的解决方法是对数据层进行虚拟化。让测试集群使用一个代表其本身的数据存储,使它与生产数据存储使用相同的数据。



这么一来,我们可以对生产数据有一个实时、直观的掌握,还可以在不影响生产数据存储的情况下,对其进行读写操作。这可以用JBoss Teiid[18]这类数据虚拟化系统工具来实现。Teiid支持为所有类型的数据存储系统提供连接,包括RDBMS、无sql系统、平面文件(flat file)、hadoop、salesforce等,并可以为测试集群(test cluster)虚拟化它们。每次进行读写操作的时候,变动过的数据就会进入垃圾数据库,但是新服务却不受影响。我有一系列的博文曾经探讨过这个问题,其中有一篇重点地讨论了微服务的迁移问题[19]


八、具体化测试集群的数据库


最后这个方法是前一项数据虚拟化技术的扩展,即完全具体化数据存储。通过这种方式,测试集群的数据存储基本上与生产集群保持一致,并能通过流处理(stream processing)不断更新。它的工作方式是抓取生产数据库中的更改(CDC-变更数据捕获),然后将这些更改发送到一个新的数据库。一些数据存储,像MySQLslave会允许这类操作作为一个内置的复制机制,但是多数情况下它们都是只读的。而Debezium[20]这类变更数据捕获工具,则可以通过构建一个简单的CDC系统,允许测试数据存储有一个复制完整,且能自由使用的生产数据库。Debezium可以为不同的数据存储提供连接器[21],并从这些数据库中获取更改事件,比如读取事务日志等,然后将这些更改导入Apache Kafka[22],进行实时流式数据分析。至此,你可以使用任意流处理工具将这些流式数据具体化到测试数据库中。前文提到的FWIW,Teiid这些工具,很快就会有这项功能。



此外,如果你已经有一个数据流管道,是用事件驱动架构(event driven architecture)的,或是采用了某种事件源数据机制的。那么这个“具体化”测试数据库将成为一个非常不错的选择。


九、总结


实践中,无论是在生产环境还是非生产环境中,将生产流量镜像到测试集群,是降低新部署风险的一种非常有效的方法。像Twitter,亚马逊这样的大型互联网企业,多年来也一直在坚持这么做。


但这种方法也带来了一些挑战,而上文的讨论中则提供了一些不错的解决方案。如果你觉得我有遗漏掉了什么内容,或者没有提及到另一个存在的问题,请赶紧联系我,我会很高兴地与你一起探讨,并把讨论内容添加到本文的更新中。谢谢!


原文链接http://blog.christianposta.com/microservices/advanced-traffic-shadowing-patterns-for-microservices-with-istio-service-mesh/


参考地址:

[1] https://www.slideshare.net/ceposta/microservices-journey-summer-2017

[2] https://www.cnet.com/news/zuckerberg-move-fast-and-break-things-isnt-how-we-operate-anymore/

[3] https://www.envoyproxy.io/docs/envoy/latest/api-v2/api/v2/route/route.proto.html?highlight=shadow#route-routeaction-requestmirrorpolicy

[4] https://istio.io/docs/tasks/traffic-management/mirroring.html

[5] http://blog.christianposta.com/microservices/traffic-shadowing-with-istio-reduce-the-risk-of-code-release/

[6] http://www.enterpriseintegrationpatterns.com/patterns/messaging/WireTap.html

[7] https://www.envoyproxy.io/docs/envoy/latest/api-v2/api/v2/route/route.proto.html?highlight=shadow#route-routeaction-requestmirrorpolicy

[8] https://github.com/envoyproxy/envoy/pull/2600

[9] https://github.com/twitter/diffy

[10] https://blog.twitter.com/engineering/en_us/a/2015/diffy-testing-services-without-writing-tests.html

[11] https://github.com/christian-posta/istio_tutorial/blob/ceposta-diffy/diffy/readme.md

[12] https://twitter.com/khanduripfy/readme.md

[13] https://twitter.com/pzdk

[14] https://twitter.com/alexsotob

[15] https://kubernetes.io/docs/tasks/configure-pod-container/configure-pod-configmap/

[16] https://hoverfly.io

[17] http://microcks.github.io

[18] http://teiid.jboss.org

[19] http://blog.christianposta.com/microservices/low-risk-monolith-to-microservice-evolution-part-iii/

[20] http://debezium.io

[21] http://debezium.io/docs/

[22] http://kafka.apache.org


关于EAWorld微服务,DevOps,数据治理,移动架构原创技术分享,长按二维码关注

COMMENTS

需要 后方可回复
如果没有账号可以 一个帐号。