你好,我是孔令飞。

应用、系统资源、应用生命周期管理这 3 个维度,构成了我们对云的所有诉求。上一讲,我从系统资源维度,介绍了虚拟化技术的演进之路。这一讲,我会介绍下应用维度和应用生命周期管理维度的技术演进。

应用软件架构是用来构建应用的,不同的软件架构,构建应用的方式、效率,以及所构建应用的可维护度、性能都是不同的。随着技术的不断更新迭代,应用软件架构也在不断往前演进。这一讲我们就来看看,应用软件架构都有哪些,这些软件架构都有什么特点,以及它们之间是如何演进的。

至于应用生命周期管理维度,我在 09讲 中已经介绍了应用生命周期管理技术的演进,这一讲也会再补充一些核心的技术,比如日志、监控告警、调用链等。

接下来,我们就先来看下软件架构的演进之路。

软件架构的演进

软件架构技术演进如下图所示:

最开始,我们使用单体架构来构建应用,后面逐渐演进为SOA架构。不管是单体架构,还是SOA架构,都很难满足互联网时代应用快速迭代的诉求。所以,在互联网时代,应用软件架构又演进成了微服务架构。当前我们正处在微服务架构阶段,也有很多团队的业务正在尝试使用Service Mesh替代微服务架构中的一些功能。

随着Serverless云函数的诞生,也诞生了一种新的软件架构,FaaS架构。这里我先简单介绍下它,后面再详细讲。FaaS架构因为限制多、使用场景局限,目前还仅仅适用于云函数这种系统资源形态,我个人认为它不会成为未来主流的软件架构。还要说明下,业界目前并没有FaaS软件架构这个说法,大家说到FaaS,一般指的都是云函数这种技术形态。这里为了方便描述,我们先这样表达。

接下来,我仍然以技术演进的思路,来介绍下这些软件架构。首先来看下最早的单体架构。

单体架构

在最早的时候,我们用的软件架构是单体架构。在单体架构中,我们会将应用程序的所有功能都存放在一个代码仓库中,并且发布时,也是发布整个代码仓库的代码和功能。

在单体架构中,应用软件一般会包含四层,分别是表示层、业务逻辑层、数据访问层、数据库,如下图所示:

这里简单介绍下每层的功能。

单体架构的优点是应用开发简单,技术单一,测试、部署相对简单明了。因此它比较适合用户访问量较小的应用服务端。但它的缺陷也是非常明显的。随着业务的发展,项目越来越大,单体架构会带来开发效率低、发布周期长、维护困难、稳定性差、扩展性差等问题。另外,单体架构的技术栈也不易扩展,只能在原有的基础上,不断地进行局部优化。

SOA架构

为了解决单体架构在业务代码变大时带来的各种问题,SOA架构出现了。

SOA架构是面向服务的软件架构,它的核心理念是:基于SOA的架构思想,将重复共用的功能抽取为组件,以服务的方式给各系统提供服务,服务之间通过ESB企业服务总线进行通信。如下图所示:

SOA架构中,主要有两个角色,分别是服务提供者和服务消费者。服务消费者可以通过发送消息来调用购买商品、申请售后的服务,这些消息由ESB总线转换后,发送给对应的服务,实现 SOA 服务之间的交互通信。

SOA架构主要适用于大型软件服务企业对外提供服务的场景,至于一般业务场景就并不适用了。这是因为,SOA服务的定义、注册和调用都需要繁琐的编码或者配置来实现,并且ESB总线也容易导致系统的单点风险,并拖累整体性能。

微服务架构

在互联网时代,越来越多的企业推出了面向普通大众的网站和应用。这些企业没有能力,也没有必要构建和维护ESB企业服务总线。于是,基于SOA架构,又演进出了微服务架构。

微服务架构由Matrin Fowler在2014年提出,它的理念是将业务系统彻底地组件化和服务化,形成多个可以独立开发、部署和维护的服务或应用的集合。微服务之间采用RESTful等轻量的传输协议,来应对更快的需求变更和更短的开发迭代周期。如下图所示:

微服务架构提出得比较早,但在这几年才逐渐流行起来。这是什么原因呢?一方面,微服务架构基于自身的特点,确实能够解决其他软件架构中存在的一些问题;另一方面,Docker + Kubernetes等云原生技术这几年也发展了起来,能够很好地支撑微服务的部署和生命周期管理。

