一 微服务架构与云原生
微服务从 2010 年左右开始兴起。最开始大家会把微服务架构应用在传统 IT 的基础设施,也就是传统的 IDC 或者说物理机上,我们使用这些物理机为我们的微服务架构提供资源,形成一个分布式的系统,互相协作、协同。
随着我们整个的 IT 基础设施的发展,逐步到了云的时代。
我们在云时代做的第一步是云托管,也就是把在传统 IDC 之中的物理机,换成云上的虚拟机以及云上虚拟的弹性资源,对于微服务架构其实改动并不是非常巨大。我们把原来部署在物理机上的架构模型,可以比较容易地转到在云上托管的虚拟机中,也称之为 Lift and Shift 方式。与此同时,在云托管时代,我们也尝试更好利用云上虚拟机的弹性能力,去做一些微服务的自动化水平扩缩容。
随着技术进一步发展,到云原生时代,到底什么不同了?云原生希望能够把像微服务、DevOps 这些架构和理念,能够和云所提供的服务、能力、平台更好地进行融合、进行协同和协作。我们希望云不光光是提供弹性物理资源,比如说存储、计算、网络等资源,而是能够为微服务提供更好的一个运行环境和平台。这个需要我们做到以下两点:
- 对资源层面联合优化,对资源的更优使用。
- 对云服务与平台的充分利用,研发、运维效率的极大提升。
这个其实是云原生想去做的事情,就是让我们的微服务架构和体系,能够和云与云上面的服务、平台,以最佳的方式工作、协同在一起,降本提效。
二 微服务与云原生
如果我们以微服务为中心去看待微服务与云原生,可以从四个点讲一讲它们之间的关系,以及微服务在云原生时代的演进。
- 生命周期的管理。
- 流量治理。
- 编程模型。
- 可信安全。
生命周期
从本质上来讲,微服务就是把单体应用从一个巨型的应用拆分成数个更微小的服务,协作完成原来单体应用所提供的等效业务服务。因此,服务与服务之间会有依赖关系,服务也需要去部署到一个或多个资源上。
如上图所示,大家可以看到,原来的单体应用与资源之间的关系其实是一对一的关系,单体应用的协同也都是一些内部协同,不存在外部动态的依赖。而当我们转换到微服务之后,可以看到这张图会变成网状,变得复杂起来。
因此由于内在原因,超过 50% 的企业会认为微服务架构或采用微服务架构,其中最大的挑战在于更为复杂的运维,也就是更为复杂的生命周期管理。
我们在今天讲云原生的根基——容器与容器服务,就在于帮助微服务解决生命周期管理以及运维管理难题,那是怎么解决的呢?
我认为分为两个部分:
第一部分,不同微服务之间可能存在一些异构,为了让每一个团队在微服务体系下发挥最大效能,我们允许不同团队采用不同的编程语言,甚至不同的运行环境来去运行这些微服务。因此,我们在运维和管理微服务时,最初其实并没有一套统一的标准去处理的异构环境,这也是为什么后来容器这项云原生技术变得流行起来,它的一个重要作用就是通过一层标准的封装以及标准的运行时,来标准化微服务部署。这样从生命周期管理的角度来看,每一个微服务之间的差异就会变少,共同点变多。
基于这个技术,后来我们又开发出另一层:容器平台,也是今天比较流行的,像 Kubernetes 。它的作用是帮助把已经标准化的微服务最便捷地运行到底层资源上面。我们讲到的存储、计算、网络都通过 Kubernetes 这层进行了统一抽象和封装,让已经被容器统一的微服务能够直接运行到 Kubernetes 平台。因此,运维人员不用再苦恼如何去把某个微服务分配到具体的某一个资源或计算单元上去。通过容器和容器平台,大大简化了微服务本身的生命周期管理,简化了微服务自身的运维管理问题,也促进了微服务更多地被企业所采用。
如果更微观地去看,容器和容器平台具体给微服务的生命周期还提供了哪些帮助呢?
例如 Kubernetes 引入了一个非常有意思的概念,叫做 pod,一个 pod 实际上是一组容器的集合,在一个 pod 中可以运行一个或多个容器。一般来讲,当我们采用微服务架构时,会把微服务的主体运行在主容器中,主容器的生命周期跟 pod 自身的生命周期是一个耦合的状态。
除此之外,我们还会运行一些边车容器或者叫 Sidecar 容器,为主容器提供一些辅助功能,比如说日志采集、网络代理、身份鉴权等。我们都可以把它放 Sidecar 容器中,这样微服务具备了 Super power,一种超能力。它除了自身提供的核心业务服务以外,我们还可以装上盔甲,也就是动态添加一些额外的辅助能力,让微服务管理变得更强健更强壮。
另一方面,其实 pod 这个模型还提供了一些非常有用的功能:
- 状态信息。Pod 会提供一个标准接口显示运行状态。例如,是否已经准备好接收流量,如果准备好接收流量,那么从 ingress 流量就可以打到微服务上。如果运行状态不良,我们可以尝试对这个容器进行修理、重启或删除,甚至是换到另一个计算单元上去运行,为微服务整体的稳定性提供了保障。
- 地址服务。每一个 pod 都有一个标准化的 DNS 地址服务,可以被统一的寻址。这样对于需要统一暴露出来 API 的日志、监控、追踪能力都有着非常大的帮助,可以根据这个 DNS 的地址来访问 pod 所暴露的可观测性信息,便捷及时的发现运行时问题。
所以我们可以看到容器平台及容器其实在微观上也帮助了微服务具有更多能力、具有更强的健壮性以及具有更好的可观测性。
在这,如上图所示,在微服务运行时这一方面,容器平台也提供了非常有效的能力帮助我们做 Day 2 的微服务更新、发布、扩容等操作。比如 Kubernetes 提供了几种比较典型的升级或是扩容策略:Rolling deployment、Blue-green release 等等,这些都有效简化了我们的运维工作,提高了运维本身的确定性和自动化效率。
总而言之,在生命周期这一方面,通过使用容器及容器平台技术,大大提高了我们对于容器生命周期管控的标准化及自动化程度,节约了人力成本,提升了效率,让更多的企业愿意使用微服务这种技术。
流量治理
微服务一方面是把原来在静态编译时产生的能力与能力之间的关联关系,通过架构拆分推演到动态的运行时。因此在运行时,服务与服务之间是需要进行通讯、协同,才能完成某一项具体的业务功能。当大家进行通讯、协同时,就一定要对其通讯过程进行管理,或者说要进行流量管理。例如,我们要知道怎样从一个微服务找到另一个微服务,以及怎样能保证一个微服务找到最佳的微服务实例跟它进行通讯,这是一个比较复杂的过程,其中包括 RPC 能力、服务注册发现能力、动态配置管理能力以及服务降级能力等等。
为了减轻业务开发同学的负担,不用重复的在每一个微服务中写一遍微服务的流量管理的通用能力,因此大家开发了很多框架,比如在 Java 体系中,著名的 Spring Cloud 提供了一个分布式微服务管理框架;在 Go 语言的开源生态中也有像 Go Mirco 这样的体系;在阿里巴巴内部我们也有像 HSF 这样的体系发展起来的微服务治理框架。
因此,从抽象层面可以看到一个服务包含了两个层面:
一个层面是本身的业务逻辑,也就是由微服务业务开发人员去编写的,功能实现与业务实现相关的代码。
另一个层面是为了实现微服务与微服务之间通讯、流量、服务治理的代码,我们会将其抽象成一个框架,如下图中标出的 Spring Cloud。这样的抽象带来了一个问题,就是所有的通用能力都依赖于这个具体的框架。
假设在公司之中,除了 Spring Cloud 之外,我们去引入另外一些服务框架,如阿里巴巴 HSF 如果希望和 Spring Cloud 框架上面编写的微服务进行通讯的话应该如何去操作?这就要求 HSF 与 Spring Cloud 之间互联互通以及协议之间的互相理解。但其实这些框架之间往往并不具备这个能力。更大的一个问题可能在于,云原生时代我们允许这些微服务的研发能用不同开发语言及模型来进行编程。因此,框架之间的系统并不是不是一对二的关系,也不是 仅仅是 Spring Cloud 与 HSF 的关系,可能是 Java 体系与 JavaScript、Python、Go 体系这些微服务框架都需要打通的问题,它变成了一个 N to M 的 problem,来解决多语言、复杂环境中微服务的治理与管理问题。
这时,当我们有了容器、容器平台、Pod 这些抽象,能够提供一个平台,而不是必须要完全依赖于业务中的代码或 框架时,有没有更好的办法来解决刚才提到的问题?
现在有一个比较流行的概念叫 Service Mesh——服务网格。它的本质就是为了更好地解决流量治理在多语言、多环境场景下的问题,它的主要思想如下:
第一就是希望把流量管理的这些框架能力从耦合在业务的二进制中抽象、剥离出来,形成一个流量管理的单独进程,并以 Sidecar 的模式部署在 Pod 中。通过操作系统级别的透明流量劫持工作,把所有的微服务之间的流量劫持到 Sidecar 中,然后通过 Sidecar 与 Sidecar 之间通讯进行流量的转发与管理。这样问题就简单多了,我们只需要让流量管理的 Sidecar 之间互相通讯、能够进行互联互通。目前比较知名、流行的开源流量劫持和管理 Sidecar 实现叫做 Envoy。
当然,单单有了这层流量劫持与管理还是不够的,还需要管控平面的支持。比如原来微服务体系做的服务注册、服务发现以及流量观测还是需要的,这些策略和规则需要下发给流量管理的 Sidecar 代理。因此,我们还需要构建一个管控平面来管理在 Pod 中部署的流量管理的数据平面的单点,让它们形成一个网状,形成一个集群。所以我们需要有一些管控平面的能力,在开源中比较流行的一个管控平面实现叫 Istio。主要实现了三个能力:流量的配置、流量的安全、流量的观测。
我们认为在云原生这个逐渐平台化的时代,大部分新的应用及场景都会尝试选用基于 Service Mesh 的技术进行微服务的流量治理。
编程模型
请求驱动
请求驱动,也就是支持基于请求的动态弹性伸缩并且简化请求处理逻辑。有些同学可能把这个模型称之为 Event-driven,也就是事件驱动,但是请求驱动实际是事件驱动中的一个分支。
什么是请求驱动呢?从传统的微服务架构看,当一个外部系统请求进来后,一般都会经过一个 L4/L7 的负载均衡,然后给到不同的微服务实例上面。在同一个微服务实例本身进程的内部,一般会有两块逻辑,第一块逻辑是请求管理,它可能是一个 HTTP Server 和一些 Handlers,有一些队列管理、请求分发等能力。这些请求最终会提交到第二个逻辑部分,processor 也就是真正的请求业务处理逻辑中,真正的处理并且相应这些请求。
这个架构会带来两个问题:
- 请求管理的逻辑在不同的微服务框架实现中都要去写一遍,比如 Java、Go 或 Python 都要有自己的请求管理逻辑。
- 请求管理和请求处理形成耦合关系。因此,在这个架构下不存在一个全局独立的可以感知请求以及进行流量管理的控制层。只有到了微服务实例自身的处理层,才能解析这个请求。这时候即便这个微服务实例已经过载,也很难把请求再转发给其他微服务实例进行负载均衡了。
请求驱动系统就是尝试去解决这两个问题,我们尝试在微服务中做一个请求驱动的解耦操作。
首先把外部系统传输过来的请求都进行一次标准化,我们有一个适配器,进行标准化之后,把它放到一个请求负载均衡器中,这个负载均衡已经能理解请求本身的语义,它就能驱动请求处理的逻辑进行请求处理。当请求处理单元不够时,可以通过请求处理的管理器进行扩容;当请求处理的逻辑单元比较多时,还可以进行缩容,这样就可以节约成本。另外,开发人员也不用再去实现请求管理逻辑,降低了开发成本。
刚才讲的请求驱动模型可以分为三部分:
- 请求的标准化
- 请求的路由
- 请求的处理
其实,如果把外部系统的请求标准化,加上请求路由、处理管理,而不包含业务代码,就会组成我们常说 Serverless 概念。这也就是一种把微服务体系和平台化的 Serverless 体系融合的一个过程。
Serverless 平台有阿里云 FaaSS、SAE,AWS 有 Lambda、Google 推出了 CloudRun、Azure 有 Functions。如果大家希望自己能够构建一个 Serverless 平台,如请求标准化,也有像 Cloud Events 这样的开源请求标准, Knative 这样的处理路由以及处理请求的水平扩展能力组件,可以利用 Kafka 或者 RocketMQ 来做请求本身的持久化以及请求本身的转发。
分布式运行时
在编程模型中的分布式运行时,也就是我们希望微服务能够有更好的多语言环境支持、环境可移植性并能做到极速启动。
单体应用时代,我们的业务代码跟中间件代码耦合在一起,而且是一份部署的实例;当到了微服务时代,通过像 Service Mesh 服务网格进行流量管理之后,可以看到我们的业务代码和流量管理代码实际上是可分离的,只有部分中间件的代码仍然和部分业务代码耦合在一起形成一个二进制。
为了能够做到充分的多语言能力支持、环境的可移植性,我们希望能够把剩余的中间件代码也从业务中解耦出来,这项技术叫做运行时解耦。它的根本理念是通过向业务代码提供一个标准的可扩展 API,并且是一个多语言可支持、轻量级的 API,通过调用 API 来实现中间件的功能,我们把中间件的能力像流量管理一样下沉到一个 Sidecar 中,用一种语言进行统一实现,然后设计一个可拔插的模式,让大家能够拔插更多中间件的能力。
Dapr
这项技术比较领先的一个实践是微软去年推出的一个分布式运行时,叫做 Dapr。
Dapr 向业务的代码暴露了两个 API,一个是 HTTP 的 API,另外一个是 grpc 的 API。这两个 API 都非常轻量级,并能够跨多种语言,Dapr 本身作为分布式运行时,可以对接多种中间件系统,向上通过标准的 API 屏蔽不同系统之间的差异性,提供一定的编程界面界面统一。代码实现上把中间件的代码从应用级别抽离到 Sidecar 中,如上图所示,我们在业务上面只需要写业务的代码,其他代码几乎都被基于 Dapr 的平台所接管。
Dapr 本身分为两部分:第一部分是 Dapr 向 Application 暴露的 API,另一部分是 Dapr 的框架层。
通过 Dapr 的框架层,我们可以接入各种不同的 Dapr 相关实现:Resource bandings 如 Kafka/SQS、管理数据的如 Redis/cassandra、或者我们去做 Publish & subscribe 如 RabbitMQ、甚至 Distributed Tracing 如 Prometheus/Open tracing 等等,都可以接入到 Dapr 这套框架里,然后通过统一的标准 HTTP、gRPC API 提供给业务代码、应用代码去使用,这样就做到了业务代码与中间件、流量管理能力的更彻底的解耦,应用的开发人员也能够更专注、更自由地去关注业务代码研发。
可信安全
因为微服务与微服务之间需要有网络通讯,需要有安全可信的认证。传统做法就是把微服务放到一个可信网络环境中,假设网络环境中所有通讯、执行的应用或微服务都是受信的,它们可以自由通讯或交流,可能有一些轻量级的鉴权与授权进行一定的安全防护措施。
但这个模型会带来一种风险,当有微服务入侵到这个可信网络之中,它会对整个微服务体系造成极大的安全隐患,因为可信网络中的内部防范一定程度上是比较欠缺的。
另外,经常会有可信网络与可信网络之间的微服务互相调用或鉴权的问题,常见解决方法是通过 VPC peering 或者通过 gateway 网络的网关打通。利用一个可信通道保证微服务与微服务之间的网络层面可信的调用。但同样引出一种问题,比如在另外一个可信网络中被入侵,便可以攻击到其它相连的可信网络,也增大了受攻击的可能性。
在微服务时代,我们思考是否有更好的解决方式,不去假设网络是完全可信的环境。因此,提出了一个叫做微服务或应用可信安全。也就是微服务与微服务或者机密文件之间的每一次通讯,都基于身份体系。微服务会提供给它所沟通、协同的目标对象一个身份认证。就好比我们通过浏览器访问 HTTPs 网站,这些网站需要提供证书,我们的微服务也需要提供一个自身的身份认证,这样接收方就可以通过平台进行认证且查看授权。只有这些认证都通过,才会和开始微服务的通讯。
实际上,利用平台的特点在网络层面内部以应用为中心,又建设起一套基于统一身份认证授权的体系。
但这个体系还是比较难构建的,相当于要在不完全受信的网络中,需要有一个可以信任的平台层或中间层,也就类似 PKI 中的 CA 机制。在 HTTPs 上,比如说我们要信任一个 CA,可能需要提前讲证书预置在操作系统中,从安装时就要预置好,才能提供一个更为安全的端到端的可信,否则是无法构建好这个信任链的。同样的,在云环境中也需要这个机制。云本身是一个可控平台,通过从云最底层的硬件机制,到云上运行的 OS 上,再到最终向上部署微服务的平台层,我们都要建立起来了一个可证的授信链,最终给每一个微服务提供自身的身份 ID 以及授权可证的授权信息,这样微服务才能提供统一可验证、可证实的身份。也就是说在云原生时代,通过平台思维能够给微服务安全提供更大的价值。
另外在平台与平台之间或网络与网络之间,也可以通过平台级别的可信授信来建立关联的关系,让不同平台之间的微服务也能产生信任关系。为了打通不同的平台,有一个开源项目叫做 spiffe,它提供了统一的标准身份 ID 以及如何统一标准地进行授权信息的可证和授权信息的认证。
三 EDAS
其实上面讲了那么多,可以发现在开源开放领域不停地有新的技术衍生出来,不管是与微服务流量治理相关,还是与微服务生命周期相关,又或是微服务的可信安全、编程模型,如果大家自己尝试去构建这种能力或平台,一般还是需要花费非常大的时间和精力的。
因此阿里云最近升级了云上的服务——EDAS,把 EDAS 从传统的面向微服务的管理体系,基于云原生的基础设施变成了一个云原生应用 PaaS,提供容器的生命周期管理、微服务治理、可观测性、安全流量治理等能力,让阿里云用户能最大化享受到微服务在云原生时代的红利,同时又不需要花费时间去构建这样的平台。
因此推荐对云原生微服务感兴趣的同学可以尝试使用 EDAS。
四 云原生微服务
在云原生时代,微服务的特点:
- 平台化,利用云作为一个平台,如微服务架构进行更多的赋能。
- 标准化,我们希望微服务本身的部署、运维,微服务之间与其它服务之间的通讯都能做到标准化,让服务与服务之间的互联互通变得更容易,服务能够跨到不同的平台上,做到一次编写、一次定义、多处运行。
- 微服务轻量化,让研发人员关心核心业务代码、业务逻辑的研发,而不是复杂的微服务治理相关的逻辑研发。
- 微服务的产品化。我们希望能构建微服务相关的产品,以产品化的方式支持大家使用微服务架构,让它变得更好用、更易用。
五 全球开源微服务框架活跃度报告
我们最近花了很大精力收集在云原生时代比较活跃的一些微服务项目,然后发现了几个特点:
- quarkus 这个项目非常活跃,它可以帮助 Java 或 Java 生态尽量轻量化,适应云原生时代我们对弹性和高效启动速度的要求。
- Spring Cloud 的上升趋势还是非常好的,作为 Java 里非常典型的微服务框架,在阿里巴巴我们也是在 Spring Cloud 生态中进行深耕,现在最流行的一个Spring Cloud 云服务提供厂商就是 Spring Cloud Alibaba。
- 最后像 Dubbo,还有阿里正投资很多人力去共建的 Dapr 项目其实也比较活跃。