Netflix OSS、Spring Cloud仍是Kubernetes? 都要吧!

Netflix OSS是由Netflix公司主持开发的一套代码框架和库,目的是解决上了规模以后的分布式系统可能出现的一些有趣问题。对于当今时代的Java开发者们来讲,Netflix OSS简直就是在云端开发微服务的代名词服务发现负载均衡容错等对于可扩展的分布式系统来讲都是很是很是重要的概念,Netflix对这些问题都给出了很好的解决方案。在这里Netflix要对那些在广大的开源社区中为这些代码框架和库作出过贡献的人们简单地说声“谢谢”,还有许多互联网公司也作出了贡献,因此在这里一并谢过。但是有一些比较大的互联网公司却为本身作的一些东西申请了专利,还把代码都保留起来没有开源,这实在不太好。html

不过,Netflix OSS的许多内容都是在一个已通过去的年代写出来的,那时全部东西都只能运行在AWS云上而没有其它选择。关于那个年代的许多宝贵遗产和前提假设都已经被封装到了Netflix的库里面,对于如今你运行的环境(好比Linux容器)已经不适用了。在Linux容器Docker容器管理系统等等出现以后,咱们愈来愈看到把咱们的微服务运行在Linux容器(公有云、私有云,或者都要,等等)里的巨大价值。另外,由于这些容器都是直接把这些服务打包起来、对外不透明的,因此咱们倾向于不要过多关心在容器里面运行的究竟是什么技术(是Java?仍是Node.js?或者Go?)。Netflix OSS主要是为Java开发者服务的,它是许多的库、框架和配置的集合,你须要把它们包含在你的Java程序或服务代码里面。html5

这就带来了第一个问题。java

种类众多的微服务是能够用各类不一样的框架或语言实现的,但像服务发现、负载均衡、容错等等功能仍是很是重要并且必不可少的。若是咱们在容器里面运行这些服务,咱们能够借助于强大的语言无关的架构来作各类事情,好比构建打包部署健康检查滚动升级蓝绿发布安全、还有其它等等各类事情。做为一种类型的KubernetesOpenShift专一于为企业用户服务,它把全部事情都帮你作完了:没有什么东西必需要你的应用层程序去知道或者处理的了。你只要简简单单的实现你的应用程序和服务就好,只要专一于它们应该作的功能就好。nginx

 

这样是否是说这些架构能够帮忙把你们从服务发现、负载均衡、容错等功能中解放出来?那为何这些还要是应用层的事?git

若是你使用Kubernets(或者某些变种),那么答案就是:是的。程序员

Kubernetes方式的服务发现

用Netflix OSS时一般你要创建一台服务发现服务器,让全部能够被各类客户端发现的服务注册在这里。好比你用Netflix Ribbon来与各类其它服务交互,就须要能找出来它们都运行在哪里。各类服务可能下线,可能按它们本身的运行须要退出,也有可能咱们要向集群中加入更多服务来作横向扩展。这种集中式的服务发现注册机制一般能够帮助咱们跟踪集群中有哪些服务是可用的。github

问题之一是,作为一个开发人员你须要作这些事情:算法

  • 决定我究竟是想要一个AP系统(ConsulEureka等)仍是CP系统(ZooKeeperetcd等等)。
  • 想明白在上了规模时如何运行、管理和监控这些系统(这可不是一个小小的演示系统)。

并且,你要找到对应你使用的编程语言的客户端库,才能与服务发现机制通讯。回到咱们刚才讨论的问题,微服务多是用许多不一样种语言实现的,因此可能一个成熟的Java客户端库好找,但相应的Go或Node.js库就没有了,那你只好本身写一个。可对每一种语言和每个程序员,他们均可能对怎么实现这样的客户端库有本身的想法,这样你就会忙于维护各类不一样的客户端库,作的事情功能都是同样的但是实现逻辑却各自不一样。也有可能每种语言都会使用它本身的服务发现服务器并且也有它本身现成的客户端库呢?因此你就要为每一种语言都管理和维护不一样的服务发现服务器吗?无论哪一种方式都是很是使人讨厌的。spring

若是咱们使用DNS能解决问题吗?