总体来说,微服务架构有下面这几个特点:

微服务架构有很多优点,但也存在着问题。因为一个应用被拆分成一个个的微服务,随着微服务的增多,就会引入一些问题,比如微服务过多导致服务部署复杂。微服务的分布式特点也带来了一些复杂度,比如需要提供服务发现能力、调用链难以追踪、测试困难,等等。服务之间相互依赖,有可能形成复杂的依赖链路,往往单个服务异常,其他服务都会受到影响,出现服务雪崩效应。

目前业界针对这些问题也有一些标准的解决方案,比如,可以通过Kubernetes、Helm和CI/CD技术,解决微服务部署复杂的问题。至于微服务的分布式特点所带来的复杂性,可以通过一些微服务开发框架来解决。一些业界比较知名的微服务开发框架,比如Spring Cloud和Dubbo,已经很好地解决了上面的问题。另外,云原生相关的技术也可以解决微服务调用链跟踪复杂、故障排障困难等问题。

另外,在我的日常开发中,经常会有开发者把SOA架构和微服务架构给搞混,所以我在这里再来介绍下二者的相同点和不同点。

微服务架构是SOA架构设计思想的另一种实现方式,这是二者相同的地方。至于区别,主要有三个。理解了下面这三点,以后你在开发中就很容易区分它们了。

Service Mesh

在讲微服务的时候,我提到微服务架构的一些问题可以通过一些微服务开发框架来解决,比如Spring Cloud和Dubbo。但这里也有个问题:这些框架通常是侵入式的,比如语言只能限制在Java,并且开发的时候要按框架的指定方式来开发。这个理念跟微服务的独立技术栈也是相反的。

2017年底Service Mesh(服务网格)的出现解决了这个问题,它是一种非侵入式技术,可以提供服务之间的网络调用、限流、熔断和服务监控等功能。Service Mesh类似于TCP/IP协议,无需应用层感知,开发者只需要开发应用程序即可。所以,Service Mesh是致力于解决服务间通讯的基础设施层,它具有下面这几个特点:

Service Mesh目前的发展比较火热,社区有很多优秀的Service Mesh开源项目,例如 IstioLinkerd 等。当前最受欢迎的开源项目是Istio。

Istio是一个完全开源的服务网格,作为透明的一层接入到现有的分布式应用程序里,提供服务治理等功能。它也是一个平台,拥有可以集成任何日志、遥测和策略系统的 API 接口。

Istio的大概实现原理是:每个服务都会被注入一个Sidecar(边车)组件,服务之间通信是先通过Sidecar,然后Sidecar再将流量转发给另一个服务。因为所有流量都经过一个Sidecar,所以可以通过Sidecar实现很多功能,比如认证、限流、调用链等。同时还有一个控制面,控制面通过配置Sidecar来实现各种服务治理功能。

目前Istio的最新版本是1.8,1.8版本的Istio架构图如下:

从图中你可以看到,Istio主要包含两大平面。一个是数据平面(Data plane),由Envoy Proxy充当的Sidecar组成。另一个是控制平面(Control plane),主要由三大核心组件Pilot、Citadel、Galley组成。下面,我来分别介绍下这三大核心组件的功能。

FaaS架构

这几年,以云函数为代表的Serverless技术异常火爆。伴随着Serverless技术的发展,一个新的软件开发模式也诞生了,这就是FaaS架构。

FaaS架构提供了一种比微服务更加服务碎片化的软件架构模式。简单来说,FaaS架构就是把之前一个完整的业务拆分成一个个Function来部署,通过事件来触发底层Function的执行。

Function里可能会调用第三方组件,比如数据库、消息队列服务等,这些第三方组件在Serverless架构中,统称为BaaS(Backend as a Serivce)。BaaS把这些后端的服务能力抽象成API让用户调用,用户不需要关注这些后端组件的高可用、扩缩容等运维层面的点,只需要去使用就可以了。

下面是FaaS架构的示意图:

从这张图里你可以看到,用户通过浏览器、手机、小程序等客户端请求触发器服务,例如API网关、COS对象存储、CLS日志等。这些触发器服务在收到来自用户的请求之后,会触发它们所绑定的云函数,云函数会根据请求量等数据,实时启动多个并发实例。在触发云函数时,也会传递参数给云函数,并在云函数中使用这些参数,进行一些业务逻辑处理。例如,调用第三方的服务,将处理结果保存在后端数据库中。

在我看来,FaaS架构未来不会成为主流,更多的是存在于云函数的场景中。我这么说是因为,如果将应用拆分成一个个Function,这些Function的部署、维护,以及之间的通信会是一个巨大的挑战,从目前来看,还不存在解决这种挑战的技术和条件。另外,FaaS架构也不适合承载一些较重的业务逻辑,比如还没法大规模迁移企业的应用系统。

应用生命周期管理技术:监控告警、日志、调用链

在这门课的 09讲 中,我已经详细介绍了应用生命周期管理技术的演进。这里我们可以再回顾一下:应用生命周期,最开始主要是通过研发模式来管理的,按时间线先后出现了瀑布模式、迭代模式、敏捷模式。接着,为了解决研发模式中的一些痛点,出现了另一种管理技术,也就是 CI/CD 技术。随着 CI/CD 技术的成熟,又催生了另一种更高级的管理技术 DevOps。

其他的细节内容,如果有遗忘,你可以返回 09讲 再复习一下,这里就不再重复介绍了。接下来,对于应用生命周期管理技术,我会补充一些之前没有讲到的重要技术,包括下面这三个:

需要说明的是,这些技术之间不存在演进关系,而是平级的,共同作为应用生命周期管理技术的补充。

监控告警组件:Prometheus

对于应用来说,监控告警功能是必不可少的一项功能,能够让开发者或运维人员及时感知到程序异常,并及时修复。另外,监控也能够收集一些有用的数据,供后面的运营分析使用。云原生技术栈中,也有很多开源的优秀监控告警项目,例如 Zabbix、Prometheus等,其中最受欢迎的是Prometheus

Prometheus是一款开源的、自带时序数据库的监控告警系统。目前,Prometheus已经成为Kubernetes集群中监控告警系统的标配。它具有下面这几个特点:

Prometheus的架构如下图所示:

从上图可以看出,Prometheus 的主要模块包括Prometheus Server、Exporters、Pushgateway、Alertmanager 以及Grafana图形界面。这些模块,有些是可选的,有些是必选的,大部分组件使用Golang编写。下面我来分别介绍下。

Prometheus大致的工作流程是:

  1. Prometheus Server 定期从配置好的 jobs 或者 Exporters 中拉 metrics,或者接收来自 Pushgateway 的 metrics,再或者从其他的 Prometheus Server 中拉 metrics。
  2. Prometheus Server 在本地存储收集到的 metrics,并运行已经定义好的 alert.rules,记录新的时间序列,或者向 Alertmanager 推送警报。
  3. Alertmanager 根据配置文件,对接收到的警报进行处理,发出告警。
  4. Grafana在图形界面中,可视化地展示采集数据。

Prometheus会将所有采集到的样本数据以时间序列的方式保存在内存数据库中,并且定时保存到硬盘上。time-series是按照时间戳和值的序列顺序存放的。每条time-series通过指标名称(metrics name)和一组标签集(labelset)命名,如下所示:

<--------------- metric ---------------------><-timestamp -><-value->
http_request_total{status="200", method="GET"}@1434417560938 => 94355
http_request_total{status="200", method="GET"}@1434417561287 => 94334

http_request_total{status="404", method="GET"}@1434417560938 => 38473
http_request_total{status="404", method="GET"}@1434417561287 => 38544

http_request_total{status="200", method="POST"}@1434417560938 => 4748
http_request_total{status="200", method="POST"}@1434417561287 => 4785

在time-series中的每一个点,我们称为一个样本(sample)。样本由下面三个部分组成。

统一日志管理框架:EFK

我们通过监控告警服务感知到程序异常,这时候需要开发者或者运维人员介入排障。排障最有效的手段,是查看日志。所以,对于一个应用来说,一个优秀的日志系统也是必不可少的功能。

