技术开发 频道

为什么 kubernetes 天然就适合微服务?

  三、微服务化的十个设计要点

  微服务有哪些要点呢?第一张图是 SpringCloud 的整个生态。

为什么 kubernetes 天然就适合微服务?

  第二张图是微服务的 12 要素以及在网易云的实践。

为什么 kubernetes 天然就适合微服务?

  接下来细说微服务的设计要点。

  设计要点一:API 网关。

为什么 kubernetes 天然就适合微服务?

  在实施微服务的过程中,不免要面临服务的聚合与拆分,当后端服务的拆分相对比较频繁的时候,作为手机 App 来讲,往往需要一个统一的入口,将不同的请求路由到不同的服务,无论后面如何拆分与聚合,对于手机端来讲都是透明的。

  有了 API 网关以后,简单的数据聚合可以在网关层完成,这样就不用在手机 App 端完成,从而手机 App 耗电量较小,用户体验较好。

  有了统一的 API 网关,还可以进行统一的认证和鉴权,尽管服务之间的相互调用比较复杂,接口也会比较多,API 网关往往只暴露必须的对外接口,并且对接口进行统一的认证和鉴权,使得内部的服务相互访问的时候,不用再进行认证和鉴权,效率会比较高。

  有了统一的 API 网关,可以在这一层设定一定的策略,进行 A/B 测试,蓝绿发布,预发环境导流等等。API 网关往往是无状态的,可以横向扩展,从而不会成为性能瓶颈。

  设计要点二:无状态化,区分有状态的和无状态的应用。

为什么 kubernetes 天然就适合微服务?

  影响应用迁移和横向扩展的重要因素就是应用的状态,无状态服务,是要把这个状态往外移,将 Session 数据,文件数据,结构化数据保存在后端统一的存储中,从而应用仅仅包含商务逻辑。

  状态是不可避免的,例如 ZooKeeper, DB,Cache 等,把这些所有有状态的东西收敛在一个非常集中的集群里面。

  整个业务就分两部分,一个是无状态的部分,一个是有状态的部分。

  无状态的部分能实现两点,一是跨机房随意地部署,也即迁移性,一是弹性伸缩,很容易地进行扩容。

  有状态的部分,如 DB,Cache,ZooKeeper 有自己的高可用机制,要利用到他们自己高可用的机制来实现这个状态的集群。

  虽说无状态化,但是当前处理的数据,还是会在内存里面的,当前的进程挂掉数据,肯定也是有一部分丢失的,为了实现这一点,服务要有重试的机制,接口要有幂等的机制,通过服务发现机制,重新调用一次后端服务的另一个实例就可以了。

  设计要点三:数据库的横向扩展。

为什么 kubernetes 天然就适合微服务?

  数据库是保存状态,是最重要的也是最容易出现瓶颈的。有了分布式数据库可以使数据库的性能可以随着节点增加线性地增加。

  分布式数据库最最下面是 RDS,是主备的,通过 MySql 的内核开发能力,我们能够实现主备切换数据零丢失,所以数据落在这个 RDS 里面,是非常放心的,哪怕是挂了一个节点,切换完了以后,你的数据也是不会丢的。

  再往上就是横向怎么承载大的吞吐量的问题,上面有一个负载均衡 NLB,用 LVS,HAProxy, Keepalived,下面接了一层 Query Server。Query Server 是可以根据监控数据进行横向扩展的,如果出现了故障,可以随时进行替换的修复,对于业务层是没有任何感知的。

  另外一个就是双机房的部署,DDB 开发了一个数据运河 NDC 的组件,可以使得不同的 DDB 之间在不同的机房里面进行同步,这时候不但在一个数据中心里面是分布式的,在多个数据中心里面也会有一个类似双活的一个备份,高可用性有非常好的保证。

  设计要点四:缓存

为什么 kubernetes 天然就适合微服务?

  在高并发场景下缓存是非常重要的。要有层次的缓存,使得数据尽量靠近用户。数据越靠近用户能承载的并发量也越大,响应时间越短。

  在手机客户端 App 上就应该有一层缓存,不是所有的数据都每时每刻从后端拿,而是只拿重要的,关键的,时常变化的数据。

  尤其对于静态数据,可以过一段时间去取一次,而且也没必要到数据中心去取,可以通过 CDN,将数据缓存在距离客户端最近的节点上,进行就近下载。

  有时候 CDN 里面没有,还是要回到数据中心去下载,称为回源,在数据中心的最外层,我们称为接入层,可以设置一层缓存,将大部分的请求拦截,从而不会对后台的数据库造成压力。

  如果是动态数据,还是需要访问应用,通过应用中的商务逻辑生成,或者去数据库读取,为了减轻数据库的压力,应用可以使用本地的缓存,也可以使用分布式缓存,如 Memcached 或者 Redis,使得大部分请求读取缓存即可,不必访问数据库。

  当然动态数据还可以做一定的静态化,也即降级成静态数据,从而减少后端的压力。

  设计要点五:服务拆分和服务发现