这样就解决了客户端库的问题吗?DNS是全部使用TCP/UDP的系统自带的,无论你是部署在单机、云、容器、Windows或是Solaris等等哪一种系统上。你的客户端程序只须要指向一个域名(好比http://awesomefooservice/),而后底层框架就知道该把服务路由到DNS指向的地方了(也多是用VIP加负载均衡,或者轮询DNS等等)。好!如今咱们没必要再关心我须要使用什么样的客户端程序来发现一个服务了,我只须要使用任意一种TCP客户端就好。我也没必要关心如何管理DNS集群,它是网络路由器自带的,你们都用得很熟。docker

但DNS对弹性发现的状况可能表现很糟糕

缺点:DNS对于弹性的、动态的服务集群就表现很差了。要向集群中加入新服务时该怎么办?要下线服务呢?服务的IP地址可能缓存在DNS服务器或路由器中(也许并不属于你的管辖范围),也可能在你本身的IP栈中。还有若是你的程序或者服务要侦听非标准的80端口呢?DNS是默认使用标准的80端口的。要让DNS使用非标准端口,你就要用到DNS SRV记录,这样就又回到了最初的问题,你须要在应用程序层有特殊的客户端库来发现这些记录。

Kubernetes服务

我们就用Kubernetes吧。反正咱们要在Docker或者Linux容器里面运行东西,那就最好是把Docker容器(或者Rocket容器,或者Hyper.sh容器等等)运行在Kubernetes里面了。

(看,个人技术其实不好,尤为是对于那些看起来好象很“简单”的技术——由于你不可能用很复杂的组件来搭建出一个很复杂的系统。你会想要简单的组件。但写出简单的组件这事自己其实很复杂。要我理解象Google或Red Hat作的和Kubernetes相关的、来简化分布式系统部署、管理等等的事对于我来讲实在是象是天方夜谭。:) )

使用Kubernetes咱们只要建立并使用Kubernetes服务就行了。咱们没必要浪费时间去搭建服务发现服务器、写定制化的客户端库、调校DNS等等。它直接就可用,咱们只要直接进入咱们微服务的下一个主题便可,即提供业务价值。

它是怎么工做的呢?

下面这些简单的抽象都是Kubernetes实现了来支持这些功能的:

Pod很简单,它们基本上就是你的Linux容器。标签也很简单,它们基本上就是一些用于标记你的Pod的键-值型字符串(好比Pod A有以下标签:app=cassandra、tier=backend、version=1.0、language=java)。这些标签能够是任何你想起的名字。

最后一个概念是服务。也很简单,一个服务就是一个固定集群IP地址。这个IP地址是虚拟的,能够用来指向真正提供服务的Pod或者容器。那这个IP地址怎么知道该找哪个Pod或者容器呢?它是用“标签选择器”来找到全部符合你要找的标签的Pod的。好比,假如咱们须要一个有“app=cassandra AND tier=backend”标签的Kubernetes服务,它就会帮咱们找到一个VIP,指向任何一个同时有这两个标签的Pod。这个选择器是动态工做的,任何加入集群的Pods都会根据它所带有的标签马上自动参与服务发现。

另外一个用Kubernetes做为Pod选择服务的好处是Kubernetes很是智能,它知道哪一个Pod是属于哪一个服务的,还会考虑它的存活和健康状况。Kubernetes会使用内置的存活和健康检查机制来判断一个Pod是否应该被加入集群,依据是它是否存活和它是否在正常工做。对于那些不符合条件的它会直接剔除掉。

要注意的是,一个Kubernetes服务的实例不是一个什么“东西”,也不是应用、Docker容器等等任何东西,它是个虚拟的东西,因此天然也不会有单点故障。它就是一个由Kubernetes路由的IP地址。

这对于程序员来讲实在是使人难以置信的强大和简单。如今若是一个应用想要使用一个Cassandra服务,它会直接使用固定IP地址来找到Cassandra数据库。但写死一个固定IP地址确定不是什么好作法,由于当你想要把你的程序或者服务挪个地方时就会遇到麻烦。因此通常作法是改IP(或者加个配置项),可这样又加剧了程序配置功能的负担,最终解决方案一般就是DNS。

使用Kubernetes内的DNS集群就能够解决上述问题。由于对于一个特定的运行环境(开发、QA等)IP地址是固定的,那咱们就不介意直接使用固定IP,反正它也永远不会变。但若是咱们是使用DNS的话,举个例子,就能够把程序配置成与http://awesomefooservice上的服务交互,这样无论咱们的运行环境怎么变,从开发改到QA再改到生产,咱们都会部署那些Kubernetes服务,业务程序就不须要改了。

咱们不须要作额外的配置,也不用费心DNS缓存或SRV记录、定制客户端库或管理额外的服务发现框架等等。Pod能够自动加入集群或从集群中剔除掉,Kubernetes服务的标签选择器会动态的依据标签分组。业务程序只须要与http://awesomefooservice/交互便可,随便你是个Java应用,或者是Python、Node.js、Perl、Go、.NET、Ruby、C++、Scala、Groovy等什么语言写的都行。这种服务发现机制就不强求必须使用什么特定的客户端,你随便用就行了。

