云原生技术:微服务架构设计的探索

云原生与微服务

云原生是近几年非常热的一个概念,人们在提到云原生的时候往往都会提到微服务和容器等概念。其中微服务在云原生兴起之前,就已经被大家熟知,大家会把微服务部署在传统的基础设施上,搭建成一个分布式系统。相比较于单体应用,微服务化之后,带来了巨大的好处,但是随着微服务的数量越来越多,导致出现了一些必须解决的问题,比如服务治理、运维等都会变得复杂,那么该如何解决这些问题呢?

微服务技术栈的选择

对于 Java 技术栈的项目来说,开发人员通常都会选择已经比较成熟的 Spring Cloud 体系,云原生到来后,微服务 Docker 容器化,运维的时候就可以不用关心微服务使用的技术栈,可以将应用以及依赖包放到一个可移植的容器中,发布到任意的 Linux机器上,再配合Kubernetes 服务编排和管理的能力,轻松完成大规模微服务的运维管理。

微服务架构中有一些常规必要的核心组件,比如服务注册发现、负载均衡、网关、配置中心、限流/熔断/降级等,对于这些组件的能力,Spring Cloud 和Kubernetes 都能提供,它们分别从不同层面来实现了这些能力,Spring Cloud 使用 Java 语言实现,在应用层面来解决,开发者除了开发业务功能之外,同时还需要关心实现这些能力,而Kubernetes 则是从平台层面来解决,和语言无关,可以针对业务特性,选择合适的技术栈来开发业务,也让开发者可以更加专注于业务开发,但是运维能力要求较高,学习成本高。那么这两者应该怎么取舍?

  • 使用 Spring Cloud 的全家桶方案,k8s 只作为服务编排管理?
  • 不再使用 Spring Cloud,完全使用k8s 来实现微服务?
  • 选取 Spring Cloud 的部分组件,结合k8s 的能力,共同实现微服务?
  • ...

下面看下两者在微服务的部分核心能力的不同实现方式。

服务注册和发现

基于SpringCloud实现

以 Spring Cloud 的 Eureka 为例,服务提供者会把自己的服务名和 IP注册到 Eureka。服务消费者调用服务提供者时,先通过服务名在 Eureka 找到对应的服务提供者的一组访问 IP,利用 Ribbon 的负载均衡策略,选择一个合适的 IP,去访问对应的微服务,属于客户端实现的负载均衡。

image.png

基于K8S实现

K8S 则是定义了 service 的概念,是一组 pod 的集合,在创建 service 时,会根据标签选择器(label selector)选择合适的 pod,并记录为 endpoint 对象,endpoint 中记录的是 service 和 pod 之间的关系。

每个 Node 节点运行了kube-proxy 来监控 pod,当 pod 的 IP 地址发生变化时,kube-proxy 会去更新对应的 endpoint,此外 service 的服务名字也会被注册到 coreDNS 服务,这样服务之间通过 service 的名字就可以互相访问了。

当服务消费者(pod A)访问服务提供者(pod B)的时候,根据kube-proxy 不同模式下的负载均衡策略(比如 iptables),来决定将请求转发到哪个 pod B。

image.png

使用 Feign

在 Spring Cloud 体系中,一般还会引入 Feign 来简化服务间的 REST 调用:指定@FeignClient 的参数 name=微服务名称,Feign 就可以根据微服务名称找到对应的服务提供者进行调用。

在k8s 中,如果想要继续使用 Feign的方式调用,可以通过指定@FeignClient 的 url={namespace}:${targetPort} 的方式,这样就能利用 service 的负载均衡能力,请求到对应的服务提供者,实现最小化的 Spring Cloud 的改造。

使用spring-cloud-kubernetes

Spring Cloud 官方推出的开源项目,可以更简单的将 Spring Cloud 运行在k8s 环境,但是服务发现本质上也是类似 Eureka 的实现方式,通过接口调用k8s 服务,获取k8s 中微服务所在的所有节点,再在客户端实现了服务发现和负载均衡的能力。

配置中心

基于SpringCloud实现

有多种配置中心可以集成到 Spring Cloud,比如 Spring cloud config、Apollo、Nacos。微服务启动后会监听配置中心的配置,当配置发生变化时,微服务可以感知,并实现动态刷新的能力。

基于K8S实现

K8S 提供了 configMap/secret,微服务可以利用它们作为配置中心。启动微服务的 pod 后可以获取configMap/secret 的配置,但是不能热更新,需要自己实现监听配置的能力,或者也可以通过引入 jar 依赖“spring-cloud-starter-kubernetes-config”简单地实现热更新的能力。

软件发布

将微服务发布到生产环境中是,可以选择多种不同的发布策略进行发布,比如重新部署、蓝绿发布、灰度发布、滚动发布...。

其中较为复杂的灰度发布,可以利用网关根据流量权重、特定请求头、来源 IP 或地址段、特定客户端等条件,将访问请求引流到不同版本的服务。

SpringCloud实现灰度发布

以 Zuul 作为网关为例,开发者通过实现 zuul 的过滤器 Filter,可以实现对请求 URL 进行转发前的处理,按不同的条件选择灰度的版本进行路由。

image.png

K8S 实现灰度发布

K8S 经常会使用 Ingress 作为网关将内部的微服务发布到公网,而 Ingress 本身就可以支持根据不同的请求标识转发到不同的服务,相对 zuul 来说对代码无侵入,不需要开发者自己在业务层实现灰度发布的能力。

image.png

全链路灰度发布

从上面的两张图可以看出,无论是通过Zuul 的过滤器来实现的灰度发布,还是通过 Ingress 来实现的灰度发布,都不能做到全链路的灰度的(如果微服务内部不通过 service 互相调用,而是通过 Ingress 来调用,理论上也算能实现全链路灰度,但是这样需要牺牲少部分的性能,架构设计上也不符合要求)。如果需要做到全链路灰度,还是需要开发者对代码做出改造,实现请求拦截器,对服务间调用的请求 URL 进行拦截,按不同的条件选择灰度的版本进行路由,同时可以配合配置中心动态刷新的能力,自行实现灰度发布的能力。

image.png

除此之外,当然还可以选择使用 istio,实现无代码侵入的灰度发布能力。

你可能感兴趣的:(云原生技术:微服务架构设计的探索)