为什么 kubernetes 天然就适合微服务?

  当系统扛不住,应用变化快的时候,往往要考虑将比较大的服务拆分为一系列小的服务。

  这样第一个好处就是开发比较独立,当非常多的人在维护同一个代码仓库的时候,往往对代码的修改就会相互影响,常常会出现我没改什么测试就不通过了,而且代码提交的时候,经常会出现冲突,需要进行代码合并,大大降低了开发的效率。

  另一个好处就是上线独立,物流模块对接了一家新的快递公司,需要连同下单一起上线,这是非常不合理的行为,我没改还要我重启,我没改还让我发布,我没改还要我开会,都是应该拆分的时机。

  另外再就是高并发时段的扩容,往往只有最关键的下单和支付流程是核心,只要将关键的交易链路进行扩容即可,如果这时候附带很多其他的服务,扩容即是不经济的,也是很有风险的。

  再就是容灾和降级,在大促的时候,可能需要牺牲一部分的边角功能,但是如果所有的代码耦合在一起,很难将边角的部分功能进行降级。

  当然拆分完毕以后,应用之间的关系就更加复杂了,因而需要服务发现的机制,来管理应用相互的关系,实现自动的修复,自动的关联,自动的负载均衡,自动的容错切换。

  设计要点六:服务编排与弹性伸缩

为什么 kubernetes 天然就适合微服务?

  当服务拆分了,进程就会非常的多,因而需要服务编排来管理服务之间的依赖关系,以及将服务的部署代码化,也就是我们常说的基础设施即代码。这样对于服务的发布,更新,回滚,扩容,缩容,都可以通过修改编排文件来实现,从而增加了可追溯性,易管理性,和自动化的能力。

  既然编排文件也可以用代码仓库进行管理,就可以实现一百个服务中,更新其中五个服务,只要修改编排文件中的五个服务的配置就可以,当编排文件提交的时候,代码仓库自动触发自动部署升级脚本,从而更新线上的环境,当发现新的环境有问题时,当然希望将这五个服务原子性地回滚,如果没有编排文件,需要人工记录这次升级了哪五个服务。有了编排文件,只要在代码仓库里面 revert,就回滚到上一个版本了。所有的操作在代码仓库里都是可以看到的。

  设计要点七:统一配置中心

为什么 kubernetes 天然就适合微服务?

  服务拆分以后,服务的数量非常多,如果所有的配置都以配置文件的方式放在应用本地的话,非常难以管理,可以想象当有几百上千个进程中有一个配置出现了问题,是很难将它找出来的,因而需要有统一的配置中心,来管理所有的配置,进行统一的配置下发。

  在微服务中,配置往往分为几类,一类是几乎不变的配置,这种配置可以直接打在容器镜像里面,第二类是启动时就会确定的配置,这种配置往往通过环境变量,在容器启动的时候传进去,第三类就是统一的配置,需要通过配置中心进行下发,例如在大促的情况下,有些功能需要降级,哪些功能可以降级,哪些功能不能降级,都可以在配置文件中统一配置。

  设计要点八:统一的日志中心

为什么 kubernetes 天然就适合微服务?

  同样是进程数目非常多的时候,很难对成千上百个容器,一个一个登录进去查看日志,所以需要统一的日志中心来收集日志,为了使收集到的日志容易分析,对于日志的规范,需要有一定的要求,当所有的服务都遵守统一的日志规范的时候,在日志中心就可以对一个交易流程进行统一的追溯。例如在最后的日志搜索引擎中,搜索交易号,就能够看到在哪个过程出现了错误或者异常。

  设计要点九:熔断,限流,降级

为什么 kubernetes 天然就适合微服务?

  服务要有熔断,限流,降级的能力,当一个服务调用另一个服务,出现超时的时候,应及时返回,而非阻塞在那个地方,从而影响其他用户的交易,可以返回默认的托底数据。

  当一个服务发现被调用的服务,因为过于繁忙,线程池满,连接池满,或者总是出错,则应该及时熔断,防止因为下一个服务的错误或繁忙,导致本服务的不正常,从而逐渐往前传导,导致整个应用的雪崩。

  当发现整个系统的确负载过高的时候,可以选择降级某些功能或某些调用,保证最重要的交易流程的通过,以及最重要的资源全部用于保证最核心的流程。

  还有一种手段就是限流,当既设置了熔断策略,又设置了降级策略,通过全链路的压力测试,应该能够知道整个系统的支撑能力,因而就需要制定限流策略,保证系统在测试过的支撑能力范围内进行服务,超出支撑能力范围的,可拒绝服务。当你下单的时候,系统弹出对话框说 “系统忙,请重试”,并不代表系统挂了,而是说明系统是正常工作的,只不过限流策略起到了作用。

  设计要点十:全方位的监控

