Spring Cloud、K8S、Netflix OSS三者啥关系?
来源:云平台
Netflix OSS是一组框架和库,Netflix为大规模解决一些有趣的分布式系统问题而编写的。今天,对于Java开发人员来说,它非常适合云环境中开发微服务。服务发现、负载平衡、容错等模式对于可扩展的分布式系统来说是非常重要的概念,Netflix为此提供了很好的解决方案。
无论如何,很多Netflix OSS都是在AWS云上运行的时候编写的,没有其他选择。许多假设都被移植到了Netflix库中,这些库可能不再适用于当前运行的位置(如Linux容器等)。随着Linux容器、Docker、容器管理系统等的出现,我们开始看到在Linux容器中(在云中、在私有云中,两者中)运行我们的微服务有很多价值。此外,因为我们不知道容器上服务的运行方式,我们往往不太关心哪种技术真正的在其中运行(它是Java吗?是node.js吗?它是Go?),Netflix OSS主要面向Java开发人员。它们是需要包含在Java应用程序/服务代码中的库/框架/配置集。
因此,我们得出第1点。
微服务可以在各种框架/语言中实现,但服务发现、负载平衡、容错等仍然非常重要。如果我们在容器中运行这些服务,我们可以利用功能强大的与语言无关的基础架构来执行如构建、打包、部署、运行状况检查、滚动升级、安全性等操作。例如,openshift是一种Kubernetes的服务,它考虑到了企业侧需求,它为您做了所有这些事情:在应用层中,您不需要强制任何人来了解或关心这些事情。让你的应用程序和服务保持简单!
那么基础设施能否帮助提供服务发现,负载平衡和容错?为什么这应该是应用程序级别的东西?
如果您使用kubernetes,答案是:是。
服务发现Kubernetes方式
使用Netflix OSS,您通常需要设置一个服务发现服务器,该服务器充当端点的注册表,这些端点可以通过各种客户端发现。例如,您可能使用Netflix功能与其他服务进行通信,并需要发现它们运行的位置。服务可以被取消,我们可以向集群添加更多的服务来帮助扩展。这个中央服务发现注册表跟踪集群中可用的服务。
其中一个问题是:作为开发人员,您需要执行以下操作:
- 决定我是否需要AP系统(Consul、Eureka等)还是一个CP系统(ZooKeeper、etcd等)
- 了解如何大规模上运行、管理和监控这些系统(这不是一个简单的PET项目)
此外,您需要为所使用的编程语言找到客户端,以便了解如何与服务发现机制对话。正如我们已经说过的,微服务可以在许多不同类型的语言中实现,所以一个完美的Java客户端可能是可用的,但是如果GO或NoDEJS客户端不存在,你必须自己编写。语言和开发人员的每一种排列都可能以他们自己对如何实现这种客户机的想法而告终,而现在,您不得不维护多个客户端,这些客户端试图做相同的事情,但语义上可能不同。或者每种语言都使用自己的服务发现服务器?那么,现在您是否按语言类型管理和维护服务发现服务器的基础结构?不管怎样……有点小麻烦。
只使用DNS怎么办?
好吧,这就解决了我们的客户端库问题,对吧?DNS可以融入到任何使用TCP/UDP的系统中,无论您在任何地方部署内部部署、云、容器、Windows、Solaris等,都可以随时使用DNS。您的客户端只指向一个域名(即http://awsomefooservice/),基础设施知道如何路由到DNS指向的服务(可以是带负载平衡器的)。哎呀!现在我们不需要弄清楚需要使用什么客户端来发现服务,我只需要使用我想要的任何TCP客户机。我也不需要弄清楚如何管理一个DNS集群,它已经融入到网络路由器中。
Kubernetes服务
我们就用Kubernetes。无论如何,我们将在docker/linux容器中运行,而kubernetes是运行docker容器的最佳方案。
有了kubernetes,我们只需创建和使用kubernetes服务,就完成了!我们不必浪费时间设置发现服务器、编写自定义客户端、使用DNS等。我们将继续讨论提供业务价值的微服务的下一个部分。
如何工作的呢
下面是Kubernetes为实现这一点而带来的简单抽象:
- Pods
- 标签/标签选择器
- 服务
Pods很简单。它们基本上就是您的Linux容器。标签很简单。它们基本上是键值字符串,可以用来标记您的pod(例如,pod有标签app=cassandra、tier=backend、version=1.0,language=java)。这些标签可以是你心中想要的任何东西。
最后一个概念是服务。也很简单。服务是固定的群集IP地址。这个IP地址是一个虚拟IP地址,可以用来发现/调用pods/容器中的实际端点。这个IP地址如何知道哪些pods/容器有资格被发现?它使用一个标签选择器来选择具有您定义的标签的pods。例如,假设我们想要一个Kubernetes服务,其选择器为“app = cassandra AND tier = backend”。这将为我提供一个带有虚拟IP的服务,它可以发现任何与该标签匹配的pods(同时具有app=cassandra和tier=backend)。此选择器被主动评估,以便离开集群的任何pods或加入集群的任何pods(基于它们拥有的标签)都将自动开始参与服务发现。
使用Kubernetes服务选择与服务相关的容器的另一个好处是,Kubernetes很聪明哪个容器属于哪个服务和健康状况。Kubernetes可以使用内置的活跃性和健康检查来确定是否应该根据特定服务的活动和/或正常运行,将pod包含在pod的集群中。
注意,kubernetes服务的一个实例不是一个“东西”,也不是一个设备,或者一个docker容器,或者任何东西……它是一个虚拟的“东西”…所以没有单一的故障点。
对于开发人员来说,这是非常强大和简单的。现在,一个希望使用Cassandra后端的应用程序只使用这个固定的IP地址与Cassandra数据库通信。但是硬编码一个固定的IP通常不是一个好办法,因为如果你想把你的应用程序/服务转移到一个不同的环境(如QA、Prod等)。现在您必须更改那个IP(或注入一些配置),现在您已经增加了配置负担。所以我们只使用dns:)
在Kubernetes中使用集群DNS是正确的答案。由于IP是为给定环境(dev、QA等)固定的,所以我们不关心缓存它:它永远不会改变。现在,如果我们使用DNS,我们的应用程序可以配置为在http://awsomefooseservice/上与服务通信,即使我们从dev到QA再到prod,到目前为止,我们在每个环境中都有这些kubernetes服务,我们的应用程序不需要更改。
我们不需要额外的配置,也不需要担心DNS缓存/SRV记录、自定义库客户端和管理其他的服务发现基础结构。pods现在可以添加到集群(或从集群中取出),kubernetes服务的标签选择器将根据标签主动对集群进行分组。你的应用程序只与http://aWeMESFoService通信,无论你是Java应用程序、Python、NoDE.JS、Perl、Go、.NET、Ruby、C++、Scala、Groovy,等等。这个服务发现机制不强制使用特定的客户端。
服务发现变得简单多了。
这个很有趣。Netflix编写了Eureka和Ribbon,通过这些组合,您可以启用客户端负载平衡。基本上,服务注册中心(eureka/consul/zookeeper/etc)会跟踪集群中存在哪些服务,并将这些数据发送给对此感兴趣的客户端。然后,由于客户机具有集群中节点的信息,所以它可以选择一个节点(随机、粘性或一些自定义算法),然后调用它。在下一个调用中,如果需要,它可以在集群中选择不同的服务。这里的优点是我们不需要物理/软件负载平衡器,这可能很快就会成为瓶颈。另一个重要方面:由于客户机知道服务在哪里,客户机可以直接联系服务提供商,而不需要额外的跳转。
客户端负载平衡是5%的用例。让我解释一下。
我们想要的是一种在没有任何附加设备和客户端库的情况下实现可扩展负载平衡的理想方法。在大多数情况下,我们可能不关心中间有负载均衡器的额外跳转(想想看..可能99%的应用程序都是以这种方式部署的)。我们可能会遇到这样一种情况,即服务A的呼叫服务B,它呼叫服务C、D和E,然后您就可以看到结果了。在这种情况下,如果每个人多跳转一次,我们会产生很多额外的延迟。所以一个可能的解决方案是“去掉多余的跳转”。它是……但不仅仅是在跳转中加载平衡程序:更多了您必须对下游服务进行的调用数量:)。
使用Kubernetes服务,正如我们在上面的服务发现部分所做的那样,我们实现了正确的负载平衡(同样,没有服务注册表、自定义客户端、DNS缺点等的所有开销)。当我们通过其DNS(或IP)与kubernetes服务交互时,kubernetes将默认地在集群中的pods之间实现负载平衡(记住,集群是由标签和标签选择器定义的)。如果您不希望在负载平衡中有额外的跳转,不用担心;这个虚拟IP直接路由到pods,它不会影响物理网络。
哎呀!对于95%的用例来说很容易。很有可能,你在95%的用例分布中,所以不需要过度设计。
那5%的情况怎么样?您可能遇到这样的情况:您必须在运行时做出一些业务决策,确定您真正要调用的集群中的哪个确切的后端端点。基本上,您需要使用一些比“循环”、“随机”、“粘性会话”更复杂的自定义算法,并且这些算法是特定于您的应用程序的。为此,请使用客户端负载平衡。在这个模型中,您仍然可以利用Kubernetes的服务发现来找出集群中的哪些pod,然后使用您自己的代码来决定直接调用哪个pod(基于标签等)。对于其他语言也可以这样做,只需使用kubernetes rest API查询pods等。对于这些用例,更合适的方法是将这个自定义逻辑分解成自己的模块,这样它的依赖关系就与应用程序分离了。使用kubernetes,您可以将这个单独的模块部署为应用程序/服务,并在那里保留定制的负载平衡逻辑。
这是5%的用例,并带来了额外的复杂性。对于95%的用例,只需使用内置的内容而无需任何特定于语言的特定客户端。
容错怎么样呢?
具有依赖性的系统应该始终考虑到promises。这意味着,即使依赖系统不可用或崩溃的情况下,他们也应该始终知道他们的义务是什么。我们应该问Kubernetes它在容错方面有什么作用吗?
好吧,Kubernetes确实有自我修复能力。如果一个pod中的pod或容器出现故障,
Kubernetes可以将其恢复以维护其ReplicaSet不变量(基本上,如果你告诉Kubernetes我想要10个“foo”的pod,它总是会尝试保持该状态)。
自我修复的基础设施是非常棒的,它与Kubernetes一起推出,但是我们对这个讨论感兴趣的是,当应用程序的依赖关系(数据库、其他服务等)失效时,它会发生什么?好吧,这取决于应用程序如何处理。例如,在Netflix,如果您尝试观看特定的电影,则会向“授权”服务发出服务呼叫,该服务知道您在观看电影时拥有哪些特权。如果服务中断,我们应该阻止用户观看那部电影吗?我们应该显示异常堆栈跟踪吗?Netflix的方法就是让用户看电影。
我们想要的是寻找其他方法来履行我们对服务合同的承诺。Netflix HySrx是Java开发人员的一个很好的解决方案。Netflix Hystrix实现了一种方法来执行“Bulkheading”、“Circuit Breaking”和“Fallbacks”。其中每一个都是特定于应用程序的命令,因此在本例中,为不同的语言提供特定的客户端库是合理的。
Kubernetes可以帮助解决这个问题吗?是!
再次,看一下很棒的Kubeflix项目,您可以使用Netflix Turbine项目来聚合和可视化集群中运行的所有断路器。Hystrix可以将服务器端事件公开为可由Turbine使用的流。但Turbine如何发现哪些pods中有hystrix?)我们可以使用Kubernetes标签。如果我们标记使用Hystrix的标签为hystrix.enabled=true那么Kubeflix可以自动发现每个hystrix断路器的SSE流并将其显示在Turbine网页上。
配置怎样呢?
Netflix Archius是为处理云中服务的分布式配置管理而编写的。要做到这一点,就像使用Eureka和Ribbon一样,您可以设置配置服务器并使用Java库查找配置值。它还支持动态配置更改等(请记住,Netflix是为AWS构建的)。他们是Netflix;作为他们的CI/CD管道的一部分,他们将构建AMI并部署它们。在大多数情况下,构建AMI或任何虚拟机镜像都是耗时的,并且有很多不必要的开销……使用docker/linux容器,事情会更加灵活,正如我将从配置的角度解释的那样)。
那95%的用例呢?我们希望将特定于环境的配置(这是一个重要的区别……不是每个配置都是特定于环境的配置,需要根据我们运行的环境进行更改)存储在应用程序外部,并根据我们运行的环境(dev、qa、prod等)将其注入。但是,我们确实希望有一种语言无关的方式来查找配置,而不是强迫每个人使用Java和/或使用其他Java库将其类路径复杂化以进行配置。
在Kubernetes中,我们有三种用于注入基于环境的配置的构造。
- 环境变量
- Gitrepo卷
- 配置映射
基本上,我们可以通过环境变量(Java、NodeJS、GO、Ruby、Python等可以轻松读取)向Linux容器注入配置数据,大多数语言都可以读取这些配置数据。我们可以将配置存储在Git中(这是一个好主意),并且可以将配置报告直接绑定到POD(作为文件系统上的文件),然后使用任何框架工具来使用应用程序配置文件。最后,我们可以使用kubernetes configmap将我们的版本化配置存储到configmap中,然后将其作为文件系统装载到pod中,从而稍微分离git repo。同样,您使用配置文件的方式与使用来自文件系统的任何配置文件的方式相同,它们使用您各自的语言/框架。
那5%的用例呢?
在5%的用例中,您可能希望在运行时动态更改配置。Kubernetes对此有所帮助。您可以在configmap中更改配置文件,并将这些更改动态地应用到装载它们的pods中。在这种情况下,您需要有一个客户端库,它能够检测这些配置更改并将它们公开给您的应用程序。Netflix Archais有一个客户机可以做到这一点。Java的Spring Cube KubNETes使Kubernetes更容易做到这一点。
那么Spring Cloud呢?
使用Spring开发微服务的Java用户通常把Spring Cloud与Netflix OSS等同起来,因为很多都是基于Netflix OSS的。
如果您正在研究构建微服务,并且已经倾向于使用Netflix OSS/Java/Spring/Spring Cloud,请注意,您不是Netflix,您不必直接使用AWS EC2原语,并通过这样做使应用程序复杂化。如果您希望使用Docker,那么采用Kubernetes是一个明智的选择,它附带了许多现成的“分布式系统特性”。在需要的地方分层,并从一开始就避免过度复杂化服务,因为Netflix五年前就提出了这种方法(因为他们必须这样做!我敢打赌,如果他们5年前发现有Kubernetes,他们的Netflix OSS堆栈看起来会大不一样!)。
end:如果你觉得本文对你有帮助的话,记得关注点赞转发,你的支持就是我更新动力。