Istio服务网格技术解析与实践
上QQ阅读APP看书,第一时间看更新

1.2 使用应用程序库解决这些挑战

第一批了解如何在云环境中运行其应用程序和服务的组织是大型互联网公司,其中许多都是我们今天所熟悉的云基础架构的先驱。这些公司投入了大量的时间和资源来构建库和框架,以便为基于某种语言框架的应用程序提供帮助,解决在云原生架构中运行服务的挑战。幸运的是这些早期的微服务实践者已经慷慨地分享了他们在微服务方面的实践,并贡献了源代码到社区中,例如阿里巴巴开源的微服务框架Dubbo、Netf lix向开源社区开放的微服务库、Spring Cloud微服务开发体系等。

具体来说,在实际项目开发过程中,对于Java程序员,如果采用了Netf lix或者Spring Cloud等微服务框架,那么势必会使用其程序库处理了云原生的一些问题,包括:

Hystrix——熔断

Ribbon——客户端负载均衡

Eureka——服务注册和发现

Zuul——动态代理

由于这些库是针对Java运行时的,因此只能用于Java项目。要使用它们,必须创建一个应用程序依赖项,将它们加入类路径,然后在应用程序代码中使用这些依赖库。通常来说,在Java应用程序构建中如果使用到Netf lix OSS程序库(譬如在应用系统中依赖Hystrix),就需要在依赖配置中添加如下信息:

<dependency>
  <groupId>com.netflix.hystrix</groupId>
  <artifactId>hystrix-core</artifactId>
  <version>x.y.z</version>
</dependency>

具体来说,要使用Hystrix,就需要使用基本的Hystrix类包装命令HystrixCommand,例如:

public class CommandHelloWorld extends HystrixCommand<String> {
  private final String name;
  public CommandHelloWorld(String name) {
    super(HystrixCommandGroupKey.Factory.asKey("ExampleGroup"));
    this.name = name;
  }
  @Override
  protected String run() {
    // a real example would do work like a network call here
    return "Hello " + name + "!";
  }
}

可能读者会疑惑为什么在服务网格的书籍中,会讲述类似于Netf lix OSS的开源微服务框架。其实,两者所解决的问题是类似的,即微服务架构中分布式特点带来的复杂性问题;然而,两者解决相同问题的方式手段不尽相同,在后续章节中我们希望能通过对Istio网格的讲解,让读者理解它们之间的差异。

1.2.1 特定应用程序库的缺点

当我们将应用程序弹性能力的实现分散到应用程序本身时,显然减轻了对大规模服务架构的担忧,但同时也引入了一些新的挑战。譬如,如果我们希望在架构中引入新服务,它将受限于其他人和其他团队的实施决策。假设在架构中要使用Netf lixOSS Hystrix,则必须使用Java或某些基于JVM的技术。通常,熔断和负载均衡是一致的,因此需要使用这两个弹性库。若要使用Netf lix功能区进行负载均衡,可能还需要使用Eureka的服务注册库。

当引入新语言或框架来实现服务时还会引发另一个潜在问题。譬如,你可能会发现NodeJS更适合实现面向用户的API,但已有架构的其余部分都是基于Java语言并利用了Netf lix OSS库。你可以选择一组不同的库来实现弹性模式,对于希望引入的每种语言,需要搜索、认证和引入新的开发堆栈。这些库中的每一个库都将具有不同的实现,在某些情况下,你可能无法为每个框架语言组合找到类似的替代方案,最终得到结果的是总体使用效果不一致性。

如图1-2所示,这些代码库中包括了服务发现、熔断、限流等功能,版本不同往往会带来冲突问题,而且版本一旦变更,整个应用也要随之全部变更,即使你的应用逻辑并没有任何变化。复杂系统中的微服务实现往往会使用不同的编程语言、不同的框架,这些实现方案往往存在差异大、缺少共性的问题。

图1-2 特定应用程序库的缺点

1.2.2 将这些问题推向基础设施

这些基本的应用程序网络问题并非特定于某个应用程序、语言或框架。例如,重试、超时、客户端负载均衡,或者熔断等,这些问题与应用程序功能本身相互独立。作为整个服务的一部分,它们是关键问题,但是为使用的每种语言或者框架投入大量时间和资源,以便在特定于语言或框架中实现类似的功能,这不应该是最佳的解决方法。我们真正想要的是以一种技术无关的方式来解决这些问题,并减轻特定于应用程序本身来实现。

Linux容器简化了应用程序的打包部署,容器是云原生应用的基石,通过应用容器化,不仅使得开发部署更加敏捷、迁移更加灵活,并且可将这些实现标准化。而容器编排则是更近一步,负责解决如何高效地编排和利用好这些资源。Kubernetes编排容器服务已经成为一种事实标准;同时微服务与容器在轻量、快速部署、运维等特征方面已实现了匹配,微服务运行在容器中也正成为一种标准实践。不论使用什么具体的语言与编程框架,针对应用程序的构造、部署、服务暴露、服务关联/反关联、健康检查以及扩展等,通过使用Kubernetes即可将其提升到新的水平。实际上,Kubernetes也有一个简单的负载均衡和服务发现机制。在Kubernetes中,我们可以使用单个虚拟IP与后端pod进行通信。Kubernetes将以轮询或随机方式自动将流量发送到pod。Kubernetes根据其健康状态以及它们是否与标签和选择器匹配来进行处理。然后,服务可以使用DNS进行服务发现和负载均衡,而不管其实现如何,也就是说无需特殊的语言特定库或注册客户端。在这种技术架构下,Kubernetes将简单的网络问题从应用程序转移到基础架构中。

Kubernetes是一个出色的容器部署和管理平台,它为创建通用的分布式系统管理并将其公开为基础架构服务树立了榜样。但是,Kubernetes是一个容器部署平台,它不会演变或应用网络的其他诸多方面,它旨在通过API以非常自然的方式进行扩展,并期望将任何更高阶的应用程序服务构建为插件。而类似于Istio的这些服务网格技术在微服务治理上很好地补齐了Kubernetes的功能,同时又与Kubernetes有着完美的集成,不同于现有的微服务架构,如SpringCloud、Dubbo、Netf lix OSS等。

将这些问题转移到基础架构中的另外一种方法是使用代理。代理是一个中间基础架构组件,可以处理连接并将它们重定向到适当的后端。我们可以使用代理来处理网络流量,强制执行安全策略以及对后端服务器进行负载均衡等工作。例如,HAProxy是一种简单但功能强大的反向代理,用于在多个后端服务器之间分配连接;mod_proxy是Apache HTTP服务器的一个模块,它也可以充当反向代理。在我们的企业IT系统中,通常所有传出的互联网流量都通过转发代理进行路由,这些代理监视流量并阻止某些类型的活动。

当然,我们需要一个七层代理。这个服务代理将需要理解应用程序架构,能够支持诸如重试、超时、熔断、客户端负载均衡、服务发现、安全性和指标收集等功能,并且不局限于任何特定的语言或框架依赖。