为什么 kubernetes 天然就适合微服务?

  当系统非常复杂的时候,要有统一的监控,主要有两个方面,一个是是否健康,一个是性能瓶颈在哪里。当系统出现异常的时候,监控系统可以配合告警系统,及时地发现,通知,干预,从而保障系统的顺利运行。

  当压力测试的时候,往往会遭遇瓶颈,也需要有全方位的监控来找出瓶颈点,同时能够保留现场,从而可以追溯和分析,进行全方位的优化。

  四、Kubernetes 本身就是微服务架构

  基于上面这十个设计要点,我们再回来看 Kubernetes,会发现越看越顺眼。

  首先 Kubernetes 本身就是微服务的架构,虽然看起来复杂,但是容易定制化,容易横向扩展。

为什么 kubernetes 天然就适合微服务?

  如图黑色的部分是 Kubernetes 原生的部分,而蓝色的部分是网易云为了支撑大规模高并发应用而做的定制化部分。

为什么 kubernetes 天然就适合微服务?

  Kubernetes 的 API Server 更像网关,提供统一的鉴权和访问接口。

  众所周知,Kubernetes 的租户管理相对比较弱,尤其是对于公有云场景,复杂的租户关系的管理,我们只要定制化 API Server,对接 Keystone,就可以管理复杂的租户关系,而不用管其他的组件。

为什么 kubernetes 天然就适合微服务?

  在 Kubernetes 中几乎所有的组件都是无状态化的,状态都保存在统一的 etcd 里面,这使得扩展性非常好,组件之间异步完成自己的任务,将结果放在 etcd 里面,互相不耦合。

  例如图中 pod 的创建过程,客户端的创建仅仅是在 etcd 中生成一个记录,而其他的组件监听到这个事件后,也相应异步的做自己的事情,并将处理的结果同样放在 etcd 中,同样并不是哪一个组件远程调用 kubelet,命令它进行容器的创建,而是发现 etcd 中,pod 被绑定到了自己这里,方才拉起。

  为了在公有云中实现租户的隔离性,我们的策略是不同的租户,不共享节点,这就需要 Kubernetes 对于 IaaS 层有所感知,因而需要实现自己的 Controller,Kubernetes 的设计使得我们可以独立创建自己的 Controller,而不是直接改代码。

为什么 kubernetes 天然就适合微服务?

  API-Server 作为接入层,是有自己的缓存机制的,防止所有的请求压力直接到后端数据库上。但是当仍然无法承载高并发请求时,瓶颈依然在后端的 etcd 存储上,这和电商应用一摸一样。当然能够想到的方式也是对 etcd 进行分库分表,不同的租户保存在不同的 etcd 集群中。

  有了 API Server 做 API 网关,后端的服务进行定制化,对于 client 和 kubelet 是透明的。

为什么 kubernetes 天然就适合微服务?

  如图是定制化的容器创建流程,由于大促和非大促期间,节点的数目相差比较大,因而不能采用事先全部创建好节点的方式,这样会造成资源的浪费,因而中间添加了网易云自己的模块 Controller 和 IaaS 的管理层,使得当创建容器资源不足的时候,动态调用 IaaS 的接口,动态的创建资源。这一切对于客户端和 kubelet 无感知。

为什么 kubernetes 天然就适合微服务?

  为了解决超过 3 万个节点的规模问题,网易云需要对各个模块进行优化,由于每个子模块仅仅完成自己的功能,Scheduler 只管调度,Proxy 只管转发,而非耦合在一起,因而每个组件都可以进行独立的优化,这符合微服务中的独立功能,独立优化,互不影响。而且 Kubernetes 的所有组件都是 Go 开发的,更加容易一些。所以 Kubernetes 上手慢,但是一旦需要定制化,会发现更加容易。

  五、Kubernetes 更加适合微服务和 DevOps 的设计

  好了,说了 K8S 本身,接下来说说 K8S 的理念设计,为什么这么适合微服务。

为什么 kubernetes 天然就适合微服务?

  前面微服务设计的十大模式,其中一个就是区分无状态和有状态,在 K8S 中,无状态对应 deployment,有状态对应 StatefulSet。

  deployment 主要通过副本数,解决横向扩展的问题。

  而 StatefulSet 通过一致的网络 ID,一致的存储,顺序的升级,扩展,回滚等机制,保证有状态应用,很好地利用自己的高可用机制。因为大多数集群的高可用机制,都是可以容忍一个节点暂时挂掉的,但是不能容忍大多数节点同时挂掉。而且高可用机制虽然可以保证一个节点挂掉后回来,有一定的修复机制,但是需要知道刚才挂掉的到底是哪个节点,StatefulSet 的机制可以让容器里面的脚本有足够的信息,处理这些情况,实现哪怕是有状态,也能尽快修复。