这样服务发现功能就大大简化了。

客户端侧的负载均衡怎么办?

这事颇有趣。Netflix提供了Eureka和Ribbon两个用于客户端侧的负载均衡,你也能够组合使用。基本实现就是服务注册(Eureka/Consul/ZooKeeper等等)功能在跟踪集群中都有什么服务,而且会向关心这些信息的客户端更新这些信息。这样客户端就知道了集群中都有哪些节点,它只须要选一个(随机,或者固定,或者任何它本身定制的算法)而后调用就好。等下一次再调用时,它想的话它也能够换另外一个来调用。这样的好处是咱们不须要那些可能会迅速成为系统瓶颈的软/硬负载均衡器。另外一个重要方面是,当客户端知道服务在哪里以后,它直接与服务交互便可,中间不须要再通过中转。

但依我拙见,客户端侧的负载均衡案例只能占实际状况的5%。我来解释一下。

咱们想要的是一种理想的、可扩展的负载均衡机制,并且不要有额外的设备和客户端库等。大多数状况下咱们并不介意处理过程当中请求会多一跳到负载均衡器(想一想看,可能你99%的应用都是这么作的)。咱们可能会碰到这样的状况:服务A要调用B,B还要调用C,而后D、E,等等,想像一下那条调用链。这种状况下若是每次调用都要增长额外的一跳的话,咱们的总体延迟就会变得很大。因此可能的解决方案就是要“减掉这多余的一跳”,但这一跳也可能不止是到负载均衡器的,更好的办法是要减小那条调用链的层级。请参考我博客上事件驱动系统专题中关于“自治与集中”的讨论,我已经考虑过这样的问题了。

根据上文将Kubernetes服务用做服务发现一节所描述的办法 ,咱们能够有不错的负载均衡机制(也不会有各类服务注册、定制客户端、DNS缺点等额外开销)。当咱们经过DNS或IP来与Kubernetes服务交互时,Kubernetes会默认地就在集群中的Pod之间作负载均衡(注意集群是由标签和标签选择器定义的)。若是你不想有负载均衡的额外一跳也不用担忧,虚拟IP是直接指向Pod的,不会通过实际的物理网络中转

那95%的案例就轻松搞定了!因此没必要过分设计,简单就好。

那剩下的5%的状况怎么办?可能你会有这样的状况,你的程序要在运行时决定调用集群中的哪一个具体终端节点。通常来讲你可能会想用些复杂的定制算法,而不是经常使用的轮询、随机、固定某一个等等。这时就可使用客户端侧的负载均衡机制。你仍然能够用Kubernetes的服务发现机制来找出集群中有哪些Pod是可用的,再根据标签来决定调用哪一个。fabric8.io社区的Kubeflix项目为Ribbon提供了发现插件,好比经过Kubernetes的REST API得到一个服务的全部Pod,而后调用者能够用代码来根据业务选择具体调用哪一个,不限编程语言。对于这些状况,花些精力来实现根据不一样客户端状况定制的发现机制代码库是值得的,更好的作法是把这些定制的逻辑模块化,把依赖关系从业务程序中独立出去。这样使用Kubernetes时,就能够把这些独立的算法模块也随着你的程序和服务部署上去,就能够方便的使用定制的负载均衡算法了。

我仍是那句话,这是5%的须要有额外复杂处理的状况。对于95%的状况,使用内置的机制就够了。

容错又怎么办?

在搭建有依赖关系的系统时要时刻记得每一个模块要对别人提供什么服务,就是说即便调用方不存在或者崩溃了,它也要记得本身的义务。Kubernetes在容错方面又有什么功能呢?

Kubernetes的确是有自愈功能的。若是一个Pod或者Pod中的一个容器挂掉了,Kubernetes能够把它再拉起来以维持ReplicaSet的不变性。好比你配置想要有10个叫“foo”的Pod,那Kubernetes就会帮你一直维持这个数量。即便某个Pod挂了,它也会再拉起一个来保持住10这个总数。

自愈功能太强大了,并且是随着Kubernetes原生提供的,但咱们讨论这个的缘由是若是被依赖物(数据库或其余服务)挂掉了,依赖它的业务程序该怎么样?这彻底要靠业务程序本身决定怎么处理了。举例来讲,若是你想在Netflix上看个电影,就会有个请求发送到受权服务上,校验你是否有权限去看那个电影。可若是受权服务挂了该怎么办呢?就不给用户看那个电影吗?或者把出错日志打给用户看?Netflix的作法是容许用户看。当受权服务出错时,容许一个无权限的用户看某个电影,这种体验比直接拒绝要好得多,也许人家有这个权限呢?