在一个大型的分布式系统中,有很多组件,这些组件分别部署在不同的服务器上。如果系统出故障,需要查看日志排障。这时候,你可能需要登陆不同的服务器,查看不同组件的日志,这个过程是非常繁琐、低效的,也会导致排障时间变长。故障时间越久,意味着给客户带来的损失越大。

所以,在一个大型系统中,传统的日志查看手段已经满足不了我们的需求了。这时候,我们需要有一个针对分布式系统的日志解决方案。当前,业界有不少成熟的分布式日志解决方案,其中使用最多的是EFK日志解决方案。甚至可以说,EFK已经成为分布式日志解决方案的事实标准。

EFK中包含三个开源的软件,分别是Elasticsearch、FlieBeat、Kibana。下面,我来介绍下这三个开源软件:

EFK的架构图如下:

通过Filebeat采集所在服务器上各服务组件的日志,并上传到Kafka中。Logstash消费Kafka中的日志,过滤后上报给Elasticsearch进行存储。最后,通过Kibana可视化平台来检索这些日志。Kibana是通过调用Elasticsearch提供的API接口,来检索日志数据的。

当Filebeat的日志生产速度和Logstash的日志消费速度不匹配时,中间的Kafka服务,会起到削峰填谷的作用。

调用链跟踪组件:Jaeger

在云原生架构中,应用普遍采用微服务。一个应用包含多个微服务,微服务之间会相互调用,这会给排障带来很大的挑战。比如,当我们通过前端访问应用报错时,我们根本不知道具体哪个服务、哪个步骤出问题了。所以这时候,应用就需要有分布式链路追踪能力。目前,业界也有多种分布式链路追踪系统,但用得最多的是Jaeger

Jaeger是Uber推出的一款开源分布式追踪系统,兼容OpenTracing API。这里我们先来介绍两个概念:

Jaeger架构图如下:

Jaeger中有7个关键组件,下面我来具体介绍下。

下面,我通过一个Jaeger官方提供的All in One教程来让你更好地理解Jaeger。具体可以分成两个操作步骤。

第一步,使用jaeger-all-in-one安装Jaeger服务:

$ wget https://github.com/jaegertracing/jaeger/releases/download/v1.25.0/jaeger-1.25.0-linux-amd64.tar.gz
$ tar -xvzf jaeger-1.25.0-linux-amd64.tar.gz
$ mv jaeger-1.25.0-linux-amd64/* $HOME/bin
$ jaeger-all-in-one --collector.zipkin.host-port=:9411

第二步,启动一个HotROD示例应用,产生调用链:

$ example-hotrod all # 第 1) 我们已经安装了 example-hotrod 命令

访问http://$IP:16686/search可以查找调用链(IP是Jaeger部署的服务器IP地址),如下图所示:

查询到调用链列表后,可以点击任意一个调用链,查看其详细的调用过程,如下图所示:

具体如何使用Jaeger来记录调用链,你可以参考Jaeger官方给出的hotrod示例。

总结

最后,我们通过下面这张图,来对整个云技术的演进之路做个整体性的回顾:

通过这张图你可以看到,每种技术并不是孤立存在的,而是相互促进的。在物理机阶段,我们用的是瀑布开发模式和单体架构;在虚拟机阶段,用得比较多的是敏捷开发模式和SOA架构;在容器这个阶段,则使用CI/CD的开发模式和微服务架构。

在Serverless阶段,软件架构仍然采用微服务,不过在一些触发器场景,也可能会编写一些FaaS架构的函数,部署在类似腾讯云云函数这样的FaaS平台上;底层系统资源主要使用Serverless容器,并配合Kubernetes资源编排技术。在一些触发器场景中,也可能会使用云函数。应用程序中的第三方服务(BaaS),也都是越来越Serverless化的服务。应用生命周期管理技术也会演进为CI/CD/CO这种模式,其中CI/CD更加智能化,自动化程度更高。

这张图里,阴影部分是我们当前所处的阶段:容器技术得到了大规模普及,业界也在积极探索Serverless技术,并取得了卓有成效的结果。

课后练习

  1. 了解下Kubernetes的声明式API机制,并思考下,微服务架构之后的软件架构可能是什么样的?
  2. 动手搭建一个Prometheus服务,产生一些数据,并配置Grafana,最终可视化地展示这些监控数据。

欢迎你在留言区与我交流讨论,我们下一讲见。