为什么 kubernetes 天然就适合微服务?

  在微服务中,比较推荐使用云平台的 PaaS,例如数据库,消息总线,缓存等。但是配置也是非常复杂的,因为不同的环境需要连接不同的 PaaS 服务。

  K8S 里面的 headless service 是可以很好地解决这个问题的,只要给外部服务创建一个 headless service,指向相应的 PaaS 服务,并且将服务名配置到应用中。由于生产和测试环境分成 Namespace,虽然配置了相同的服务名,但是不会错误访问,简化了配置。

为什么 kubernetes 天然就适合微服务?

  微服务少不了服务发现,除了应用层可以使用 SpringCloud 或者 Dubbo 进行服务发现,在容器平台层当然是用 Service了,可以实现负载均衡,自修复,自动关联。

为什么 kubernetes 天然就适合微服务?

  服务编排,本来 K8S 就是编排的标准,可以将 yml 文件放到代码仓库中进行管理,而通过 deployment 的副本数,可以实现弹性伸缩。

为什么 kubernetes 天然就适合微服务?

  对于配置中心,K8S 提供了 configMap,可以在容器启动的时候,将配置注入到环境变量或者 Volume 里面。但是唯一的缺点是,注入到环境变量中的配置不能动态改变了,好在 Volume 里面的可以,只要容器中的进程有 reload 机制,就可以实现配置的动态下发了。

为什么 kubernetes 天然就适合微服务?

  统一日志和监控往往需要在 Node 上部署 Agent,来对日志和指标进行收集,当然每个 Node 上都有,daemonset 的设计,使得更容易实现。

为什么 kubernetes 天然就适合微服务?

  当然目前最最火的 Service Mesh,可以实现更加精细化的服务治理,进行熔断,路由,降级等策略。Service Mesh 的实现往往通过 sidecar 的方式,拦截服务的流量,进行治理。这也得力于 Pod 的理念,一个 Pod 可以有多个容器,如果当初的设计没有 Pod,直接启动的就是容器,会非常的不方便。

  所以 K8S 的各种设计,看起来非常冗余和复杂,入门门槛比较高,但是一旦想实现真正的微服务,K8S 可以给你各种可能的组合方式。实践过微服务的人,往往会对这一点深有体会。

  六、Kubernetes 常见的使用方式

  下面我们来看一下,微服务化的不同阶段,Kubernetes 的使用方式。

  第一阶段:使用公有云虚拟机

  也即没有微服务化的阶段,基本上一个进程就能搞定,两个进程做高可用,不需要使用容器,虚拟机就非常好。

  第二阶段:容器作为持续集成工具

为什么 kubernetes 天然就适合微服务?

  当微服务开始拆分了,如何保证拆分后功能的一致性,需要持续集成作为保证,如前面的论述,容器是非常好的持续集成工具,是解决 CI/CD 中 D 的,所以一开始用 host 网络就可以,这样可以保证部署方式和原来兼容。

为什么 kubernetes 天然就适合微服务?

  如果想用私有云进行部署,直接部署在物理机上,在性能要求没有很高,但是又要和其他物理机很好的通信的情况下,可以用 bridge 打平网络的方式比较好。通过创建网桥,将物理网卡,容器网卡都连接到一个网桥上,可以实现所有的容器和物理机在同样的一个二层网络里面。

为什么 kubernetes 天然就适合微服务?

  如果性能要求比较高,例如要部署类似缓存,则可以使用 sr-iov 网卡。

为什么 kubernetes 天然就适合微服务?

  如果想实现租户的简单隔离,则往往使用各种 Overlay 的网络模式,这是最常用的部署方式。图中的数据来自网络。Flannel,Calico 都是非常好的网络插件,虽然 Flannel 一开始使用用户态的模式性能不好,后来使用内核态,性能大大改善,使用 gw 模式后,和 Calico 性能相当。

为什么 kubernetes 天然就适合微服务?

  网易云采用了 Kubernetes 和 IaaS 深度融合的方式,类似 AWS 的 Fargate 的模式,一方面可以使得原来使用虚拟机的用户平滑地迁移到容器,另一方面可以实现公有云的租户隔离。

为什么 kubernetes 天然就适合微服务?

  如图是融合的网易云容器服务的架构,这个管理 OpenStack 和 Kubernetes 的管理平台,也是用的微服务架构,有 API 网关,熔断限流功能,拆分成不同的服务,部署在 K8S 上的,所以处处是微服务。

0
相关文章