比较好的作法是优雅的降级,或者找出替代方案来持续提供服务。Netflix Hystrix就是一个很是好的Java解决方案,它实现了机制来作隔离熔断回滚。每一种都是针对不一样业务的具体实现,因此在这种状况下,针对不一样的编程语言有定制的客户端库也是很是合理的。

Kubernetes也有相似功能吗?固然!

再看看强大的Kubeflix项目,你能够用Netflix Turbine项目来累积而且将你的集群中运行的全部断路器可视化。Hystrix能够把全部的服务器事件以数据流的形式发送出去,由Turbine消费掉。那Turbine又怎么知道哪些Pod里面有Hystrix呢?好问题,这个能够用Kubernetes的标签解决。若是给全部有Hystrix的Pod全都打上个“hystrix.enabled=true”的标签,Kubeflix Turbine引擎就能够自动发现每一个Hystrix断路器的SSE流,而且把它们展示在Turbine的网页上。太感谢你了,Kubernetes!

上图由Joseph Wilk绘制,谢谢。

配置管理怎么办?

Netflix Archaius是用于处理云上系统的分布式配置管理的。用法与Eureka和Ribbon同样,搭起一个配置服务器,再用一个Java库去查出配置项的值就能够了,它也支持动态更改配置等。请记住Netflix是为了在AWS上构建系统实现的这个功能,可是属于Netflix的。做为CI/CD管道的一部分,他们要构建AMI而且部署,可构建AMI或任何VM镜像都是很是耗时的,而且大多数时候都要有不少前置工做。有了Docker或Linux容器,事情就容易多了,接下来我将从配置的角度解释一下。

仍是先说95%的状况。咱们但愿把环境相关的配置信息保存在咱们的业务程序以外,再在运行时从运行环境(开发、QA、生产等)中得到它们。这里有个很是重要的区别,不是每一个配置都和环境相关,要随着运行环境来改的。并且咱们也很是想要有与编程语言不相关的办法来查找配置,避免强迫你们用Java,而后又要配一堆的Java库和路径等。

咱们能够用Kubernetes来提供基于环境的配置管理:

咱们能够把配置信息经过环境变量提供给Linux容器,这样无论Java、Node.js、GO、Ruby仍是Python等等,大多数编程语言均可以很容易获取。也能够把配置信息保存在Git上,再把Git Repo和Pod捆绑起来,映射成Pod本地文件系统的文件,这样任何编程框架均可以以获取本地文件的方式来获取配置信息了,这是个好方案。最后,也能够经过Kubernetes的ConfigMap来把版本化的配置信息保存在ConfigMap中,它也是作为文件系统加载到Pod上,这样就能够将Git Repo解耦出去了。获取配置信息的方法还是从文件系统的配置文件中读数据,你本身喜欢用什么编程语言或者框架都好。

另外5%的状况呢?

在剩下的5%的状况下你可能想要在程序运行时动态更改配置信息。这一点Kubernetes能够帮忙。你只须要在ConfigMap中更改配置文件,而后把那些改变更态的推送到加载了它的Pod上就行了。在这个方案里,你要使用客户端的库来帮助你感知到这些配置的改动,而且把它们提交到业务程序中。Netflix Archais就提供了有这样功能的客户端。Java版的Spring Cloud Kubernetes在用了ConfigMap时处理这样的事情更容易。

Spring Cloud怎么样?

使用Java的程序员们在Spring下开发微服务时经常把Spring Cloud和Netflix OSS等同起来,由于它很大一部分就是基于Netflix OSS实现的。fabric8.io社区中也有不少用Kubernetes运行Spring Cloud的好东西,请查看https://github.com/fabric8io/spring-cloud-kubernetes。包括配置、日志等在内的不少模式均可以用Kubernetes运行得很是好,不用借助服务发现引擎、配置管理引擎等额外的、复杂的框架。

小结

若是你正在构建本身的微服务,并且你也对Netflix OSS/Java/Spring/Spring Cloud等方案很感兴趣,请必定提醒本身大家不是Netflix,因此没必要直接调用AWS EC2的原语来把本身的程序搞得很是复杂。若是你在调查Docker的方案,那采用Kubernetes是个很是明智的选择,它自己就自带许多这样的分布式系统功能。请在合适的时候将业务级程序库分层,这样能够从一开始就避免把你的服务搞的太复杂,由于Netflix在5年前就很是明智的开始这样作了。事实上他们也是不得不这样的,但想一想若是5年前他们有Kubernetes又会是怎样?他们的Netflix OSS栈会看起来彻底不一样的! :)

阅读英文原文Netflix OSS, Spring Cloud, or Kubernetes? How About All of Them!

相关文章
相关标签/搜索