- Istio最佳实战
- (美)克里斯汀·波斯塔等
- 2377字
- 2023-07-26 17:05:47
1.1 快速迭代带来的挑战
ACME公司的技术团队已经购买了微服务、自动化测试、容器以及持续集成和持续交付(CI/CD)等服务。他们决定从负责核心收入的ACMEmono系统中分离出模块A和模块B,使它们成为独立的服务。他们还需要一些新的功能,因此决定将其构建为服务C,从而产生了如图1.1所示的服务架构。
图1.1 ACMEmono现代化服务架构
他们将新服务打包在容器中并部署在基于Kubernetes的平台上。在实施这些方法时,他们很快就遇到了一些挑战。
ACME注意到的第一件事情是,有时架构内部的服务处理请求所需的时间非常不一致。在客户使用高峰期间,一些服务出现了间断问题,无法为任何流量提供服务。此外,ACME还发现,如果服务B在处理请求时遇到了问题,服务A也会遇到,但只是针对某些请求。
ACME注意到的第二件事情是,当他们实践自动化部署时,有时会在系统中引入自动化测试无法捕获的错误。他们实践了一种称为蓝绿部署的部署方法,这意味着他们在自己的集群中引入新的部署(绿色部署),然后在某个时刻将来自旧集群(蓝色部署)的通信截流到新集群。他们原本希望蓝绿部署的方法可以降低部署的风险,但却经历了更多的“大爆炸”,这原本是其想要避免的。
最后,ACME发现实现服务A和服务B的团队处理安全性的方式完全不同。团队A偏爱使用证书和私钥的安全连接,而团队B创建了构建在传递令牌和验证签名基础上的自定义框架。运营服务C的团队决定他们不需要任何额外的安全措施,因为这是公司防火墙后的“内部”服务。
这些挑战并不是ACME所独有的,挑战的范围也不局限于他们所遇到的情况。当转移到面向服务的架构时,必须解决以下问题:
• 防止故障超出隔离边界。
• 构建能够响应环境变化的应用程序/服务。
• 建立能够在部分失效条件下运行的系统。
• 理解整个系统在不断变化和发展时发生了什么。
• 无法控制系统的运行时行为。
• 随着攻击面的增加,加强安全防护。
• 降低系统变更产生的风险。
• 执行关于谁、什么东西可以使用系统组件,以及何时可以使用系统组件的策略。
随着对Istio了解的深入,我们将更深入地探讨和处理这些问题。这是在任何云基础设施上构建基于服务的架构所面临的核心挑战。过去,非云架构确实需要解决这些问题;但在今天的云环境中,它们被高度放大,如果没有妥善考虑,可能会导致整个系统崩溃。让我们更仔细地看看在不可靠的基础设施中所遇到的问题。
1.1.1 不可靠的云基础设施
尽管作为云基础设施的消费者,我们看不到实际的硬件,但云是由数百万个硬件和软件组成的。这些组件构成了计算、存储和网络虚拟化基础设施,我们可以通过自助服务API提供这些基础设施。这些组件中的任何一个都可能并且确实会失败。过去,我们竭尽所能使基础设施高可用,并在此基础上构建应用程序,假定其具有高可用性和可靠性。在云计算中,我们必须假设基础设施是短暂的,并且有时是不可用的。在我们的架构中,必须首先考虑这种短暂性。
举一个简单的例子。假设Preference服务负责管理客户偏好,并最终调用Customer服务。在图1.2中,Preference服务调用Customer服务来更新一些客户数据,在发送消息时遇到了非常高的延迟。它做了什么?缓慢的下游依赖关系会对Preference服务造成严重破坏,包括导致其失败(从而引发级联失败)。导致这种情况发生的原因有很多,比如:
• Customer服务负载过重,运行缓慢。
• Customer服务有问题。
• 网络中存在防火墙,降低了网络流量。
• 网络拥塞,降低了流量。
• 网络出现硬件故障,正在重新路由流量。
• Customer服务硬件的网卡出现故障。
图1.2 不可靠网络上的简单服务通信
问题是,Preference服务无法区分这是否是Customer服务的失败。同样,在拥有数百万个硬件和软件组件的云环境中,这些类型的场景一直在发生。
1.1.2 服务通信需要弹性
Preference服务可以重试请求,尽管在过载的情况下,这可能只会给下游带来额外的问题。如果重试请求,它不能确定以前的尝试没有成功。它可以在某个阈值之后使请求超时并抛出错误,还可以重试Customer服务的不同实例,可能是在不同的可用性区域。如果Customer服务在很长一段时间内遇到这些或类似的问题,Preference服务可能会选择在一段冷却时间后完全停止调用Customer服务(一种熔断形式,我们将在后面的章节中更深入地讨论它)。
有些模式已经发展起来了,可以缓解应用程序的这类问题,使应用程序更有弹性,以应对计划外的、意想不到的故障:
• 客户端负载均衡——为客户端提供可能的端点列表,并让它决定调用哪一个。
• 服务发现——一种用于查找特定逻辑服务端点列表的机制,这些端点是健康的且定期更新。
• 熔断——在一段时间内对表现异常的服务进行屏蔽。
• bulkheading——在调用服务时,使用显式的阈值(连接、线程、会话等)限制客户端资源的使用。
• 超时——在调用服务时,对Request、Socket、Liveness等执行时间限制。
• 重试——重试失败的请求。
• 重试限制——对重试的次数进行限制:限制在给定的时间段内重试的次数(例如,在10s的窗口内,只有50%的请求重试)。
• 最后期限——给出请求上下文,说明一个响应可以持续多久;如果超过了最后期限,就不再处理请求。
总的来说,这些类型的模式可以被认为是应用程序网络。它们与网络栈较低层的类似结构有很多重叠之处,只不过它们是在消息层而不是在包层操作的。
1.1.3 实时可观测性
快速迭代的一个非常重要的方面是确保方向正确。我们试图快速部署应用程序,看看客户的反应,但如果应用程序很慢或不可用,客户就没有机会做出反应了(或避免使用我们的服务)。当对服务进行更改时,我们是否了解这将产生什么影响(积极的或消极的)?在做出改变之前,我们知道事情是如何运行的吗?
了解我们的服务架构非常重要,比如哪些服务正在相互通信、典型的服务负载是什么样子的、预计会看到多少故障、服务出现故障时会发生什么、服务运行状况,等等。每次通过部署新代码或配置进行更改时,我们都可能会给关键指标引入负面影响。当网络和基础设施变得不可靠时,或者部署了带有bug的新代码时,我们能否确保自己对实际发生的事情有足够的把握,从而相信系统不会濒临崩溃?使用指标、日志和追踪功能来观察系统是服务架构的关键部分。