概述:
云原生浪潮已经到来,越来越多的公司选择向云原生转型,架构逐渐向微服务演进。此次演讲的主旨在于分享微服务的相关概念和它的优缺点,以及在云原生浪潮下使用 ServiceMesh 技术治理微服务的探索实战。
微服务是什么
微服务是一种架构风格,使用微服务架构在设计应用的时候我们不再是一个大的单体服务,而是拆分为一堆粒度较小服务,这些服务之间通过 API 进行通信。
并且这些服务具有以下这一些特点:
高可维护性和可测试性:
微服务的架构风格让我们将大的单体拆分为颗粒更细的服务,每个服务的功能更单一内聚,所以维护和上手简单,测试起来也更加容易。
服务和服务之间是松耦合的
服务和服务之间通过 API 进行通信,服务边界更加清晰
服务是独立部署的
这样可以做到良好的故障隔离
围绕业务能力网站的服务
微服务里面划分服务和网站服务的方式是按照具体的业务能力来,根据康威定律我们维护微服务的团队也是围绕业务能力网站的团队。
小团队进行管理和维护
微服务-优点
敏捷
对于每一个微服务来说,他需要理解的上下文环境更加简单,可以便于我们更快独立地进行开发,迭代,测试,和部署。
技术栈自由
从概念上讲微服务之间通过接口进行通信所以服务内部的实现互不依赖,服务之间完全可以技术栈独立,便于我们技术栈进行演进。
灵活扩展
微服务的服务粒度比较小,所以对于具体业务需求发生变化,可以灵活的进行扩展保持服务的可用性
弹性,故障隔离
微服务是独立进行部署的,如果某一个服务出现问题,可以通过服务降级的方式避免整个应用的不可用从而故障隔离的效果
既然微服务这么好为什么不是所有人都上了微服务?
这里是一个关于大部分开发者在使用微服务的时候遇到的挑战统计:
有56%的人认为每一个额外的微服务提升了运维上的挑战
47 % 的人认为他们的团队架构还不能适应微服务架构
45 % 的开发者认为难以定位到性能问题更本原因
45 % 的开发者觉得没有构建和管理微服务的技术
微服架构架构不仅有这些挑战还存在一些缺点:
缺点
微服务的第一个缺点就是因为服务拆分后服务数量增多所带来的问题,比如构建、测试、部署、管理、 问题定位等等,虽然对于每一个服务来说这些问题都变得更加简单,但是作为一个整体应用来看,复杂度就会提升很多。
另外一个问题就是服务分布式所带来的额外逻辑处理,比如分布式一致性
最后就是微服务会引入大量服务治理逻辑,这是原来单体应用不太关注或者很少关注的问题
具体有哪些服务治理的问题:
服务注册与发现
身份验证与授权
服务的伸缩控制
反向代理与负载均衡
路由控制
流量切换
日志管理
性能度量、监控与调优
分布式跟踪
过载保护
服务降级
服务部署与版本升级策略支持
错误处理
我们团队是怎么开始微服务化的:
我所在的团队我们最开始的业务最开始的形态也是一个大的单体应用,在经过两到三年的迭代以后项目规模非常的庞大,有超级多的各种业务模块,定时任务。上线一个新功能需要修改到的模块可能特别多,依赖耦合严重,而且存在很多隐式约定,以至于新人上手非常的困难不知道何从下手,渐渐地我们发现还有一些问题比如一些特定的定时任务或者业务接口异常可能会导致整个服务挂掉。以前最频繁遇到的就是有一个计量模块,它会在每天凌晨和月初同步大量供应商的 meter 数据,这些大量的数据写入就会直接把 db 给打爆。所以很自然而然的就会说想到去拆分服务。服务拆分好了我们就得到多个子服务,那剩下的事情就是需要把这些n个服务部署到m台机器上,这是一个很头疼的问题,正如同我前面讲的所有微服的缺点你都会碰上,比如服务发现,节点管理,构建部署,等等。当然没有什么问题是我们程序员解决不了的,对于每一个问题我们都可以引入相应的工具来解决,比如服务发现用Zookeeper,或者Consul,构建部署,流量管理引入网关和NGINX。但明显整个过程非常的痛苦,随着这些年容器技术的发展,云原生浪潮已经到来,并且完全改变了微服务的使用姿势。
云原生浪潮
云原生的三个核心技术:
docker 容器本质上是一种进程隔离的技术,解决了构建,部署,运行环境等等问题
kubernetes 技术帮我们解决多容器编排的问题,对应到微服务上我们用它解决服务节点管理,服务发现,调度,伸缩等问题
Service Mesh 帮我们解决了更多微服务治理的问题
Service Mesh 服务间通信的基础设施层
Service Mesh 的出现是一个很有意思的过程,我们可以看下面一张图:
这张图里面我们的微服务除了有我们业务逻辑模块也会有一些模块专门用于处理服务治理的逻辑,比如这里写的断路器模块和服务发现模块。如果我们每一写一个服务都要重新实现这些逻辑显然做了很多重复的工作,所以自然而然就会想到把这些逻辑抽象为一些独立调用的库,这样就解决了大量的重复的劳动,事实上大部分微服务框架就是帮我们做了这些事情。更进一步我们把服务治理的逻辑抽离出来以 side car 的方式和业务服务进行交互就是现在 service mesh 的基本原型。
为什么使用 Service Mesh
Service Mesh架构使现有的服务可以在不改造的情况下引入了服务治理能力
大大降低了中间件的研发投入和演进成本,也降低了业务和中间件的耦合成本
Service Mesh架构为多语言栈提供了服务治理能力,lib 的方式还是会受限于具体的语言和具体的服务框架
目前市面上已经有很多 Service Mesh 架构的实现:
Linkerd(by: Buoyant)
Istio(by: Google, IBM)
Envoy(by: Lyft)
ServiceComb(by: 华为)
SOFA Mesh(by: 蚂蚁)
Nginmesh(by: Nginx)
TSF(by: Tencent)
其中目前最流行的应该就是 Istio
Istio 是什么?
Istio 它是一个完全开源的 Service Mesh 的实现,作为透明的一层接入到现有的分布式应用程序里。它也一个平台可以集成日志、指标,和分布式链路追踪数据。
Istio 提供的三大核心功能
流量管理
Istio 通过简单的规则配置和流量路由就可以让我们实现控制服务之间的流量和 API 调用过程。
安全
Istio 提供了底层的安全通信通道,并为大规模的服务通信管理认证、授权和加密。所以默认情况下我们服务之间的通信就是受保护的,这样我们作为开发者就只需要专注于应用程序级别的安全,而不用担心服务通信的安全问题。
可观察性
服务的可观察性我们可以分为4个维度:
Logging 服务日志
当流量流入网格中的服务时,Istio 可以生成每个请求的完整记录,包括源和目标的元数据。
Metrcis 事件指标
Istio 基于 4 个监控的黄金标识(延迟、流量、错误、饱和)生成了一系列服务指标。
Tracing 分布式追踪链路信息
Istio 为每个服务生成分布式追踪 span,运维人员可以理解网格内服务的依赖和调用流程
Alerting 报警,通知,可以接入第三方平台实现
下面举一个例子来给大家看看在 Istio 里面流量管理是怎么使用的:
讲到 Istio 流量管理,我们先了解下 Istio 中两个核心的资源:
虚拟服务
用来设置服务路由,它的作用是将流量路由给目标地址
目标规则
流量路由到正确的服务服务后,作用于服务实例上的流量策略和规则
在我们的例子中有三个服务:
服务 Resource 是我们业务中资源服务它负责具体资源的创建删除和管理的功能
服务 Order 负责创建订单,订单发货,订单管理等功能
第三方支付服务 Pay 订单服务通过调用第三方支付服务支付订单
接下来我们一一来实现流量管理中比较重要的几个功能:
超时
在服务请求长时间无法返回结果,则需要设置超时机制,超过设置的时间则返回错误信息。这样做既可以节约等待时消耗的资源,也可以避免由于级联错误引起的一系列问题。
接下来我们需要对订单回调资源发货设置超时机制
apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
name: resource
spec:
hosts:
- resource
http:
- match:
- uri:
exact: /resource/delivery
- method:
exact: POST
route:
- destination:
host: resource
subset: v1
timeout: 10s
YAML
重试
网络环境不稳定的情况下,会出现暂时的网络不可达现象,这时需要重试机制,通过多次尝试来获取正确的返回信息,提高服务的可靠性。
比如我们的某些资源服务可能是第三方供应商提供的,由于外部网络可靠性较差,可能会偶发超时或者网络错误
我们通过virtual service 中的路由参数来设置服务重试的策略,这样失败的请求就会在这里进行重试从而提高了resource服务的可靠性。
apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
name: reserouce-retries
spec:
hosts:
- resource
http:
- route:
- destination:
host: resource
retries:
attempts: 3
perTryTimeout: 10s
retryOn: 5xx
YAML
熔断和限流
熔断则是指当服务到达系统负载阈值时,为避免整个软件系统不可用,而采取的一种主动保护措施,微服务系统而言,熔断尤为重要,因为我们的服务链路长,它可以使系统在遭遇某些模块故障时,通过服务降级等方式来提高系统核心功能的可用性,得以应对来自故障、潜在峰值或其他未知网络因素的影响
我们的订单服务是除了创建订单和订单发货这些功能还有订单管理的功能,当有大量订单管理请求的时候可能会导致订单服务的不可用,那为了规避这种情况我们就需要在订单服务上增加限流和熔断功能
我们可以通过在目标规则配置http 参数来简单的实现熔断和限流功能,下面是具体的yaml 配置:
apiVersion: networking.istio.io/v1alpha3
kind: DestinationRule
metadata:
name: order
spec:
host: order
trafficPolicy:
connectionPool:
tcp:
maxConnections: 100
http:
http1MaxPendingRequests: 100
maxRequestsPerConnection: 100
YAML
灰度升级
灰度升级允许我们在线上发布功能的时候,仅对部分用户开放新的功能,当没有异常功能验证通过后在逐步对所有用户开放新功能。
apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
name: order
spec:
hosts:
- order
http:
- match:
- headers:
cookie:
regex: "^(.*?;)?(email=[^;]*@qiniu.com)(;.*)?$"
route:
- destination:
host: order
subset: v2
weight: 50
- route:
- destination:
host: order
subset: v1
---
apiVersion: networking.istio.io/v1alpha3
kind: DestinationRule
metadata:
name: order
spec:
host: order
subsets:
- name: v1
labels:
version: v1
- name: v2
labels:
version: v2
YAML
好的经过我们上面的一系列配置我们就完成了对资源,订单和支付服务实现简单的流量管理功能
微服务设计的误区:
不要过早应用微服务
过早应用微服务架构可能让我们陷入过度设计的误区,在开发的过程中更关注于架构和基础组件本身而忽略了业务本身的关注点,引入了不必要的复杂度。在构建一个新的项目和应用的时候我们的起点应该是单体应用,在使用单体架构的时候保持良好的模块化设计,便于我们在有需要的时候快速的拆分演进成微服务架构。
简单用远程调用替换函数调用
在将我们的服务拆分的时候,大部分人会将原来模块间的函数调用简单替换为远程调用来完成服务剥离。这样其实原有的耦合并没有完全解耦,只是换了一种耦合的形式,而远程调用的方式比起函数调用会更加的不可靠;最终让我们的应用沦为一个超级庞大的分布式单体应用。正确的做法是我们需要对拆分的服务进行抽象,隐藏内部的实现细节,对外只暴露网络协议和数据契约。
不要过分细粒度化
在做微服务拆分的时候,其实很多人都会有个疑问,我们的服务到底应该拆分多细粒度?是不是越细越好?当然不是,如果服务粒度过细,即使我们拥有更强大的服务治理工具,大量细粒度的服务也会分散我们的精力,我们需要花大量的时间去定义这些服务和划分他们之间的边界。正确的合理粒度微服务的方法有很多比如:
根据业务规模和团队规模
可靠性和性能拆分