如何通过Istio实现微服务特性

{"type":"doc","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"在微服务架构中,应用程序是由多个相互连接的服务组成的,这些服务协同工作以实现所需的业务功能。所以,一个典型的企业级微服务架构如下所示:"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https:\/\/imgopt.infoq.com\/fit-in\/1200x2400\/filters:quality(80)\/filters:no_upscale()\/articles\/microservicilities-istio\/en\/resources\/71image001-1624026324115.jpg","alt":null,"title":null,"style":[{"key":"width","value":"75%"},{"key":"bordertype","value":"none"}],"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"最初,我们可能认为使用微服务架构实现一个应用程序是很容易的事情。但是,要恰当地完成这一点并不容易,因为我们会面临一些新的挑战,而这些挑战是单体架构所未曾遇到的。举例来讲,这样的挑战包括容错、服务发现、扩展性、日志和跟踪等。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"为了应对这些挑战,每个微服务都需要实现在Red Hat被称为“微服务特性(microservicility)”的内容。这个术语指的是除了业务逻辑之外,服务必须要实现的一个横切性关注点的列表。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"这些关注点总结起来如下图所示:"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https:\/\/imgopt.infoq.com\/fit-in\/1200x2400\/filters:quality(80)\/filters:no_upscale()\/articles\/microservicilities-istio\/en\/resources\/27image002-1624026324115.jpg","alt":null,"title":null,"style":[{"key":"width","value":"75%"},{"key":"bordertype","value":"none"}],"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"业务逻辑可以使用任何语言(Java、Go或JavaScript)或任何框架(Spring Boot、Quarkus)来实现,但是围绕着业务逻辑,我们应该实现如下的关注点:"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong"}],"text":"API"},{"type":"text","text":":服务可以通过一组预先定义的API操作进行访问。例如,在采用RESTful Web API的情况下,会使用HTTP作为协议。此外,API还可以使用像"},{"type":"link","attrs":{"href":"https:\/\/swagger.io\/","title":"","type":null},"content":[{"type":"text","text":"Swagger"}]},{"type":"text","text":"这样的工具实现文档化。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong"}],"text":"发现(Discovery)"},{"type":"text","text":":服务需要发现其他的服务。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong"}],"text":"调用(Invocation)"},{"type":"text","text":":在服务发现之后,需要使用一组参数来调用它,并且可能会返回一个响应。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong"}],"text":"弹性(Elasticity)"},{"type":"text","text":":微服务架构很重要的特性之一就是每个服务都是有弹性的,这意味着它可以根据一些参数(比如系统的重要程度或当前的工作负载)独立地进行扩展和伸缩。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong"}],"text":"回弹性(Resiliency)"},{"type":"text","text":":在微服务架构中,我们在开发时应该要考虑到故障,特别是与其他服务进行通信的时候。在单体架构中,应用会作为一个整体进行启动和关闭。但是,当我们把应用拆分成微服务架构之后,应用就变成由多个服务组成的,所有的服务会通过网络互相连接,这意味着应用的某些部分可能在正常运行,而其他部分可能已经出现了故障。在这种情况下,很重要的一点就是遏制故障,避免错误通过其他的服务进行传播。回弹性(或称为应用回弹性)是指一个应用\/服务能够对面临的问题作出反应的能力,在出现问题的时候,依然能够提供尽可能最好的结果。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"","alt":null,"title":null,"style":[{"key":"width","value":"75%"},{"key":"bordertype","value":"none"}],"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong"}],"text":"管道(Pipeline)"},{"type":"text","text":":服务应该能够独立部署,不需要任何形式的部署编排。基于这一点,每个服务应该有自己的部署管道。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong"}],"text":"认证(Authentication)"},{"type":"text","text":":在微服务架构中,涉及到安全性时,很重要的一个方面就是如何认证\/授权内部服务之间的调用。Web token(以及通用的token)是在内部服务之间声明安全性的首选方式。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong"}],"text":"日志(Logging)"},{"type":"text","text":":在单体应用中,日志是很简单的事情,因为应用的所有组件都在同一个节点中运行。现在,组件以服务的形式分布在多个节点上,因此,为了全面了解日志跟踪的情况,我们需要一个统一的日志系统\/数据收集器。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong"}],"text":"监控(Monitoring)"},{"type":"text","text":":要保证基于微服务的应用正确运行,很重要的一个方面就是衡量系统的运行情况、理解应用的整体健康状况并在出现问题的时候发出告警。监控是控制应用程序的重要方面。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong"}],"text":"跟踪(Tracing)"},{"type":"text","text":":跟踪是用来可视化一个程序的流程和数据进展的。当我们需要检查用户在整个应用中的操作时,它对开发人员或运维人员尤其有用。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"Kubernetes正在成为部署微服务的事实标准工具。它是一个开源的系统,用来自动化、编排、扩展和管理容器。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"但是在我们提到的十个微服务特性中,通过使用Kubernetes只能覆盖其中的三个。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https:\/\/imgopt.infoq.com\/fit-in\/1200x2400\/filters:quality(80)\/filters:no_upscale()\/articles\/microservicilities-istio\/en\/resources\/15image006-1624026324115.jpg","alt":null,"title":null,"style":[{"key":"width","value":"75%"},{"key":"bordertype","value":"none"}],"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"**发现(Discovery)**是通过_Kubernetes Service_理念实现的。它提供了一种将_Kubernetes Pod_(作为一个整体)进行分组的方式,使其具有稳定的虚拟IP和DNS名。要发现一个服务只需要发送请求的时候使用Kubernetes的服务名作为主机名即可。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"使用Kubernetes **调用(Invocation)**服务是非常容易的,因为平台本身提供了所需的网络来调用任意的服务。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong"}],"text":"弹性(Elasticity)"},{"type":"text","text":"(或者说扩展性)是Kubernetes从一开始就考虑到的问题,例如,如果运行"},{"type":"codeinline","content":[{"type":"text","text":"kubectl scale deployment myservice --replicas=5"}]},{"type":"text","text":"命令的话,myservice deployment就会扩展至五个副本或实例。Kubernetes平台会负责寻找合适的节点、部署服务并维持所需数量的副本一直处于运行状态。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"但是,剩余的微服务特性该怎么处理呢?Kubernetes只涵盖了其中的三个,那么我们该如何实现剩余的哪些呢?"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"在本系列的"},{"type":"link","attrs":{"href":"https:\/\/www.infoq.cn\/article\/MBAYoWrjCOAJK5QuqbcQ","title":"","type":null},"content":[{"type":"text","text":"第一篇文章"}]},{"type":"text","text":"中,我介绍了一种实现它们的方式,那就是使用Java将它们嵌入到服务内部。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"在代码内部实现横切性关注点的服务如下图所示:"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https:\/\/imgopt.infoq.com\/fit-in\/1200x2400\/filters:quality(80)\/filters:no_upscale()\/articles\/microservicilities-istio\/en\/resources\/14image008-1624026324115.jpg","alt":null,"title":null,"style":[{"key":"width","value":"75%"},{"key":"bordertype","value":"none"}],"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"正如在前面的文章中所阐述的那样,这种方式能够正常运行并且具有很多的优势,但是它也有一些缺点。我们介绍主要的几个问题:"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"bulletedlist","content":[{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"服务的基础代码变成了业务逻辑(会给公司带来价值)和基础设施代码(微服务所需)的混合体。"}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"微服务架构中的服务可能会使用不同的语言开发,比如服务A使用Java语言,服务B使用Go语言。多语言服务所带来的挑战在于学习如何为每种语言实现这些微服务特性。例如,在Java中使用哪个库来实现回弹性,在Go中使用哪个库等等。"}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"就Java来讲,对于每个微服务特性来讲,我们可能都会添加新的库(及其所有的传递性依赖),例如,为了实现回弹性引入"},{"type":"link","attrs":{"href":"https:\/\/github.com\/resilience4j\/resilience4j","title":"","type":null},"content":[{"type":"text","text":"Resiliency4J"}]},{"type":"text","text":"、为了实现跟踪引入"},{"type":"link","attrs":{"href":"https:\/\/github.com\/jaegertracing\/jaeger-client-java","title":"","type":null},"content":[{"type":"text","text":"Jaeger"}]},{"type":"text","text":"或者为了实现监控引入"},{"type":"link","attrs":{"href":"https:\/\/micrometer.io\/","title":"","type":null},"content":[{"type":"text","text":"Micrometer"}]},{"type":"text","text":"。尽管这么做没有什么问题,但是我们在类路径下加入不同种类的库的过程中,会增加类路径冲突的机率。除此之外,内存消耗和启动时间也会随之增加。最后同样重要的是,在所有的Java服务之间维护这些库的版本也是一个问题,我们要让它们保持相同的版本。"}]}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"归根到底,我们可能会想,为什么需要实现这些微服务特性呢?"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"在微服务架构中,应用程序是由相互连接的多个服务组成的,所有的服务相互协作以生成我们所需的业务功能。这些服务都是使用网络互相连接在一起的,所以实际上我们实现了一个分布式计算的模型。由于它是分布式的,可观察性(监控、跟踪、日志)就变得有些复杂了,因为所有的数据分散在多个服务中。因为网络是不可靠的,或者网络延迟不可能为零,所以服务需要在面临故障的时候具备回弹性。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"因此,我们可以假定之所以需要微服务特性,是因为在基础设施层(我们需要使用网络的分布式服务通信,而不是单体)所做的决定。那么我们为什么要在应用层面实现这些微服务特性,而不是在基础设施层面实现呢?问题就在这里,这个问题有一个很简单的答案,那就是"},{"type":"text","marks":[{"type":"strong"}],"text":"服务网格"},{"type":"text","text":"。"}]},{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","text":"什么是服务网格和Istio?"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"服务网格是一个专用的基础设施层,目的在于使得服务与服务之间的通信变得安全、快速和可靠。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"服务网格通常以轻量级网络代理的形式实现并且会与服务代码部署在一起,它会拦截服务所有进站\/出站的网络流量。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"link","attrs":{"href":"https:\/\/istio.io\/","title":"","type":null},"content":[{"type":"text","text":"Istio"}]},{"type":"text","text":"是一个适用于Kubernetes的开源服务网格实现。Istio采用的策略是集成一个网络流量代理到Kubernetes Pod中,而这个过程是借助"},{"type":"link","attrs":{"href":"https:\/\/istio.io\/latest\/docs\/setup\/additional-setup\/sidecar-injection\/","title":"","type":null},"content":[{"type":"text","text":"sidecar容器"}]},{"type":"text","text":"实现的。sidecar容器与服务容器运行在同一个Pod中。因为它们运行在系统的Pod之中,所以两个容器会共享IP、生命周期、资源、网络和存储。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https:\/\/imgopt.infoq.com\/fit-in\/1200x2400\/filters:quality(80)\/filters:no_upscale()\/articles\/microservicilities-istio\/en\/resources\/11image010-1624026324115.jpg","alt":null,"title":null,"style":[{"key":"width","value":"75%"},{"key":"bordertype","value":"none"}],"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"Istio使用"},{"type":"link","attrs":{"href":"https:\/\/www.envoyproxy.io\/","title":"","type":null},"content":[{"type":"text","text":"Envoy Proxy"}]},{"type":"text","text":"作为sidecar容器中的网络代理,并且会配置Pod通过Envoy代理(sidecar容器)发送所有的入站\/出站流量。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"在使用Istio的时候,服务之间的通信并不是直接进行的,而是通过sidecar容器(即Envoy)进行的,当服务A请求服务B的时候,请求会通过服务A的DNS发送到它的代理容器上。随后,服务A的代理容器会发送请求至服务B的代理容器,代理容器最终会调用真正的服务B。响应过程则会遵循完全相反的路径。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https:\/\/imgopt.infoq.com\/fit-in\/1200x2400\/filters:quality(80)\/filters:no_upscale()\/articles\/microservicilities-istio\/en\/resources\/9image012-1624026324115.jpg","alt":null,"title":null,"style":[{"key":"width","value":"75%"},{"key":"bordertype","value":"none"}],"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"Envoy代理的sidecar容器实现了如下的特性:"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"bulletedlist","content":[{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"智能路由和跨服务的负载均衡。"}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"故障注入。"}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"回弹性:重试和断路器。"}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"可观察性和遥测:指标与跟踪。"}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"安全性:加密和授权。"}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"全局范围(fleet-wide)的策略执行。"}]}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"通过下图我们可以看出,sidecar容器实现的特性能够非常好地匹配五个微服务特性:服务发现、回弹性、认证、监控和跟踪。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https:\/\/imgopt.infoq.com\/fit-in\/1200x2400\/filters:quality(80)\/filters:no_upscale()\/articles\/microservicilities-istio\/en\/resources\/9image014-1624026324115.jpg","alt":null,"title":null,"style":[{"key":"width","value":"75%"},{"key":"bordertype","value":"none"}],"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"在容器中实现微服务特性的逻辑有如下几个好处:"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"bulletedlist","content":[{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"业务代码与微服务特性完全隔离。"}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"所有的服务会使用完全相同的实现,因为它们使用的是同一个容器。"}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"它的代码是独立的。服务可以使用任意语言实现,但是这些横切性的关注点始终是相同的。"}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"所有服务的配置过程和参数是相同的。"}]}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"但是,Istio内部是如何运行的,我们为什么需要Istio,而不是直接使用Envoy代理呢?"}]},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"架构"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"Envoy代理是一个轻量级的网络代理,它可以单独使用,但是如果有十个服务要部署的话,我们就需要配置十个Envoy代理。这个过程会变得有一些复杂和繁琐。Istio简化了这一过程。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"从架构上来讲,Istio服务网格是由数据平面(data plane)和控制平面(control plane)组成的。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong"}],"text":"数据平面"},{"type":"text","text":"是由以sidecar形式部署的Envoy代理组成的。这个代理会拦截所有网络之间的通信。它还会收集和报告所有网格流量的遥测数据。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong"}],"text":"控制平面"},{"type":"text","text":"负责管理和配置Envoy代理。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"下图描述了这两个组件:"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https:\/\/imgopt.infoq.com\/fit-in\/1200x2400\/filters:quality(80)\/filters:no_upscale()\/articles\/microservicilities-istio\/en\/resources\/8image016-1624026324115.jpg","alt":null,"title":null,"style":[{"key":"width","value":"75%"},{"key":"bordertype","value":"none"}],"href":null,"fromPaste":true,"pastePass":true}},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"安装Istio"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"我们需要一个安装Istio的Kubernetes集群。就本文来讲,我们会使用"},{"type":"link","attrs":{"href":"https:\/\/minikube.sigs.k8s.io\/docs\/start\/","title":"","type":null},"content":[{"type":"text","text":"Minikube"}]},{"type":"text","text":",但是任意其他的Kubernetes集群都是可以的。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"运行如下的命令来启动集群:"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"text"},"content":[{"type":"text","text":"minikube start -p istio --kubernetes-version='v1.19.0' --vm-driver='virtualbox' --memory=4096\n\n [istio] minikube v1.17.1 on Darwin 11.3\n Kubernetes 1.20.2 is now available. If you would like to upgrade, specify: --kubernetes-version=v1.20.2\n minikube 1.19.0 is available! Download it: https:\/\/github.com\/kubernetes\/minikube\/releases\/tag\/v1.19.0\n To disable this notice, run: 'minikube config set WantUpdateNotification false'\n"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"✨  基于已有的profile并使用virtualbox驱动"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"❗  对于既有的minikube集群,我们无法改变它的内存大小。如果需要的话,请先将该集群删除掉。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"text"},"content":[{"type":"text","text":" Starting control plane node istio in cluster istio\n Restarting existing virtualbox VM for \"istio\" ...\n Preparing Kubernetes v1.19.0 on Docker 19.03.12 ...\n Verifying Kubernetes components...\n Enabled addons: storage-provisioner, default-storageclass\n Done! kubectl is now configured to use \"istio\" cluster and \"\" namespace by default\n"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"Kubernetes集群运行起来之后,我们就可以下载"},{"type":"codeinline","content":[{"type":"text","text":"istioctl "}]},{"type":"text","text":"CLI工具来安装Istio到集群中了。在本例中,我们会从"},{"type":"link","attrs":{"href":"https:\/\/github.com\/istio\/istio\/releases\/tag\/1.9.4","title":"","type":null},"content":[{"type":"text","text":"版本发布"}]},{"type":"text","text":"页面下载Istio 1.9.4。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"codeinline","content":[{"type":"text","text":"istioctl"}]},{"type":"text","text":"工具安装完成之后,我们就可以将Istio部署到集群之中了。Istio自带了不同的"},{"type":"link","attrs":{"href":"https:\/\/istio.io\/latest\/docs\/setup\/additional-setup\/config-profiles\/","title":"","type":null},"content":[{"type":"text","text":"profiles"}]},{"type":"text","text":",但是就开始学习Istio而言,demo profile是最合适的。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"codeinline","content":[{"type":"text","text":"istioctl install --set profile=demo -y"}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"codeinline","content":[{"type":"text","text":"Detected that your cluster does not support third party JWT authentication. Falling back to less secure first party JWT. See https:\/\/istio.io\/docs\/ops\/best-practices\/security\/#configure-third-party-service-account-tokens for details."}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"✔ Istio core installed"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"✔ Istiod installed"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"✔ Egress gateways installed"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"✔ Ingress gateways installed"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"✔ Addons installed"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"✔ Installation complete"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"我们要一直等到"},{"type":"codeinline","content":[{"type":"text","text":"istio-system"}]},{"type":"text","text":"命名空间中的所有Pod均处于running状态。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"text"},"content":[{"type":"text","text":"kubectl get pods -n istio-system\n\nNAME READY STATUS RESTARTS AGE\ngrafana-b54bb57b9-fj6qk 1\/1 Running 2 171d\nistio-egressgateway-68587b7b8b-m5b58 1\/1 Running 2 171d\nistio-ingressgateway-55bdff67f-jrhpk 1\/1 Running 2 171d\nistio-tracing-9dd6c4f7c-9gcx9 1\/1 Running 3 171d\nistiod-76bf8475c-xphgd 1\/1 Running 2 171d\nkiali-d45468dc4-4nbl4 1\/1 Running 2 171d\nprometheus-74d44d84db-86hdr 2\/2 Running 4 171d\n"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"为了发挥Istio的所有功能,网格中的Pod必须运行一个Istio sidecar代理。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"我们有两种方式将Istio sidecar注入到Pod中:使用"},{"type":"codeinline","content":[{"type":"text","text":"istioctl"}]},{"type":"text","text":"命令手动注入或者在将Pod部署到配置好的命名空间时自动注入。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"为了简单起见,我们通过执行如下命令,为"},{"type":"codeinline","content":[{"type":"text","text":"default"}]},{"type":"text","text":"命名空间配置默认的自动化sidecar注入:"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"codeinline","content":[{"type":"text","text":"kubectl label namespace default istio-injection=enabled"}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"codeinline","content":[{"type":"text","text":"namespace\/default labeled"}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"现在,Istio已经安装到了Kubernetes集群中,并且为在"},{"type":"codeinline","content":[{"type":"text","text":"default"}]},{"type":"text","text":"命名空间使用做好了准备。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"在下面的章节中,我们将会看到如何“Istio化”应用并部署一个这样的应用。"}]},{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","text":"应用概览"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"应用是由两个服务组成的,分别是book service和rating service。Book service返回一本图书的信息及其评分。Rating service返回给定图书的评分。我们有rating service的两个版本:v1会为所有的图书返回一个固定的评分(也就是1),而v2会返回一个随机的评分值。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https:\/\/imgopt.infoq.com\/fit-in\/1200x2400\/filters:quality(80)\/filters:no_upscale()\/articles\/microservicilities-istio\/en\/resources\/7image018-1624026735202.jpg","alt":null,"title":null,"style":[{"key":"width","value":"75%"},{"key":"bordertype","value":"none"}],"href":null,"fromPaste":true,"pastePass":true}},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"部署"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"因为已经启用了sidecar注入,我们不需要对Kubernetes部署文件做任何变更。接下来,我们将这三个服务部署到“Istio化”的命名空间中。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"举例来说,_book service_的部署文件如下所示:"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"text"},"content":[{"type":"text","text":"---\napiVersion: v1\nkind: Service\nmetadata:\n labels:\n app.kubernetes.io\/name: book-service\n app.kubernetes.io\/version: v1.0.0\n name: book-service\nspec:\n ports:\n - name: http\n port: 8080\n targetPort: 8080\n selector:\n app.kubernetes.io\/name: book-service\n app.kubernetes.io\/version: v1.0.0\n type: LoadBalancer\n---\napiVersion: apps\/v1\nkind: Deployment\nmetadata:\n labels:\n app.kubernetes.io\/name: book-service\n app.kubernetes.io\/version: v1.0.0\n name: book-service\nspec:\n replicas: 1\n selector:\n matchLabels:\n app.kubernetes.io\/name: book-service\n app.kubernetes.io\/version: v1.0.0\n template:\n metadata:\n labels:\n app.kubernetes.io\/name: book-service\n app.kubernetes.io\/version: v1.0.0\n spec:\n containers:\n - env:\n - name: KUBERNETES_NAMESPACE\n valueFrom:\n fieldRef:\n fieldPath: metadata.namespace\n image: quay.io\/lordofthejars\/book-service:v1.0.0\n imagePullPolicy: Always\n name: book-service\n ports:\n - containerPort: 8080\n name: http\n protocol: TCP\n"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"我们可以看到,在文件中既没有Istio相关的内容,也没有sidecar容器的配置。Istio功能的注入默认会自动进行。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"我们把应用部署到Kubernetes集群中:"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"text"},"content":[{"type":"text","text":"kubectl apply -f rating-service\/src\/main\/kubernetes\/service.yml -n default\nkubectl apply -f rating-service\/src\/main\/kubernetes\/deployment-v1.yml -n default\nkubectl apply -f rating-service\/src\/main\/kubernetes\/deployment-v2.yml -n default\nkubectl apply -f book-service\/src\/main\/kubernetes\/deployment.yml -n default\n"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"几秒钟之后,应用就会启动起来了。为了进行校验,我们运行如下的命令并观察Pod所拥有的容器数量:"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"text"},"content":[{"type":"text","text":"kubectl get pods -n default\n\nNAME READY STATUS RESTARTS AGE\nbook-service-5cc59cdcfd-5qhb2 2\/2 Running 0 79m\nrating-service-v1-64b67cd8d-5bfpf 2\/2 Running 0 63m\nrating-service-v2-66b55746d-f4hpl 2\/2 Running 0 63m\n"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"注意,每个Pod都包含了两个正在运行的容器,其中一个是服务本身,另外一个是Istio代理。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"如果描述这个Pod的话,我们会发现:"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"text"},"content":[{"type":"text","text":"kubectl describe pod rating-service-v2-66b55746d-f4hpl\n\nName: rating-service-v2-66b55746d-f4hpl\nNamespace: default\n…\nContainers:\n rating-service:\n Container ID: docker:\/\/cda8d72194ee37e146df7bf0a6b23a184b5bfdb36fed00d2cc105daf6f0d6e85\n Image: quay.io\/lordofthejars\/rating-service:v2.0.0\n…\n istio-proxy:\n Container ID: docker:\/\/7f4a9c1f425ea3a06ccba58c74b2c9c3c72e58f1d805f86aace3d914781e0372\n Image: docker.io\/istio\/proxyv2:1.6.13\n"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"因为我们使用了Minikube并且Kubernetes服务是LoadBalancer类型,所以要访问应用需要Minikube的 IP和服务端口。为了找到这些值,可以执行如下命令:"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"text"},"content":[{"type":"text","text":"minikube IP -p istio\n192.168.99.116\n\nkubectl get services -n default\nNAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE\nbook-service LoadBalancer 10.106.237.42 <pending> 8080:31304\/TCP 111m\nkubernetes ClusterIP 10.96.0.1 <none> 443\/TCP 132m\nrating LoadBalancer 10.109.106.128 <pending> 8080:31216\/TCP 95m\n"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"接下来,我们可以对服务执行curl命令:"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"text"},"content":[{"type":"text","text":"curl 192.168.99.116:31304\/book\/1\n{\"bookId\":1,\"name\":\"Book 1\",\"rating\":1}\ncurl 192.168.99.116:31304\/book\/1\n{\"bookId\":1,\"name\":\"Book 1\",\"rating\":3}\ncurl 192.168.99.116:31304\/book\/1\n{\"bookId\":1,\"name\":\"Book 1\",\"rating\":1}\ncurl 192.168.99.116:31304\/book\/1\n{\"bookId\":1,\"name\":\"Book 1\",\"rating\":3}\n"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"从输出我们可以看到评分值的变化,也就是对于同一个图书的id,评分值会在1和3之间变化。默认情况下,Istio会使用round-robin方式平衡对服务的调用。在本例中,请求会在"},{"type":"codeinline","content":[{"type":"text","text":"rating:v1"}]},{"type":"text","text":"(返回固定的评分值1)和"},{"type":"codeinline","content":[{"type":"text","text":"rating:v2"}]},{"type":"text","text":"(在启动的时候进行随机的评分计算,在本例中,会对ID为1的图书返回3)之间进行平衡。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"应用现在已经部署好了,并且实现了“Istio化”,但是到目前为止还没有启用任何的微服务特性。我们首先来创建一些Istio资源,以便于在Istio代理容器上启用和配置微服务特性。"}]},{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","text":"Istio微服务特性"}]},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"服务发现"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"italic"}],"text":"Kubernetes Service_实现了服务发现的理念。它提供了一种方式将一组_Kubernetes Pod"},{"type":"text","text":"(作为一个整体)赋予一个稳定的虚拟IP和DNS名。Pod在访问其他Pod的时候,可以使用_Kubernetes Service_名作为主机名。这只能允许我们实现基本的服务发现策略,但是我们可能会需要更高级的发现\/部署策略,比如金丝雀发布、灰度发布或者镜像流量(shadowing traffic),此时Kubernetes Service就爱莫能助了。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"Istio能够让我们很容易地控制服务之间的网络流量,这是通过两个概念来实现的,即"},{"type":"codeinline","content":[{"type":"text","text":"DestinationRule"}]},{"type":"text","text":"和"},{"type":"codeinline","content":[{"type":"text","text":"VirtualService"}]},{"type":"text","text":"。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"codeinline","content":[{"type":"text","text":"DestinationRule"}]},{"type":"text","text":"定义了在路由发生之后如何为网络流量提供服务的策略。在destination rule中我们可以配置的内容如下所示:"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"bulletedlist","content":[{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"网络流量策略"}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"负载均衡策略"}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"连接池设置"}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"mTLS"}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"回弹性"}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"使用标签(label)指定服务的子集(subset),这些子集会在"},{"type":"codeinline","content":[{"type":"text","text":"VirtualService"}]},{"type":"text","text":"中用到。"}]}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"我们创建一个名为"},{"type":"codeinline","content":[{"type":"text","text":"destination-rule-v1-v2.yml"}]},{"type":"text","text":"的文件来注册两个子集,其中一个用于_rating service v1_,另外一个用于_rating service v2_:"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"text"},"content":[{"type":"text","text":"apiVersion: networking.istio.io\/v1alpha3\nkind: DestinationRule\nmetadata:\n name: rating\nspec:\n host: rating\n subsets:\n - labels:\n app.kubernetes.io\/version: v1.0.0\n name: version-v1\n - labels:\n app.kubernetes.io\/version: v2.0.0\n name: version-v2\n"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"在这里,我们将"},{"type":"codeinline","content":[{"type":"text","text":"host"}]},{"type":"text","text":"字段设置为rating,因为这是在_Kubernetes Service_中定义的DNS名。随后,在"},{"type":"codeinline","content":[{"type":"text","text":"subsets"}]},{"type":"text","text":"部分,我们以"},{"type":"codeinline","content":[{"type":"text","text":"labels"}]},{"type":"text","text":"集的形式定义了多个子集,并将它们分组到一个“虚拟的”"},{"type":"codeinline","content":[{"type":"text","text":"name"}]},{"type":"text","text":"。例如,在前面的例子中,我们定义了两个组,其中一个组用于rating service的version 1,另外一个组用于version 2。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"text"},"content":[{"type":"text","text":"kubectl apply -f src\/main\/kubernetes\/destination-rule-v1-v2.yml -n default\ndestinationrule.networking.istio.io\/rating created\n"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"codeinline","content":[{"type":"text","text":"VirtualService"}]},{"type":"text","text":"能够让我们配置请求该如何路由至Istio服务网格的服务中。借助virtual service,实现像A\/B测试、蓝\/绿部署、金丝雀发布或灰度发布这样的策略就会变得非常简单。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"我们创建一个名为"},{"type":"codeinline","content":[{"type":"text","text":"virtual-service-v1.yml"}]},{"type":"text","text":"的文件以发送所有的流量到v1:"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"text"},"content":[{"type":"text","text":"apiVersion: networking.istio.io\/v1alpha3\nkind: VirtualService\nmetadata:\n name: rating\nspec:\n hosts:\n - rating\n http:\n - route:\n - destination:\n host: rating\n subset: version-v1\n weight: 100\n"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"在前面的文件中,我们配置所有到达rating主机的请求都会被发送到version-v1子集所属的Pod中。我们需要记住,子集是在"},{"type":"codeinline","content":[{"type":"text","text":"DestinationRule"}]},{"type":"text","text":"文件中创建的。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"text"},"content":[{"type":"text","text":"kubectl apply -f src\/main\/kubernetes\/virtual-service-v1.yml -n default\nvirtualservice.networking.istio.io\/rating created\n"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"现在,我们可以再次向服务执行一些"},{"type":"codeinline","content":[{"type":"text","text":"curl"}]},{"type":"text","text":"命令,但是在输出方面最大的差异在于所有的请求都发送到了_rating v1_中。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"text"},"content":[{"type":"text","text":"curl 192.168.99.116:31304\/book\/1\n{\"bookId\":1,\"name\":\"Book 1\",\"rating\":1}\ncurl 192.168.99.116:31304\/book\/1\n{\"bookId\":1,\"name\":\"Book 1\",\"rating\":1}\ncurl 192.168.99.116:31304\/book\/1\n{\"bookId\":1,\"name\":\"Book 1\",\"rating\":1}\n"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"显然,我们可以创建另外一个virtual service文件,使其指向rating v2:"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"text"},"content":[{"type":"text","text":"apiVersion: networking.istio.io\/v1alpha3\nkind: VirtualService\nmetadata:\n name: rating\nspec:\n hosts:\n - rating\n http:\n - route:\n - destination:\n host: rating\n subset: version-v2\n weight: 100\n"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"text"},"content":[{"type":"text","text":"kubectl apply -f src\/main\/kubernetes\/virtual-service-v2.yml -n default\nvirtualservice.networking.istio.io\/rating configured\n"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"这样,所有的流量会发送至_rating_ v2:"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"text"},"content":[{"type":"text","text":"curl 192.168.99.116:31304\/book\/1\n{\"bookId\":1,\"name\":\"Book 1\",\"rating\":3}\ncurl 192.168.99.116:31304\/book\/1\n{\"bookId\":1,\"name\":\"Book 1\",\"rating\":3}\n"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"现在,"},{"type":"codeinline","content":[{"type":"text","text":"rating"}]},{"type":"text","text":"字段没有被设置为1,这是因为所有的请求都被version 2处理了。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"通过修改virtual service的"},{"type":"codeinline","content":[{"type":"text","text":"weight"}]},{"type":"text","text":"字段,我们就能实现"},{"type":"link","attrs":{"href":"https:\/\/www.getambassador.io\/docs\/edge-stack\/latest\/topics\/using\/canary\/","title":"","type":null},"content":[{"type":"text","text":"金丝雀发布"}]},{"type":"text","text":"。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"text"},"content":[{"type":"text","text":"apiVersion: networking.istio.io\/v1alpha3\nkind: VirtualService\nmetadata:\n name: rating\nspec:\n hosts:\n - rating\n http:\n - route:\n - destination:\n host: rating\n subset: version-v1\n weight: 75\n - destination:\n host: rating\n subset: version-v2\n weight: 25\n"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"text"},"content":[{"type":"text","text":"kubectl apply -f src\/main\/kubernetes\/virtual-service-v1-v2-75-25.yml -n default\nvirtualservice.networking.istio.io\/rating configured\n"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"现在,我们对应用执行一些"},{"type":"codeinline","content":[{"type":"text","text":"curl"}]},{"type":"text","text":"命令:"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"text"},"content":[{"type":"text","text":"curl 192.168.99.116:31304\/book\/1\n{\"bookId\":1,\"name\":\"Book 1\",\"rating\":1}\ncurl 192.168.99.116:31304\/book\/1\n{\"bookId\":1,\"name\":\"Book 1\",\"rating\":1}\ncurl 192.168.99.116:31304\/book\/1\n{\"bookId\":1,\"name\":\"Book 1\",\"rating\":1}\ncurl 192.168.99.116:31304\/book\/1\n{\"bookId\":1,\"name\":\"Book 1\",\"rating\":3}\n"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"rating v1的访问次数要比rating v2更多,这遵循了在"},{"type":"codeinline","content":[{"type":"text","text":"weight"}]},{"type":"text","text":"字段中设置的占比。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"现在,我们移除virtual service资源,使其回到默认的行为(也就是round-robin策略):"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"text"},"content":[{"type":"text","text":"kubectl delete -f src\/main\/kubernetes\/virtual-service-v1-v2-75-25.yml -n default\nvirtualservice.networking.istio.io \"rating\" deleted\n"}]},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"回弹性"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"在微服务架构中,我们在开发时要始终考虑到可能出现的故障,在与其他的服务进行通信时更是如此。在单体应用中,我们的应用会作为一个整体,要么全部处于可用状态,要么全部处于宕机状态,但是在微服务架构中,情况却并非如此,因为有些服务是可用的,而另外一些则可能已经宕机了。回弹性(或称为应用回弹性)是指一个应用\/服务能够对面临的问题作出反应的能力,在出现问题的时候,依然能够提供尽可能最好的结果。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"接下来我们看一下Istio如何帮助我们实现回弹性策略,以及如何配置它们。"}]},{"type":"heading","attrs":{"align":null,"level":4},"content":[{"type":"text","text":"故障"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"rating service实现了一个特殊的端点,当它被访问后会导致服务开始返回503 HTTP错误码。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"执行如下的命令(将Pod名替换为你自己的),使服务rating v2在访问的时候开始出现故障::"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"text"},"content":[{"type":"text","text":"kubectl get pods -n default\n\nNAME READY STATUS RESTARTS AGE\nbook-service-5cc59cdcfd-5qhb2 2\/2 Running 4 47h\nrating-service-v1-64b67cd8d-5bfpf 2\/2 Running 4 47h\nrating-service-v2-66b55746d-f4hpl 2\/2 Running 4 47h\n\nkubectl exec -ti rating-service-v2-66b55746d-f4hpl -c rating-service -n default curl localhost:8080\/rate\/misbehave\n\nRatings endpoint returns 503 error.\n"}]},{"type":"heading","attrs":{"align":null,"level":4},"content":[{"type":"text","text":"重试"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"目前,Istio配置为没有virtual service,这意味着它会在两个版本之间平衡请求。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"我们发送一些请求并校验rating v2会失败:"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"text"},"content":[{"type":"text","text":"curl 192.168.99.116:31304\/book\/1\n{\"bookId\":1,\"name\":\"Book 1\",\"rating\":1}\n\ncurl 192.168.99.116:31304\/book\/1\n\ncurl 192.168.99.116:31304\/book\/1\n{\"bookId\":1,\"name\":\"Book 1\",\"rating\":1}\n"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"其中有个请求没有产生响应,这是因为_rating v2_没有返回合法的响应,而是产生了错误。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"Istio支持重试,这是通过在"},{"type":"codeinline","content":[{"type":"text","text":"VirtualService"}]},{"type":"text","text":"资源中进行配置实现的。创建名为"},{"type":"codeinline","content":[{"type":"text","text":"virutal-service-retry.yml"}]},{"type":"text","text":"的文件,其内容如下所示:"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"text"},"content":[{"type":"text","text":"apiVersion: networking.istio.io\/v1alpha3\nkind: VirtualService\nmetadata:\n name: rating\nspec:\n hosts:\n - rating\n http:\n - route:\n - destination:\n host: rating\n retries:\n attempts: 2\n perTryTimeout: 5s\n retryOn: 5xx\n"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"按照配置,如果rating service(不管哪个版本)返回"},{"type":"codeinline","content":[{"type":"text","text":"5XX"}]},{"type":"text","text":" HTTP错误码的话,会自动进行两次重试。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"text"},"content":[{"type":"text","text":"kubectl apply -f src\/main\/kubernetes\/virtua-service-retry.yml -n default\nvirtualservice.networking.istio.io\/rating created\n"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"接下来,我们发送一些请求并检查输出:"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"text"},"content":[{"type":"text","text":"curl 192.168.99.116:31304\/book\/1\n{\"bookId\":1,\"name\":\"Book 1\",\"rating\":1}\n\ncurl 192.168.99.116:31304\/book\/1\n{\"bookId\":1,\"name\":\"Book 1\",\"rating\":1}\n\ncurl 192.168.99.116:31304\/book\/1\n{\"bookId\":1,\"name\":\"Book 1\",\"rating\":1}\n"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"现在,我们可以看到,所有的请求都是由rating v1响应的。原因很简单,当对rating service的请求发送至v1,会得到一个合法的响应。但是,如果请求被发送到v2的时候,会出现错误并且会自动执行重试。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"因为调用是在两个服务之间进行负载均衡的,所以重试请求会被发送到v1,从而产生一个合法的响应。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"基于这样的原因,上述的所有请求都会返回来自v1的响应。"}]},{"type":"heading","attrs":{"align":null,"level":4},"content":[{"type":"text","text":"断路器"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"对于处理网络故障或偶尔出现的错误来说,自动重试是一个很好的方式,但是如果多个并发用户向一个具有自动重试功能的故障系统发送请求时,会发生什么呢?"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"我们通过使用"},{"type":"link","attrs":{"href":"https:\/\/github.com\/JoeDog\/siege","title":"","type":null},"content":[{"type":"text","text":"Siege"}]},{"type":"text","text":"(一个HTTP负载测试工具)模拟这个场景,但首先,我们使用kubectl命令来探查一下rating v2的日志:"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"text"},"content":[{"type":"text","text":"kubectl get pods -n default\n\nNAME READY STATUS RESTARTS AGE\nbook-service-5cc59cdcfd-5qhb2 2\/2 Running 4 47h\nrating-service-v1-64b67cd8d-5bfpf 2\/2 Running 4 47h\nrating-service-v2-66b55746d-f4hpl 2\/2 Running 4 47h\n\nkubectl logs rating-service-v2-66b55746d-f4hpl -c rating-service -n default\n\n…\nRequest 31\nRequest 32\nRequest 33\nRequest 34\n"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"这些日志行展示了该服务所处理的请求数。目前,该服务处理了34个请求。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"为了模拟四个并发用户,并且每个用户发送十个请求到应用上,我们可以执行如下的siege命令:"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"text"},"content":[{"type":"text","text":"siege -r 10 -c 4 -v -d 1 192.168.99.116:31304\/book\/1\n\nHTTP\/1.1 200 0.04 secs: 39 bytes ==> GET \/book\/1\nHTTP\/1.1 200 0.03 secs: 39 bytes ==> GET \/book\/1\n\nTransactions: 40 hits\nAvailability: 100.00 %\nElapsed time: 0.51 secs\nData transferred: 0.00 MB\nResponse time: 0.05 secs\nTransaction rate: 78.43 trans\/sec\nThroughput: 0.00 MB\/sec\nConcurrency: 3.80\nSuccessful transactions: 40\nFailed transactions: 0\nLongest transaction: 0.13\nShortest transaction: 0.01\n"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"当然,这里没有错误发送给调用者,这是因为有自动重试机制,但是我们再次探测一下rating v2的日志:"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"text"},"content":[{"type":"text","text":"kubectl logs rating-service-v2-66b55746d-f4hpl -c rating-service -n default\n\n…\nRequest 56\nRequest 57\nRequest 58\nRequest 59\n"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"尽管rating v2不能产生一个合法的响应,但是服务依然被访问了25次,这会对应用产生很大的影响,因为:"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"numberedlist","attrs":{"start":1,"normalizeStart":1},"content":[{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":1,"align":null,"origin":null},"content":[{"type":"text","text":"如果服务已经处于过载状态的话,发送更多的请求对它的恢复来讲并不是一个好主意。也许,最好的方式是将实例放到一个隔离区中。"}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":2,"align":null,"origin":null},"content":[{"type":"text","text":"如果服务此时恰好因为某个缺陷出现了故障,那么重试并不会改善这种情况。"}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":3,"align":null,"origin":null},"content":[{"type":"text","text":"对于每次重试,都会建立一个socket、分配一些文件描述符(file descriptor),还要通过网络发送一些数据包,但最终得到的却是故障。这个过程会影响在同一个节点中其他服务(CPU、内存、文件描述符等)或者网络(增加无用的流量、延迟等)。"}]}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"为了解决这个问题,我们需要有一种方式能够在出现重复执行失败的时候,让调用能够自动地快速失败。"},{"type":"link","attrs":{"href":"https:\/\/en.wikipedia.org\/wiki\/Circuit_breaker_design_pattern","title":"","type":null},"content":[{"type":"text","text":"断路器(circuit breaker)"}]},{"type":"text","text":"设计模式和舱壁(bulkhead)模式是这个问题的解决方案。前者提供了在遇到并发错误的时候,快速失败的策略,而后者则能限制并发执行的数量。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"现在,创建一个名为"},{"type":"codeinline","content":[{"type":"text","text":"destination-rule-circuit-breaker.yml"}]},{"type":"text","text":"的文件,内容如下所示:"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"text"},"content":[{"type":"text","text":"apiVersion: networking.istio.io\/v1alpha3\nkind: DestinationRule\nmetadata:\n name: rating\nspec:\n host: rating\n subsets:\n - labels:\n version: v1\n name: version-v1\n - labels:\n version: v2\n name: version-v2\n trafficPolicy:\n connectionPool:\n http:\n http1MaxPendingRequests: 3\n maxRequestsPerConnection: 3\n tcp:\n maxConnections: 3\n outlierDetection:\n baseEjectionTime: 3m\n consecutive5xxErrors: 1\n interval: 1s\n maxEjectionPercent: 100\n"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"我们要注意的第一件事情就是"},{"type":"codeinline","content":[{"type":"text","text":"DestinationRule"}]},{"type":"text","text":"配置了断路器。除了配置断路器之外,子集也需要指定。对并发连接的限制是在"},{"type":"codeinline","content":[{"type":"text","text":"connectionPool"}]},{"type":"text","text":"字段中实现的。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"要配置断路器,我们需要使用"},{"type":"codeinline","content":[{"type":"text","text":"outlierDetection"}]},{"type":"text","text":"。就本例而言,如果在一秒钟的时间窗口中发生了一次错误,断路器将会打开,使服务暂时跳闸(trip)三分钟。在这个时间之后,断路器会处于半开状态,这意味着会执行真实的逻辑。如果再次失败的话,断路器会保持打开的状态,否则的话,它将会关闭。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"text"},"content":[{"type":"text","text":"kubectl apply -f src\/main\/kubernetes\/destination-rule-circuit-breaker.yml\ndestinationrule.networking.istio.io\/rating configured\n"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"我们已经在Istio中配置完了断路器模式,接下来,我们再次执行"},{"type":"codeinline","content":[{"type":"text","text":"siege"}]},{"type":"text","text":"命令并探查_rating v2_ v2的日志。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"text"},"content":[{"type":"text","text":"siege -r 10 -c 4 -v -d 1 192.168.99.116:31304\/book\/1\n\nHTTP\/1.1 200 0.04 secs: 39 bytes ==> GET \/book\/1\nHTTP\/1.1 200 0.03 secs: 39 bytes ==> GET \/book\/1\n\nTransactions: 40 hits\nAvailability: 100.00 %\n"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"再次探查日志。注意,在前面的运行中,我们已经到了Request 59。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"text"},"content":[{"type":"text","text":"kubectl logs rating-service-v2-66b55746d-f4hpl -c rating-service -n default\n\n…\nRequest 56\nRequest 57\nRequest 58\nRequest 59\nRequest 60\n"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"_Rating v2 _只接收到了一个请求,因为在第一次处理请求的时候返回了错误,断路器就会打开,因此不会有更多的请求发送到rating v2上。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"现在,我们已经看到了如何使用Istio实现回弹性。在这里,我们并没有在服务中实现相关的逻辑,将其与业务逻辑混在一起,而是让sidecar容器实现了这些逻辑。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"最后,执行如下的命令,让rating v2服务回到之前的状态。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"text"},"content":[{"type":"text","text":"kubectl exec -ti rating-service-v2-66b55746d-f4hpl -c rating-service curl localhost:8080\/rate\/behave\nBack to normal\n"}]},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"认证"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"在实现微服务架构的时候,我们可能会发现的一个问题就是如何保护内部服务之间的通信。我们是不是要使用mTLS?是不是要对请求进行认证?是否要对请求进行鉴权?所有这些问题的答案都是肯定的!接下来,我们将会一步一步地看一下Istio是如何帮助我们实现这些功能的。"}]},{"type":"heading","attrs":{"align":null,"level":4},"content":[{"type":"text","text":"认证"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"Istio会自动将代理和工作负载之间的所有网络流量升级为mTLS,这个过程不需要修改任何的服务代码。与此同时,作为开发人员,我们会使用HTTP协议实现服务。当服务被“Istio化”的时候,服务之间的通信会采用HTTPS。Istio会负责管理证书,担任证书颁发机构并撤销\/更新证书。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"要校验mTLS是否已启用,我们可以使用istioctl工具执行如下的命令:"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"text"},"content":[{"type":"text","text":"istioctl experimental authz check book-service-5cc59cdcfd-5qhb2 -a\n\nLISTENER[FilterChain] HTTP ROUTE ALPN mTLS (MODE) AuthZ (RULES)\n\n...\nvirtualInbound[5] inbound|8080|http|book-service.default.svc.cluster.local istio,istio-http\/1.0,istio-http\/1.1,istio-h2 noneSDS: default yes (PERMISSIVE) no (none)\n\n…\n"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"book-service托管在了8080端口,并且以permissive策略配置了mTLS。"}]},{"type":"heading","attrs":{"align":null,"level":4},"content":[{"type":"text","text":"授权"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"接下来,我们看一下如何使用JSON Web Token("},{"type":"link","attrs":{"href":"https:\/\/jwt.io\/","title":"","type":null},"content":[{"type":"text","text":"JWT"}]},{"type":"text","text":")格式启用Istio的终端用户认证。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"我们要做的第一件事情是应用一个"},{"type":"codeinline","content":[{"type":"text","text":"RequestAuthentication"}]},{"type":"text","text":"资源。这个策略能够确保如果"},{"type":"codeinline","content":[{"type":"text","text":"Authorization"}]},{"type":"text","text":"头信息包含JWT token的话,它必须是合法的、没有过期的、由正确的用户颁发的并且没有被篡改。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"text"},"content":[{"type":"text","text":"apiVersion: \"security.istio.io\/v1beta1\"\nkind: \"RequestAuthentication\"\nmetadata:\n name: \"bookjwt\"\n namespace: default\nspec:\n selector:\n matchLabels:\n app.kubernetes.io\/name: book-service\n jwtRules:\n - issuer: \"[email protected]\"\n jwksUri: \"https:\/\/gist.githubusercontent.com\/lordofthejars\/7dad589384612d7a6e18398ac0f10065\/raw\/ea0f8e7b729fb1df25d4dc60bf17dee409aad204\/jwks.json\"\n"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"其中的关键字段包括:"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"bulletedlist","content":[{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"codeinline","content":[{"type":"text","text":"issuer"}]},{"type":"text","text":":token的合法颁发者。如果所提供的token没有在"},{"type":"codeinline","content":[{"type":"text","text":"iss"}]},{"type":"text","text":" JWT字段指定该颁发者,那么这个token就是非法的。"}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"codeinline","content":[{"type":"text","text":"jwksUri"}]},{"type":"text","text":":"},{"type":"codeinline","content":[{"type":"text","text":"jwks"}]},{"type":"text","text":"文件的URL,它指定了公钥注册的地址,用来校验token的签名。"}]}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"text"},"content":[{"type":"text","text":"kubectl apply -f src\/main\/kubernetes\/request-authentication-jwt.yml -n default\nrequestauthentication.security.istio.io\/bookjwt created\n"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"我们现在使用一个非法的token来运行"},{"type":"codeinline","content":[{"type":"text","text":"curl"}]},{"type":"text","text":"命令:"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"text"},"content":[{"type":"text","text":"curl 192.168.99.116:31304\/book\/1 -H \"Authorization: Bearer eyJhbGciOiJSUzI1NiIsImtpZCI6IkRIRmJwb0lVcXJZOHQyenBBMnFYZkNtcjVWTzVaRXI0UnpIVV8tZW52dlEiLCJ0eXAiOiJKV1QifQ.eyJleHAiOjQ2ODU5ODk3MDAsImZvbyI6ImJhciIsImlhdCI6MTUzMjM4OTcwMCwiaXNzIjoidGVzdGluZ0BzZWN1cmUuaXN0aW8uaW8iLCJzdWIiOiJ0ZXN0aW5nQHNlY3VyZS5pc3Rpby5pbyJ9.CfNnxWP2tcnR9q0vxyxweaF3ovQYHYZl82hAUsn21bwQd9zP7c-LS9qd_vpdLG4Tn1A15NxfCjp5f7QNBUo-KC9PJqYpgGbaXhaGx7bEdFWjcwv3nZzvc7M__ZpaCERdwU7igUmJqYGBYQ51vr2njU9ZimyKkfDe3axcyiBZde7G6dabliUosJvvKOPcKIWPccCgefSj_GNfwIip3-SsFdlR7BtbVUcqR-yv-XOxJ3Uc1MI0tz3uMiiZcyPV7sNCU4KRnemRIMHVOfuvHsU60_GhGbiSFzgPTAa9WTltbnarTbxudb_YEOx12JiwYToeX0DCPb43W1tzIBxgm8NxUU\"\n\nJwt verification fails\n"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"因为token是非法的,所以请求会被拒绝,并返回HTTP\/1.1 401 Unauthorized状态码。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"使用合法的token重复前面的请求:"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"text"},"content":[{"type":"text","text":"curl 192.168.99.116:31304\/book\/1 -H \"Authorization: Bearer eyJhbGciOiJSUzI1NiIsImtpZCI6IkRIRmJwb0lVcXJZOHQyenBBMnFYZkNtcjVWTzVaRXI0UnpIVV8tZW52dlEiLCJ0eXAiOiJKV1QifQ.eyJleHAiOjQ2ODU5ODk3MDAsImZvbyI6ImJhciIsImlhdCI6MTUzMjM4OTcwMCwiaXNzIjoidGVzdGluZ0BzZWN1cmUuaXN0aW8uaW8iLCJzdWIiOiJ0ZXN0aW5nQHNlY3VyZS5pc3Rpby5pbyJ9.CfNnxWP2tcnR9q0vxyxweaF3ovQYHYZl82hAUsn21bwQd9zP7c-LS9qd_vpdLG4Tn1A15NxfCjp5f7QNBUo-KC9PJqYpgGbaXhaGx7bEdFWjcwv3nZzvc7M__ZpaCERdwU7igUmJqYGBYQ51vr2njU9ZimyKkfDe3axcyiBZde7G6dabliUosJvvKOPcKIWPccCgefSj_GNfwIip3-SsFdlR7BtbVUcqR-yv-XOxJ3Uc1MI0tz3uMiiZcyPV7sNCU4KRnemRIMHVOfuvHsU60_GhGbiSFzgPTAa9WTltbnarTbxudb_YEOx12JiwYToeX0DCPb43W1tzIBxgm8NxUg\"\n\n{\"bookId\":1,\"name\":\"Book 1\",\"rating\":3}\n"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"现在,我们可以看到一个合法的响应了,因为此时token是正确的。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"到目前为止,我们只是认证了请求(只需要一个合法的token),其实Istio还支持基于角色访问控制(role-based access control,RBAC)模型的授权。我们接下来创建一个"},{"type":"codeinline","content":[{"type":"text","text":"AuthorizationPolicy"}]},{"type":"text","text":"策略,只允许具有合法JSON Web Token并且claim role设置为customer的请求。创建名为"},{"type":"codeinline","content":[{"type":"text","text":"authorization-policy-jwt.yml"}]},{"type":"text","text":"的文件:"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"text"},"content":[{"type":"text","text":"apiVersion: security.istio.io\/v1beta1\nkind: AuthorizationPolicy\nmetadata:\n name: require-jwt\n namespace: default\nspec:\n selector:\n matchLabels:\n app.kubernetes.io\/name: book-service\n action: ALLOW\n rules:\n - from:\n - source:\n requestPrincipals: [\"[email protected]\/[email protected]\"]\n when:\n - key: request.auth.claims[role]\n values: [\"customer\"]\n"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"text"},"content":[{"type":"text","text":"kubectl apply -f src\/main\/kubernetes\/authorization-policy-jwt.yml\nauthorizationpolicy.security.istio.io\/require-jwt created\n"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"然后执行和上面一样的curl命令:"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"text"},"content":[{"type":"text","text":"curl 192.168.99.116:31304\/book\/1 -H \"Authorization: Bearer eyJhbGciOiJSUzI1NiIsImtpZCI6IkRIRmJwb0lVcXJZOHQyenBBMnFYZkNtcjVWTzVaRXI0UnpIVV8tZW52dlEiLCJ0eXAiOiJKV1QifQ.eyJleHAiOjQ2ODU5ODk3MDAsImZvbyI6ImJhciIsImlhdCI6MTUzMjM4OTcwMCwiaXNzIjoidGVzdGluZ0BzZWN1cmUuaXN0aW8uaW8iLCJzdWIiOiJ0ZXN0aW5nQHNlY3VyZS5pc3Rpby5pbyJ9.CfNnxWP2tcnR9q0vxyxweaF3ovQYHYZl82hAUsn21bwQd9zP7c-LS9qd_vpdLG4Tn1A15NxfCjp5f7QNBUo-KC9PJqYpgGbaXhaGx7bEdFWjcwv3nZzvc7M__ZpaCERdwU7igUmJqYGBYQ51vr2njU9ZimyKkfDe3axcyiBZde7G6dabliUosJvvKOPcKIWPccCgefSj_GNfwIip3-SsFdlR7BtbVUcqR-yv-XOxJ3Uc1MI0tz3uMiiZcyPV7sNCU4KRnemRIMHVOfuvHsU60_GhGbiSFzgPTAa9WTltbnarTbxudb_YEOx12JiwYToeX0DCPb43W1tzIBxgm8NxUg\"\n\nRBAC: access denied\n"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"这一次的响应显然不一样了。尽管token是合法的,但是访问被拒绝了,这是因为token中并没有值为customer的claim role。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"然后,我们使用如下的token:"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https:\/\/imgopt.infoq.com\/fit-in\/1200x2400\/filters:quality(80)\/filters:no_upscale()\/articles\/microservicilities-istio\/en\/resources\/7image020-1624051579986.jpg","alt":null,"title":null,"style":[{"key":"width","value":"75%"},{"key":"bordertype","value":"none"}],"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"codeinline","content":[{"type":"text","text":"curl 192.168.99.116:31304\/book\/1 -H "Authorization: Bearer eyJhbGciOiJSUzI1NiIsImtpZCI6IkRIRmJwb0lVcXJZOHQyenBBMnFYZkNtcjVWTzVaRXI0UnpIVV8tZW52dlEiLCJ0eXAiOiJKV1QifQ.eyJleHAiOjI1NDkwNTY4ODgsImlhdCI6MTU0OTA1Njg4OSwiaXNzIjoidGVzdGluZ0BzZWN1cmUuaXN0aW8uaW8iLCJyb2xlIjoiY3VzdG9tZXIiLCJzdWIiOiJ0ZXN0aW5nQHNlY3VyZS5pc3Rpby5pbyJ9.VM9VOHD2NwDjQ6k7tszB3helfAn5wcldxe950BveiFVg43pp7x5MWTjMtWQRmQc7iYul19PXsmGnSSOiQQobxdn2UnhHJeKeccCdX5YVgX68tR0R9xv_wxeYQWquH3roxHh2Xr2SU3gdt6s7gxKHrW7Zc4Z9bT-fnz3ijRUiyrs-HQN7DBc356eiZy2wS7O539lx3mr-pjM9PQtcDCDOGsnmwq1YdKw9o2VgbesfiHDDjJQlNv40wnsfpq2q4BgSmdsofAGwSNKWtqUE6kU7K2hvV2FvgwjzcB19bbRYMWxRG0gHyqgFy-uM5tsC6Cib-gPAIWxCdXDmLEiqIdjM3w""}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"codeinline","content":[{"type":"text","text":"{\"bookId\":1,\"name\":\"Book 1\",\"rating\":3}"}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"现在,我们看到了一个合法的响应,因为此时token是正确的并且包含了一个合法的role值。"}]},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"可观察性"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"Istio自带了四个组件以适应可观察性的需求:"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"bulletedlist","content":[{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"link","attrs":{"href":"https:\/\/prometheus.io\/","title":"","type":null},"content":[{"type":"text","text":"Prometheus"}]},{"type":"text","text":":用于监控。"}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"link","attrs":{"href":"https:\/\/grafana.com\/","title":"","type":null},"content":[{"type":"text","text":"Grafana"}]},{"type":"text","text":":用于可视化。"}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"link","attrs":{"href":"https:\/\/www.jaegertracing.io\/","title":"","type":null},"content":[{"type":"text","text":"Jaeger"}]},{"type":"text","text":" + "},{"type":"link","attrs":{"href":"https:\/\/zipkin.io\/","title":"","type":null},"content":[{"type":"text","text":"Zipkin"}]},{"type":"text","text":":用于跟踪。"}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"link","attrs":{"href":"https:\/\/kiali.io\/","title":"","type":null},"content":[{"type":"text","text":"Kiali"}]},{"type":"text","text":":用于为应用提供一个全局的概览。"}]}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"我们可以在"},{"type":"codeinline","content":[{"type":"text","text":"istio-system"}]},{"type":"text","text":"命名空间中看到所有的Pod:"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"text"},"content":[{"type":"text","text":"kubectl get pods -n istio-system\nNAME READY STATUS RESTARTS AGE\n\ngrafana-b54bb57b9-k5qbm 1\/1 Running 0 178m\nistio-egressgateway-68587b7b8b-vdr67 1\/1 Running 0 178m\nistio-ingressgateway-55bdff67f-hlnqw 1\/1 Running 0 178m\nistio-tracing-9dd6c4f7c-44xhk 1\/1 Running 0 178m\nistiod-76bf8475c-xphgd 1\/1 Running 7 177d\nkiali-d45468dc4-fl8j4 1\/1 Running 0 178m\nprometheus-74d44d84db-zmkd7 2\/2 Running 0 178m\n"}]},{"type":"heading","attrs":{"align":null,"level":4},"content":[{"type":"text","text":"监控"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"Istio集成了"},{"type":"link","attrs":{"href":"https:\/\/prometheus.io\/","title":"","type":null},"content":[{"type":"text","text":"Prometheus"}]},{"type":"text","text":",用于发送与网络流量和服务相关的各种信息。除此之外,它还提供了一个"},{"type":"link","attrs":{"href":"https:\/\/grafana.com\/grafana\/","title":"","type":null},"content":[{"type":"text","text":"Grafana"}]},{"type":"text","text":"实例来可视化所有收集到的数据。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"要访问Grafana,我们可以使用port-forward命令来将Pod暴露出来:"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"text"},"content":[{"type":"text","text":"kubectl port-forward -n istio-system grafana-b54bb57b9-k5qbm 3000:3000\nForwarding from 127.0.0.1:3000 -> 3000\nForwarding from [::1]:3000 -> 3000\n"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"打开浏览器并导航至"},{"type":"codeinline","content":[{"type":"text","text":"locahost:3000"}]},{"type":"text","text":"以访问Grafana的仪表盘。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https:\/\/imgopt.infoq.com\/fit-in\/1200x2400\/filters:quality(80)\/filters:no_upscale()\/articles\/microservicilities-istio\/en\/resources\/4image022-1624051579986.jpg","alt":null,"title":null,"style":[{"key":"width","value":"75%"},{"key":"bordertype","value":"none"}],"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"link","attrs":{"href":"https:\/\/kiali.io\/","title":"","type":null},"content":[{"type":"text","text":"Kiali"}]},{"type":"text","text":"是另外一个在Istio中运行的工具,它能够管理Istio并观察服务网格参数,比如服务是如何连接的、它们是如何执行的以及Istio资源是如何注册的。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"要访问Kiali,我们可以使用port-forward命令来将Pod暴露出来:"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"text"},"content":[{"type":"text","text":"kubectl port-forward -n istio-system kiali-d45468dc4-fl8j4 20001:20001\nForwarding from 127.0.0.1:20001 -> 20001\nForwarding from [::1]:20001 -> 20001\n"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"打开浏览器,访问Istio仪表盘,然后导航至locahost:20001。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https:\/\/imgopt.infoq.com\/fit-in\/1200x2400\/filters:quality(80)\/filters:no_upscale()\/articles\/microservicilities-istio\/en\/resources\/2image024-1624051579986.jpg","alt":null,"title":null,"style":[{"key":"width","value":"75%"},{"key":"bordertype","value":"none"}],"href":null,"fromPaste":true,"pastePass":true}},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"跟踪"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"跟踪用来可视化一个程序的流程和数据进展。Istio会拦截所有的请求\/响应,并将它们发送至"},{"type":"link","attrs":{"href":"https:\/\/www.jaegertracing.io\/","title":"","type":null},"content":[{"type":"text","text":"Jaeger"}]},{"type":"text","text":"。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"在这里,我们可以不用port-forward命令,而是使用"},{"type":"codeinline","content":[{"type":"text","text":"istioctl"}]},{"type":"text","text":"来暴露端口并自动打开页面。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"codeinline","content":[{"type":"text","text":"istioctl dashboard jaeger"}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https:\/\/imgopt.infoq.com\/fit-in\/1200x2400\/filters:quality(80)\/filters:no_upscale()\/articles\/microservicilities-istio\/en\/resources\/3image026-1624051579986.jpg","alt":null,"title":null,"style":[{"key":"width","value":"75%"},{"key":"bordertype","value":"none"}],"href":null,"fromPaste":true,"pastePass":true}},{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","text":"结论"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"开发和实现微服务架构要比开发单体应用更具挑战性。我们相信,微服务特性能够促使你在应用基础设施方面正确地开发服务。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"Istio在一个sidecar容器中实现了一些微服务特性,使得它们能够跨所有的服务进行重用,独立于应用所使用的编程语言。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"除此之外,Istio方式能够让我们在不重新部署服务的前提下改变服务的行为。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"如果你计划开发微服务并将它们部署到Kubernetes中,那么Istio是一个切实可行的方案,因为它能够与Kubernetes无缝集成。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"本文中所用到的源码可以在GitHub的"},{"type":"link","attrs":{"href":"https:\/\/github.com\/lordofthejars\/rating-service\/tree\/istio","title":"","type":null},"content":[{"type":"text","text":"仓库"}]},{"type":"text","text":"中找到,本系列"},{"type":"link","attrs":{"href":"https:\/\/www.infoq.cn\/article\/MBAYoWrjCOAJK5QuqbcQ","title":"","type":null},"content":[{"type":"text","text":"第一篇文章"}]},{"type":"text","text":"的源码也可以在GitHub的"},{"type":"link","attrs":{"href":"https:\/\/github.com\/lordofthejars\/microservicilities-quarkus","title":"","type":null},"content":[{"type":"text","text":"仓库"}]},{"type":"text","text":"中找到。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong"}],"text":"作者简介:"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong"}],"text":"Alex Soto"},{"type":"text","text":"是红帽公司的开发者体验总监。他对Java领域、软件自动化充满热情,他相信开源软件模式。Soto是"},{"type":"link","attrs":{"href":"https:\/\/www.manning.com\/books\/testing-java-microservices","title":"","type":null},"content":[{"type":"text","text":"Manning的《Testing Java Microservices》"}]},{"type":"text","text":"和"},{"type":"link","attrs":{"href":"https:\/\/www.oreilly.com\/library\/view\/quarkus-cookbook\/9781492062646\/","title":"","type":null},"content":[{"type":"text","text":"O’Reilly的《Quarkus Cookbook》"}]},{"type":"text","text":"两本书的共同作者,他还是多个开源项目的贡献者。自2017年以来,他一直是Java Champion,是国际演讲者和Salle URL大学的教师。你可以在Twitter上关注他("},{"type":"link","attrs":{"href":"https:\/\/twitter.com\/alexsotob","title":"","type":null},"content":[{"type":"text","text":"Alex Soto ⚛"}]},{"type":"text","text":"️),随时了解Kubernetes和Java领域的动态。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong"}],"text":"原文链接:"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"link","attrs":{"href":"https:\/\/www.infoq.com\/articles\/microservicilities-istio\/","title":null,"type":null},"content":[{"type":"text","text":"Implementing Microservicilites with Istio"}]}]}]}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章