mirror of
https://github.com/cheetahlou/CategoryResourceRepost.git
synced 2025-11-19 15:43:44 +08:00
del
This commit is contained in:
166
极客时间专栏/geek/Serverless入门课/基础篇/01|定义:到底什么是Serverless?.md
Normal file
166
极客时间专栏/geek/Serverless入门课/基础篇/01|定义:到底什么是Serverless?.md
Normal file
@@ -0,0 +1,166 @@
|
||||
<audio id="audio" title="01|定义:到底什么是Serverless?" controls="" preload="none"><source id="mp3" src="https://static001.geekbang.org/resource/audio/72/56/72049d40d625f17c5bffdab43dc24356.mp3"></audio>
|
||||
|
||||
你好,我是秦粤。Serverless目前是大热的话题,相信你肯定听过,但如果你去百度、Google或者维基百科上查的话,你会发现它连个准确的定义都没有。
|
||||
|
||||
作为本专栏的第一讲,今天我就想带你深入地了解下Serverless,看看它都能解决哪些问题,以及为什么难定义。
|
||||
|
||||
## Serverless能解决什么问题?
|
||||
|
||||
理清Serverless要解决的问题其实很简单,我们可以从字面上把它拆开来看。
|
||||
|
||||
Server这里指服务端,它是Serverless解决问题的边界;而less我们可以理解为较少关心,它是Serverless解决问题的目的。组合在一起就是“较少关心服务端”。怎么理解这句话呢?我们依然是拆开来分析。
|
||||
|
||||
### 什么是服务端?
|
||||
|
||||
我们先看Server,这里我用Web应用经典的MVC架构来举例。
|
||||
|
||||
现代研发体系主要分为前端和后端,前端负责客户终端的体验,也就是View层;后端负责商业的业务逻辑和数据处理,也就是Control层和Model层。如果你有过一些开发经验,应该会了解自己的代码在本地开发和调试时的数据流。
|
||||
|
||||
<img src="https://static001.geekbang.org/resource/image/3d/b3/3db954564207537706321daa50b870b3.png" alt="" title="MVC架构的Web应用">
|
||||
|
||||
通常我们会在自己电脑上启动一个端口号,例如127.0.0.1:3001。浏览器访问这个地址,就可以调用和调试自己的代码,但如果我们要将这个Web应用部署到互联网上,提供给互联网用户访问,就需要服务端的运维知识了。
|
||||
|
||||
<img src="https://static001.geekbang.org/resource/image/41/71/4174f5f6485cb1e942782f2ad003d971.png" alt="" title="MVC架构的Web应用">
|
||||
|
||||
通常我部署运维一个应用时,由于要考虑容灾和容错,我都会保障异地多活,因此我会部署多个Web应用实例。每个Web应用实例跟我们在本地开发时是一样的,只是IP改为了私有网络IP。
|
||||
|
||||
随着云服务商的兴起,现在已经很少有互联网企业还在自己维护物理机了。在云服务的运维体系中,各个环节都已经有了对应的成熟的云服务产品或解决方案。
|
||||
|
||||
为了使多个Web应用实例在容灾和容错的场景下稳定地切换流量,我就需要负载均衡服务和反向代理服务。负载均衡服务,正如其名是负责将流量均衡地分配到各个应用机器上;反向代理,常见的就是Nginx,它的任务是从请求中解析出域名信息,并将请求转发到上游upstream的监听地址。
|
||||
|
||||
服务端的边界,就是上图中的整个蓝色部分,它是负责应用或代码的线上运维。Serverless解决问题的边界,就是服务端的边界,即服务端运维。
|
||||
|
||||
### 服务端运维发展史,从full到less
|
||||
|
||||
了解完Server,我们再来看less。
|
||||
|
||||
我们可以先看Serverfull的概念,对比Serverfull和Serverless之间的差别,相信这样可以加深你的理解。Serverfull就是服务端运维全由我们自己负责,Serverless则是服务端运维较少由我们自己负责,大多数的运维工作交给自动化工具负责。
|
||||
|
||||
可能这么说比较抽象,我举个例子来给你讲吧。这个例子有点长,但可以带你很好地了解下服务端运维的发展史,我们后面也会再次用到。
|
||||
|
||||
假设我有一家互联网公司,我的产品是“待办任务(ToDoList)”Web应用——记录管理每个用户的待办任务列表。针对这个网站的研发,我们简化为两个独立角色:研发工程师小程和运维工程师小服。
|
||||
|
||||
做研发的小程,他是个精通前后端的全栈工程师,但他只关心应用的业务逻辑。具体来说就是,整个MVC架构Web应用的开发都归小程负责,也就是从服务端界面View层,到业务逻辑Control层,再到数据存储Model层,整个Web应用的版本管理和线上bug修复都归小程。
|
||||
|
||||
负责运维的小服,则只关心应用的服务端运维事务。他负责部署上线小程的Web应用,绑定域名以及日志监控。在用户访问量大的时候,他要给这个应用扩容;在用户访问量小的时候,他要给这个应用缩容;在服务器挂了的时候,他还要重启或者换一台服务器。
|
||||
|
||||
**史前时代,Serverfull**
|
||||
|
||||
最开始运维工程师小服承诺将运维的事情全包了,小程不用关心任何部署运维相关的事情。小程每次发布新的应用,都会打电话给小服,让小服部署上线最新的代码。小服要管理好迭代版本的发布,分支合并,将应用上线,遇到问题回滚。如果线上出了故障,还要抓取线上的日志发给小程解决。
|
||||
|
||||
小程和小服通过工作职责任务上的安排,将研发和运维完全隔离开来了。好处很明显:分工明确小程可以专心做好自己的业务;缺陷也很明显:小服成了工具人,被困在了大量的运维工作中,处理各种发布相关琐碎的杂事。
|
||||
|
||||
这个时代研发和运维隔离,服务端运维都交给小服一个人,纯人力处理,也就是Serverfull。
|
||||
|
||||
我们可以停下来想想,像发布版本和处理线上故障这种工作这些是小程的职责,都要小服协助,是不是应该小程自己处理?
|
||||
|
||||
**农耕时代,DevOps**
|
||||
|
||||
后来,小服渐渐发现日常其实有很多事情都是重复性的工作,尤其是发布新版本的时候,与其每次都等小程电话,线上出故障了还要自己抓日志发过去,效率很低,不如干脆自己做一套运维控制台OpsConsole,将部署上线和日志抓取的工作让小程自己处理。
|
||||
|
||||
OpsConsole上线后,小服稍微轻松了一些,但是优化架构节省资源和扩缩容资源方案,还是需要小服定期审查。而小程除了开发的任务,每次发布新版本或解决线上故障,都要自己到OpsConsole平台上去处理。
|
||||
|
||||
这个时代就是研发兼运维DevOps,小程兼任了小服的部分工作,小服将部分服务端运维的工作工具化了,自己可以去做更加专业的事情。相对史前时代,看起来是不是小程负责的事情多(More)了?但实际这些事情本身就应该是小程负责的,版本控制、线上故障都是小程自己应该处理的,而小服已经将这部分人力的工作工具化了,更加高效,其实已经有变少(less)的趋势了。
|
||||
|
||||
我们再想想能否进一步提升效率,让小程连OpsConsole平台都可以不用?
|
||||
|
||||
**工业时代**
|
||||
|
||||
这时,小服发现资源优化和扩缩容方案也可以利用性能监控+流量估算解决。小服又基于小程的开发流程,OpsConsole系统再进一步,帮小程做了一套代码自动化发布的流水线:代码扫描-测试-灰度验证-上线。现在的小程连OpsConsole都不用登陆操作,只要将最新的代码合并到Git仓库指定的develop分支,剩下的就都由流水线自动化处理发布上线了。
|
||||
|
||||
这个时代研发不需要运维了,免运维NoOps。小服的服务端运维工作全部自动化了,小程也变回到最初,只需要关心自己的应用业务就可以了。我们不难看出,在服务端运维的发展历史中,对于小程来说,小服的角色存在感越来越弱,需要小服参与的事情越来越少,都由自动化工具替代了。这就是“Serverless”。
|
||||
|
||||
到这里你一定会想,既然服务端都做到免运维了,小服是不是就自己革了自己的命,失业了?
|
||||
|
||||
**未来**
|
||||
|
||||
实现了免运维NoOps,并不意味着小服要失业了,而是小服要转型,转型去做更底层的服务,做基础架构的建设,提供更加智能、更加节省资源、更加周到的服务。小程则可以完全不被运维的事情困扰,放心大胆地依赖Serverless服务,专注做好自己的业务,提升用户体验,思考业务价值。
|
||||
|
||||
免运维NoOps并不是说服务端运维就不存在了,而是通过全知全能的服务,覆盖研发部署需要的所有需求,让研发同学小程对它的感知越来越少。另外,NoOps是理想状态,因为我们只能无限逼近NoOps,所以这个单词是less,不可能是Server**Least**或者Server**Zero**。
|
||||
|
||||
另外你需要知道的是,目前大多数互联网公司,包括一线互联网公司,都还在DevOps时代。但Serverless的概念已经提出,NoOps的时代正在到来。
|
||||
|
||||
Server限定了Serverless解决问题的边界,即服务端运维;less说明了Serverless解决问题的目的,即免运维NoOps。所以我们重新组合一下这个词的话,Serverless就应该叫做服务端免运维。这也就是Serverless要解决的问题。
|
||||
|
||||
## Serverless为什么难准确定义?
|
||||
|
||||
换句话说,服务端免运维,要解决的就是将小服的工作彻底透明化;研发同学只关心业务逻辑,不用关心部署运维和线上的各种问题。要实现这个终态,就意味着需要对整个互联网服务端的运维工作进行极端抽象。
|
||||
|
||||
那越抽象的东西,其实越难定义,因为蕴含的信息量太大了,这就是Serverless很难准确定义的根本原因。Serverless对Web服务开发的革命之一,就是极度简化了服务端运维模型,使一个零经验的新手,也能快速搭建一套低成本、高性能的服务端应用(下一讲,我就会带你体验一下从零开始的Serverless应用)。
|
||||
|
||||
我们现在虽然知道了Serverless的终态是NoOps,但它作为一门新兴的技术,我们还是要尝试给它定义吧?
|
||||
|
||||
## 到底什么是Serverless?
|
||||
|
||||
我在日常和其他同事沟通的时候,我发现大家对Serverless概念的认知很模糊,往往要根据沟通时的上下内容去看到底此时的Serverless在指代什么。主要是因为Serverless这个词包含的信息量太大,而且适用性很广,但总结来说Serverless的含义有这样两种。
|
||||
|
||||
第一种:狭义Serverless(最常见)= Serverless computing架构 = FaaS架构 = Trigger(事件驱动)+ FaaS(函数即服务)+ BaaS(后端即服务,持久化或第三方服务)= FaaS + BaaS
|
||||
|
||||
第二种:广义Serverless = 服务端免运维 = 具备Serverless特性的云服务
|
||||
|
||||
<img src="https://static001.geekbang.org/resource/image/ff/28/ff0f26ec67f6455dfc142c2943da0028.png" alt="" title="Serverless定义">
|
||||
|
||||
我用图片来阐明一下这两种概念。其实你不难看出,广义Serverless包含的东西更多,适用范围更广,但我们经常在工作中提到的Serverless一般都是指狭义的Serverless。其实这是历史原因,2014 年11月份,亚马逊推出真正意义上的第一款Serverless FaaS服务:Lambda。Serverless的概念才进入了大多数人的视野,也因此Serverless曾经一度就等于FaaS。
|
||||
|
||||
我们再聚焦到图上“狭义Serverless”这里。你注意看的话,图中有几个陌生的名词,我先来给你解释下。FaaS(Function as a Service)就是函数即服务,BaaS(Backend as a Service)就是后端即服务。XaaS(X as a Service)就是X即服务,这是云服务商喜欢使用的一种命名方式,比如我们熟悉的SaaS、PaaS、IaaS都是这样。
|
||||
|
||||
先说FaaS,函数即服务,它还有个名字叫作Serverless Computing,它可以让我们随时随地创建、使用、销毁一个函数。
|
||||
|
||||
你可以想一下通常函数的使用过程:它需要先从代码加载到内存,也就是实例化,然后被其它函数调用时执行。在FaaS中也是一样的,函数需要实例化,然后被触发器Trigger或者被其他的函数调用。二者最大的区别就是在Runtime,也就是函数的上下文,函数执行时的语境。
|
||||
|
||||
FaaS的Runtime是预先设置好的,Runtime里面加载的函数和资源都是云服务商提供的,我们可以使用却无法控制。你可以理解为FaaS的Runtime是临时的,函数调用完后,这个临时Runtime和函数一起销毁。
|
||||
|
||||
FaaS的函数调用完后,云服务商会销毁实例,回收资源,所以FaaS推荐无状态的函数。如果你是一位前端工程师的话,可能很好理解,就是函数不可改变Immutable。简单解释一下,就是说一个函数只要参数固定,返回的结果也必须是固定的。
|
||||
|
||||
用我们上面的MVC架构的Web应用举例,View层是客户端展现的内容,通常并不需要函数算力;Control层,就是函数的典型使用场景。MVC架构里面,一个HTTP的数据请求,就会对应一个Control函数,我们完全可以用FaaS函数来代替Control函数。在HTTP的数据请求量大的时候,FaaS函数会自动扩容多实例同时运行;在HTTP的数据请求量小时,又会自动缩容;当没有HTTP数据请求时,还会缩容到0实例,节省开支。
|
||||
|
||||
<img src="https://static001.geekbang.org/resource/image/41/a6/415a8dc0dc026fe2db71e2406e568ba6.png" alt="" title="MVC架构的Web应用">
|
||||
|
||||
此刻或许你会有点疑惑,Runtime不可控,FaaS函数无状态,函数的实例又不停地扩容缩容,那我需要持久化存储一些数据怎么办,MVC里面的Model层怎么解决?
|
||||
|
||||
此时我就要介绍另一位嘉宾,BaaS了。
|
||||
|
||||
BaaS其实是一个集合,是指具备高可用性和弹性,而且免运维的后端服务。说简单点,就是专门支撑FaaS的服务。FaaS就像高铁的车头,如果我们的后端服务还是老旧的绿皮火车车厢,那肯定是要散架的,而BaaS就是专门为FaaS准备的高铁车厢。
|
||||
|
||||
MVC架构中的Model层,就需要我们用BaaS来解决。Model层我们以MySQL为例,后端服务最好是将FaaS操作的数据库的命令,封装成HTTP的OpenAPI,提供给FaaS调用,自己控制这个API的请求频率以及限流降级。这个后端服务本身则可以通过连接池、MySQL集群等方式去优化。各大云服务商自身也在改造自己的后端服务,BaaS这个集合也在日渐壮大。
|
||||
|
||||
<img src="https://static001.geekbang.org/resource/image/f6/66/f6fa8a879b34885d8037265bedbd2366.png" alt="" title="MVC架构的Model层">
|
||||
|
||||
基于Serverless架构,我们完全可以把传统的MVC架构转换为BaaS+View+FaaS的组合,重构或实现。
|
||||
|
||||
这样看下来的话,狭义Serverless的含义也就不难理解了。
|
||||
|
||||
>
|
||||
第一种:狭义Serverless(最常见)= Serverless computing架构 = FaaS架构 = Trigger(事件驱动)+ FaaS(函数即服务)+ BaaS(后端即服务,持久化或第三方服务)= FaaS + BaaS
|
||||
|
||||
|
||||
Serverless毋庸置疑正是因为FaaS架构才流行起来,进入大家认知的。所以我们最常见的Serverless都是指Serverless Computing架构,也就是由Trigger、FaaS和BaaS架构组成的应用。这也是我给出的狭义Serverless的定义。
|
||||
|
||||
**那什么是广义Serverless呢?**
|
||||
|
||||
将狭义的Serverless推升至广义,具备以下特性的,就是Serverless服务。你可以回忆一下小服的工作,要达成NoOps,都应该具备什么条件?
|
||||
|
||||
小服的工作职责:
|
||||
|
||||
1. 无需用户关心服务端的事情(容错、容灾、安全验证、自动扩缩容、日志调试等等)。
|
||||
1. 按使用量(调用次数、时长等)付费,低费用和高性能并行,大多数场景下节省开支。
|
||||
1. 快速迭代&试错能力(多版本控制,灰度,CI&CD等等)。
|
||||
|
||||
广义Serverless,其实就是指服务端免运维,也是未来的主要趋势。
|
||||
|
||||
总结来说的话就是,我们日常谈Serverless的时候,基本都是指狭义的Serverless,但当我们提到某个服务Serverless化的时候,往往都是指广义的Serverless。我们后面的课程中也是如此。
|
||||
|
||||
## 总结
|
||||
|
||||
今天我们一起学习了Serverless的边界、目标以及定义。我还介绍了即将贯穿我们整个专栏的Web应用,即任务列表。
|
||||
|
||||
现在我们可以回过头来看看最初的两个问题了,你是不是已经有答案了呢?
|
||||
|
||||
1. Serverless能解决什么问题?Serverless可以使应用在服务端免运维。
|
||||
1. Serverless为什么难定义?Serverless将服务端运维高度抽象成了一种解决方案,包含的信息量太大了。
|
||||
|
||||
另外,Serverless可分为狭义和广义。狭义Serverless是指用FaaS+BaaS这种Serverless架构开发的应用;广义Serverless则是具备Serverless特性的云服务。现在的云服务商,正在积极地将自己提供的各种云服务Serverless化。
|
||||
|
||||
## 作业
|
||||
|
||||
最后,留给你一个作业:请你注册一个云服务商的云账号,并根据云服务商的文档和教程,自己部署一个Serverless应用。下节课,我将用云上的FaaS部署一个Serverless应用,给你详细讲解Serverless引擎盖内的知识。如果今天这节课让你有所收获,也欢迎你把它分享给更多的朋友。
|
||||
119
极客时间专栏/geek/Serverless入门课/基础篇/02 | 原理:通过一个案例,理解FaaS的运行逻辑.md
Normal file
119
极客时间专栏/geek/Serverless入门课/基础篇/02 | 原理:通过一个案例,理解FaaS的运行逻辑.md
Normal file
@@ -0,0 +1,119 @@
|
||||
<audio id="audio" title="02 | 原理:通过一个案例,理解FaaS的运行逻辑" controls="" preload="none"><source id="mp3" src="https://static001.geekbang.org/resource/audio/3e/6c/3e020dfe7f763cdc1dc84db8f652f86c.mp3"></audio>
|
||||
|
||||
你好,我是秦粤。上一讲我们介绍了什么是Serverless,从概念的角度我们已经对Serverless有了一个深入的了解;那从应用角度来看,Serverless对于开发者究竟有什么魔力呢?这一讲,我准备通过快速部署纯FaaS的Serverless应用,给你讲一讲FaaS应用背后的运行原理。
|
||||
|
||||
为了让你更好地体验Serverless带来的变革,这节课我们以Serverless版本的"Hello World"实操例子进行展示。鉴于我的熟悉程度,我选择了阿里云,当然,你也可以选择你熟悉的云服务商(我在专栏的最后一课还会讲到如何解除云服务商的限制,混合使用多云运营商服务等等)。
|
||||
|
||||
另外,需要注意的是,如果你是跟着我一步步实操练习的,那么开通云服务可能会产生少量费用,遇到充值提示你要自行考虑一下。当然,如果你不着急体验,我觉得看我的视频演示也已经足够了。
|
||||
|
||||
<video poster="https://media001.geekbang.org/f8a8253fd4b3406b9b8ff62190e0b809/snapshots/407a079dddfa43268b3a3dca71908903-00005.jpg" preload="none" controls=""><source src="https://media001.geekbang.org/customerTrans/7264ef1853aeb04326c1b89724de902a/56fb86fe-171920eee1f-0000-0000-01d-dbacd.mp4" type="video/mp4"><source src="https://media001.geekbang.org/f8a8253fd4b3406b9b8ff62190e0b809/70dd5676e19241eb8d35bcdc40780127-6e40078d58d70683dfe179b76ce7db26-ld.m3u8" type="application/x-mpegURL"><source src="https://media001.geekbang.org/f8a8253fd4b3406b9b8ff62190e0b809/70dd5676e19241eb8d35bcdc40780127-6e40078d58d70683dfe179b76ce7db26-ld.m3u8" type="application/x-mpegURL"></video>
|
||||
|
||||
我们从上面的演示也看到了,会用Serverless这个目标我觉得不难实现,但这不是我们这节课的终极目的。今天我就想带着你打开这个FaaS "Hello World"应用的引擎盖,来看看它内部到底是如何运行的。为什么要急着给你讲原理呢?因为如果你不理解原理的话,后面在应用Serverless化的时候就无从下手了。
|
||||
|
||||
## FaaS是怎么运行的?
|
||||
|
||||
现在大家都觉得Serverless是个新东西,是个新风口,刚才在演示的视频里你也能看到,它确实很方便。但你也不用把它想得多复杂,运行应用的那套逻辑还没有变化,Serverless只是用技术手段帮我们屏蔽了复杂性,这点它和其他的云技术没有任何差别。
|
||||
|
||||
你可以想想,在Serverless出现之前,我们要部署这样一个"Hello World"应用得何等繁琐。首先为了运行我们的应用,我们要在服务端构建代码的运行环境:我们要购买虚拟机服务,初始化虚拟机运行环境,安装我们需要的应用运行环境,尽量和本地开发环境保持一致;紧接着为了让用户能够访问我们刚刚启动的应用,我们需要购买域名,用虚拟机IP注册域名;配置Nginx,启动Nginx;最后我们还需要上传应用代码,启动应用。
|
||||
|
||||
你可以闭上眼睛想想是不是我说的这样,当然,为了方便你理解,我还画了张图。前面5步都准备好了,用户在第6步才能成功访问到我们的应用。
|
||||
|
||||
<img src="https://static001.geekbang.org/resource/image/20/63/20d3270c573266f1a01d788d52260863.png" alt="" title="Hello World 应用部署的传统流程">
|
||||
|
||||
与上面传统流程形成鲜明对比的是,我们刚刚的Serverless部署只需要简单的3步,而且目前这样操作下来,没有产生任何费用。上一课我们讲过,**Serverless是对服务端运维体系的极端抽象。**注意,这句话里面有个关键词,“抽象”,我没有用“革新”“颠覆”之类的词语,也就是说,用户HTTP数据请求的全链路,并没有质的改变,Serverless只是将全链路的模型简化了。
|
||||
|
||||
具体来说,之前我们需要在服务端构建代码的运行环境,而FaaS应用将这一步抽象为函数服务;之前我们需要负载均衡和反向代理,而FaaS应用将这一步抽象为HTTP函数触发器;之前我们需要上传代码和启动应用,而FaaS应用将这一步抽象为函数代码。
|
||||
|
||||
<img src="https://static001.geekbang.org/resource/image/08/fd/084b55574ca1588097383571c57c1dfd.png" alt="" title="Hello World 应用的运行架构图">
|
||||
|
||||
触发器、函数服务……咦,是不是发现开始出现了一些陌生名词?不用着急,还是对照着上面这张图,我给你再串下"Hello World"这个纯FaaS应用的数据请求链条。理解了这些链条,你自然就理解了这几个新名词的背景了。
|
||||
|
||||
咱们先从图的右边开始看,图上我标注了次序。当用户第一次访问HTTP函数触发器时,函数触发器就会Hold住用户的HTTP请求,并产生一个HTTP Request事件通知函数服务。
|
||||
|
||||
紧接着函数服务就会检查有没有闲置的函数实例;如果没有函数实例,就去函数代码仓库中拉取你的代码;初始化并启动一个函数实例,执行这个函数,传入这个HTTP Request对象作为函数的参数,执行函数。
|
||||
|
||||
再进一步,函数执行的结果HTTP Response返回函数触发器,函数触发器再将结果返回给等待的用户客户端。
|
||||
|
||||
如果你还记得的话,我们刚刚的视频演示,你可以看到我们的纯FaaS "Hello World"应用例子中,默认创建了3个服务。
|
||||
|
||||
第一个"GreetingServiceGreetingFunctionhttpTrigger"函数触发器,函数触发器是所有请求的统一入口,当请求发生时,它会触发事件通知函数服务,并且等待函数服务执行返回后,将结果返回给等待的请求。
|
||||
|
||||
第二个"GreetingService"函数服务,当函数触发器通知的“事件”到来,它会查看当前有没有闲置的函数实例,如果有则调用函数实例处理;如果没有,则会创建函数实例,等实例创建完毕后,再调用函数实例处理事件。
|
||||
|
||||
第三个"GreetingServiceGreetingFunction"函数代码,“函数服务”在第一次实例化函数时,就会从这个代码仓库中拉取代码,并构建函数实例。
|
||||
|
||||
理解了FaaS应用调用链路,我想你可能会问:“真够复杂,折腾来折腾去,怎么感觉它的这套简化逻辑很像以前新浪的SAE或者Heroku那样的NoOps应用托管PaaS平台?”不知道你是不是有这样的问题,反正我当时第一次接触Serverless时就有类似的疑问。
|
||||
|
||||
其实,FaaS与应用托管PaaS平台对比,**最大的区别在于资源利用率,**这也是FaaS最大的创新点。FaaS的应用实例可以缩容到0,而应用托管PaaS平台则至少要维持1台服务器或容器。
|
||||
|
||||
你注意看的话,在上面"Hello World"例子中,函数在第一次调用之前,实际的服务器占用为0。因为直到用户第一次HTTP数据请求过来时,函数服务才被HTTP事件触发,启动函数实例。也就是说没有用户请求时,函数服务没有任何的函数实例,也就不占用任何的服务器资源。而应用托管PaaS平台,创建应用实例的过程通常需要几十秒,为了保证你的服务可用性,必须一直维持着至少一台服务器运行你的应用实例。
|
||||
|
||||
打个比方的话,FaaS就有点像我们的声控灯,有人的时候它可以很快亮起来,没人的时候又可以关着。对比传统的需要人手动开关的灯,声控灯最大的优势肯定就是省电了。但你想想,能省电的前提是有人的时候,声控灯能够找到比较好的方式快速亮起来。
|
||||
|
||||
FaaS也是这样,它优势背后的关键点是可以极速启动。那它是怎么做的呢?要理解极速启动背后的逻辑,这里我就要引入冷启动的概念了。
|
||||
|
||||
## FaaS为什么可以极速启动?
|
||||
|
||||
冷启动本来是PC上的概念,它是指关闭电源后,PC再启动仍然需要重新加载BIOS表,也就是从硬件驱动开始启动,因此启动速度很慢。
|
||||
|
||||
现在的云服务商,线上物理服务器断电重启几乎是不太可能的。**FaaS中的冷启动是指从调用函数开始到函数实例准备完成的整个过程。**冷启动我们关注的是启动时间,启动时间越短,我们对资源的利用率就越高。现在的云服务商,基于不同的语言特性,冷启动平均耗时基本在100~700毫秒之间。得益于Google的JavaScript引擎Just In Time特性,Node.js在冷启动方面速度是最快的。
|
||||
|
||||
100~700毫秒的冷启动时间,我不知道你听到这个数据的时候是不是震惊了一下。
|
||||
|
||||
下面这张图是FaaS应用冷启动的过程。其中,蓝色部分是云服务商负责的,红色部分由你负责,而函数代码初始化,一人一半。也就是说蓝色部分在冷启动时候的耗时你不用关心,而红色部分就是你的函数耗时。至于资源调度是要做什么,你可以先忽略,我后面会提到。
|
||||
|
||||
例如从刚才演示视频的云服务控制台我们可以看到,"Hello World"的单次函数耗时是0.0125 CU-S,也就是说耗时12.5毫秒,实际我们抓数据包来看,除去建立连接的时间,我们整个HTTPS请求到完全返回结果需要100毫秒。我们负责的红色部分耗时是12.5毫秒,也就是说云服务商负责的蓝色部分耗时是87.5毫秒。
|
||||
|
||||
<img src="https://static001.geekbang.org/resource/image/53/28/53d9831798509d2b8cd66e1714ab8428.png" alt="" title="FaaS应用冷启动过程图">
|
||||
|
||||
注意,FaaS服务从0开始,启动并执行完一个函数,只需要100毫秒。这也是为什么FaaS敢缩容到0的主要原因。通常我们打开一个网页有个关键指标,响应时间在1秒以内,都算优秀。这么一对比,100毫秒的启动时间,对于网页的秒开率影响真的极小。
|
||||
|
||||
而且可以肯定的是,云服务商还会不停地优化自己负责的部分,毕竟启动速度越快对资源的利用率就越高,例如冷启动过程中耗时比较长的是下载函数代码。所以一旦你更新代码,云服务商就会偷偷开始调度资源,下载你的代码构建函数实例的镜像。请求第一次访问时,云服务商就可以利用构建好的缓存镜像,直接跳过冷启动的下载函数代码步骤,从镜像启动容器,这个也叫**预热冷启动**。所以如果我们有些业务场景对响应时间比较敏感,我们就可以通过**预热冷启动或预留实例策略**[1],加速或绕过冷启动时间。
|
||||
|
||||
了解了冷启动的概念,我们再看看为什么FaaS可以极速启动,而应用托管平台PaaS不行?
|
||||
|
||||
首先应用托管平台PaaS为了适应用户的多样性,必须支持多语言兼容,还要提供传统后台服务,例如MySQL、Redis。
|
||||
|
||||
这也意味着,应用托管平台PaaS在初始化环境时,有大量依赖和多语言版本需要兼容,而且兼容多种用户的应用代码往往也会增加应用构建过程的时间。所以通常应用托管平台PaaS无法抽象出轻量的可复用的层级,只能选择服务器或容器方案,从操作系统层开始构建应用实例。
|
||||
|
||||
FaaS设计之初就牺牲了用户的可控性和应用场景,来简化代码模型,并且通过分层结构进一步提升资源的利用率。学到这里,我们得来看看隐藏在FaaS冷启动中最重要的革新技术:分层结构。
|
||||
|
||||
## FaaS是怎么分层的?
|
||||
|
||||
<img src="https://static001.geekbang.org/resource/image/64/1b/64a03d797850a58f8d5f8d117fa0031b.png" alt="" title="FaaS实例执行结构图">
|
||||
|
||||
你的FaaS实例执行时,就如上图所示,至少是3层结构:容器、运行时Runtime、具体函数代码。
|
||||
|
||||
容器你可以理解为操作系统OS。代码要运行,总需要和硬件打交道,容器就是模拟出内核和硬件信息,让你的代码和Runtime可以在里面运行。容器的信息包括内存大小、OS版本、CPU信息、环境变量等等。目前的FaaS实现方案中,容器方案可能是Docker容器、VM虚拟机,甚至Sandbox沙盒环境。
|
||||
|
||||
运行时Runtime [2],就是你的函数执行时的上下文context。Runtime的信息包括代码运行的语言和版本,例如Node.js v10,Python3.6;可调用对象,例如aliyun SDK;系统信息,例如环境变量等等。
|
||||
|
||||
关于FaaS的3层结构,你可以这么想象:容器层就像是Windows操作系统;Runtime就像是Windows里面的播放器暴风影音;你的代码就像是放在U盘里的电影。
|
||||
|
||||
这样分层有什么好处呢?容器层适用性更广,云服务商可以预热大量的容器实例,将物理服务器的计算资源碎片化。Runtime的实例适用性较低,可以少量预热;容器和Runtime固定后,下载你的代码就可以执行了。通过分层,我们可以做到资源统筹优化,这样就能让你的代码快速低成本地被执行。
|
||||
|
||||
理解了分层,我们再回想一下FaaS分层对应冷启动的过程,其实你就不难理解云服务商负责的就是容器和Runtime的准备阶段了。而开发者自己负责的则是函数执行阶段。一旦容器&Runtime启动后,就会维持一段时间,这段时间内的这个函数实例就可以直接处理用户数据请求。当一段时间内没有用户请求事件发生(各个云服务商维持实例的时间和策略不同),则会销毁这个函数实例。
|
||||
|
||||
具体你可以看下下面这张图,以辅助你理解。
|
||||
|
||||
<img src="https://static001.geekbang.org/resource/image/a8/69/a82eef4cb307dfe42040ffb7d4852a69.png" alt="" title="FaaS分层对应冷启动示意图">
|
||||
|
||||
## 总结
|
||||
|
||||
这一讲,我带你体验了只需要三步就能快速部署纯FaaS的Web应用上线,我们也打开了FaaS引擎盖,介绍了FaaS的内部运行机制。现在我们就来总结一下这节课的关键点。
|
||||
|
||||
1. 纯FaaS应用调用链路由函数触发器、函数服务和函数代码三部分组成,它们分别替代了传统服务端运维的负载均衡&反向代理,服务器&应用运行环境,应用代码部署。
|
||||
1. 对比传统应用托管PaaS平台,FaaS应用最大的不同就是,FaaS应用可以缩容到0,在事件到来时极速启动,Node.js的函数甚至可以做到100ms启动并执行。
|
||||
1. FaaS在设计上牺牲了用户的可控性和应用场景,来简化代码模型,并且通过分层结构进一步提升资源的利用率,这也是为什么FaaS冷启动时间能这么短的主要原因。关于FaaS的3层结构,你可以这么想象:容器层就像是Windows操作系统;Runtime就像是Windows里面的播放器暴风影音;你的代码就像是放在U盘里的电影。
|
||||
|
||||
## 作业
|
||||
|
||||
最后,给你留个作业吧。我知道整个原理你听起来肯定还不是那么好理解,实践是检验真理的唯一标准,如果你有时间并且方便的话,可以试着自己动手Run一个FaaS的Hello World例子,然后思考其中的原理。
|
||||
|
||||
当然,如果今天这节课让你有所收获,也欢迎你把它分享给更多的朋友。
|
||||
|
||||
## 参考资料
|
||||
|
||||
[1] 预留实例介绍,[https://help.aliyun.com/document_detail/138103.html](https://help.aliyun.com/document_detail/138103.html?spm=a2c4g.11186623.6.621.3f085c22jYnnb6)
|
||||
|
||||
[2] Node.js Runtime介绍,[https://help.aliyun.com/document_detail/58011.html](https://help.aliyun.com/document_detail/58011.html?spm=5176.11065259.1996646101.searchclickresult.3d147730b7VloO)
|
||||
125
极客时间专栏/geek/Serverless入门课/基础篇/03 | 原理:FaaS的两种进程模型及应用场景.md
Normal file
125
极客时间专栏/geek/Serverless入门课/基础篇/03 | 原理:FaaS的两种进程模型及应用场景.md
Normal file
@@ -0,0 +1,125 @@
|
||||
<audio id="audio" title="03 | 原理:FaaS的两种进程模型及应用场景" controls="" preload="none"><source id="mp3" src="https://static001.geekbang.org/resource/audio/a9/2e/a91024a738381ce20c413897d499f62e.mp3"></audio>
|
||||
|
||||
你好,我是秦粤。上一讲我们通过一个Node.js纯FaaS的Serverless应用,给你介绍了Serverless引擎盖下的运作机制,总结来说,FaaS依赖分层调度和极速冷启动的特性,在无事件时它居然可以缩容到0,就像我们的声控灯一样,有人的时候它可以亮起来,没人的时候,又可以自动关了。
|
||||
|
||||
听完了原理,我估计你肯定会问,FaaS这么好,但是它的应用场景是什么呢?今天我们就来一起看下。不过,想要理解FaaS的应用场景,我们就需要先理解FaaS的进程模型,这也是除了冷启动之后的另外一个重要概念。
|
||||
|
||||
## FaaS进程模型
|
||||
|
||||
咱先回想一下上节课的FaaS的冷启动过程,我们知道容器和Runtime准备阶段都是由云服务商负责的,我们只需要关注具体的函数执行就可以了。而函数执行在FaaS里是由“函数服务”负责的,当函数触发器通知“事件”到来时,函数服务就会根据情况创建函数实例,然后执行函数。当函数执行完之后,函数实例也随之结束自己的使命,FaaS应用缩容到0,然后开始进入节能模式。
|
||||
|
||||
上面这套逻辑是我们上节课讲的,课后有同学就问,函数执行完之后实例能否不结束,让它继续等待下一次函数被调用呢?这样省去了每次都要冷启动的时间,响应时间不就可以更快了吗?
|
||||
|
||||
是的,本身FaaS也考虑到了这种情况,所以从运行函数实例的进程角度来看,就有两种模型。我也画了张图,方便你理解。
|
||||
|
||||
- 用完即毁型:函数实例准备好后,执行完函数就直接结束。这是FaaS最纯正的用法。
|
||||
- 常驻进程型:函数实例准备好后,执行完函数不结束,而是返回继续等待下一次函数被调用。**这里需要注意,即使FaaS是常驻进程型,如果一段时间没有事件触发,函数实例还是会被云服务商销毁。**
|
||||
|
||||
<img src="https://static001.geekbang.org/resource/image/84/20/84a81773202e2599474f9c9272a65d20.png" alt="" title="模型示意图">
|
||||
|
||||
这两个模型其实也对应两种不同的应用场景。我举个例子,比如你要把我们第一讲中的“待办任务”应用部署上线,还记得小程同学吧,他完成了第一个版本,他用Express.js[1] 框架开发的MVC架构,View层他采用流行的React[2],并且使用了Ant Design Pro[3] React组件库,Model数据库采用MongoDB。小程的第一个版本,就是一个典型的传统Web服务。
|
||||
|
||||
从可控性和改造成本角度来看Web服务,服务端部署方案最适合的还是托管平台PaaS或者自己搭服务跑在IaaS上。正如我上一讲所说,使用FaaS就必须在FaaS的条件限制内使用,最佳的做法应该是一开始就选用FaaS开发。
|
||||
|
||||
但是小程的运气比较好,我们查了一下文档,发现FaaS的Node.js的Runtime是支持Express的,所以我们只需少量修改,**小程的第一个版本就可以使用FaaS的常驻进程方案部署。**
|
||||
|
||||
这里我要做个对比。在之前,假设没有FaaS,我们要将应用部署到托管平台PaaS上;启动Web服务时,主进程初始化连接MongoDB,初始化完成后,持续监听服务器的80端口,直到监听端口的句柄关闭或主进程接收到终止信号;当80端口和客户端建立完TCP链接,有HTTP请求过来,服务器就会将请求转发给Web服务的主进程,这时主进程会创建一个子进程来处理这个请求。
|
||||
|
||||
而在FaaS常驻进程型模式下,首先我们要改造一下代码,Node.js的Server对象采用FaaS Runtime提供的Server对象;然后我们把监听端口改为监听HTTP事件;启动Web服务时,主进程初始化连接MongoDB,初始化完成后,持续监听HTTP事件,直到被云服务商控制的父进程关闭回收。
|
||||
|
||||
当HTTP事件发生时,我们的Web服务主进程跟之前一样,创建一个子进程来处理这个请求事件。主进程就如我们上图中绘制的那个蓝色的圆点,当HTTP事件发生时,它创建的子进程就是蓝色弧形箭头,当子进程处理完后就会被主进程回收。
|
||||
|
||||
在我看来,常驻进程型就是为了传统MVC架构部署上FaaS专门设计的。数据库也可以使用原来的DB连接方式,不过这样做会增加冷启动的时间(我特意在图中用曲线代表时间增加),从而导致第一次请求长延迟甚至失败。比较适合的做法是我们[[第 1 课]](https://time.geekbang.org/column/article/224559) 中,讲Serverless架构时说的,数据持久化采用BaaS服务。
|
||||
|
||||
那么我们能否用用完即毁型来部署小程的这个MVC架构的Web服务呢?可以,但是我不推荐你这样做,因为用完即毁型对传统MVC改造的成本太大。
|
||||
|
||||
<img src="https://static001.geekbang.org/resource/image/2f/33/2f1c4643057d8fbcbec6f7514dd9cd33.png" alt="" title="模型示意图">
|
||||
|
||||
说到这里,我们再将上面对比两个模型的示意图镜头再拉远一点,加上HTTP触发器看看。其实从另外一个角度看,触发器就是一个常驻进程型模型一直在等待,只不过这个触发器是由云服务商处理罢了。
|
||||
|
||||
这里我再啰嗦强调下,还是我们上一讲说的,FaaS只是做了极端抽象,云服务商通过技术手段帮助开发者屏蔽了细节,让他们尽量只关注代码本身。
|
||||
|
||||
所以,在用完即毁型中,我们只要将MVC的Control层部署到函数执行就可以了。这也意味着我们要将我们的MVC架构的Control函数一个个拆解出来部署,一个HTTP请求对应一个Control函数;Control函数实例启动时连接MongoDB,一个请求处理完后直接结束。你如果要提升Control函数的冷启动时间,Model层同样要考虑BaaS化改造。这里你听着可能有点陌生,没关系,后面我会通过代码给你演示,你到时候再理解也不迟。
|
||||
|
||||
现在,理解了两种类型,我们再来看看FaaS是怎么收费的,以及常驻型进程这种模式是不是官方会多收费。云服务商FaaS函数服务的收费标准各不相同,但他们都会提供一定的免费额度。我给你归纳下FaaS的收费标准,主要有两个维度:调用函数次数和函数耗时。
|
||||
|
||||
- 调用函数次数,函数每次被事件触发,计数器加一。例如我们Hello World例子的index.js文件的handler函数,它每调用一次,计数就加一。这种模式因为不占资源,所以资源利用率高、收费低。
|
||||
- 函数耗时,说的是函数执行的运行时长,它的计算单位是CU-S,也就是CPU运行了多少秒。
|
||||
|
||||
例如我们上面“待办任务”改造的常驻进程型和用完即毁型,多数情况下其实他们两个的函数耗时是一样的。这里可能有些绕,需要给你解释一下。
|
||||
|
||||
常驻进程型改造后主要占用的是内存,而FaaS收费的是CPU计算时间,也就是说常驻进程的模式并不会持续收费。但常驻型应用的冷启动时间会增加,所以我们要尽量避免冷启动,避免冷启动通常又需要做一些额外的工作,比如定时触发一下实例或者购买预留实例,这地方就会增加额外的费用了。这样听起来,是不是觉得常驻进程型改造MVC应用用起来很别扭?是的,我们前面也说了,常驻进程模式就是为了传统MVC架构部署上FaaS专门设计的,算是一种权宜之计吧。
|
||||
|
||||
用完即毁型改造后,同样冷启动时间会增加,但是冷启动时间是云服务商负责的。我们Control函数的执行时间,和MVC部署在FaaS中Control的执行时间是一样的。每个请求都增加了冷启动时间,响应时间会更长一些,但我们不用考虑额外的成本。那学到这儿,相信你也可以感觉到了,用完即毁型也不太适合传统MVC架构的改造,也是一种权宜之计,但这是FaaS最纯正的用法,肯定还是有它的用武之地的。
|
||||
|
||||
接下来,我们就继续把焦点放到用完即毁型上,来具体看看它可以用在哪些更加自然的场景里。
|
||||
|
||||
## 数据编排
|
||||
|
||||
我们做开发的多多少少都知道,目前最成功最广泛的设计模式就是MVC模式。但随着前端MVVM框架越来越火,前端View层逐渐前置,发展成SPA单页应用;后端Control和Model层逐渐下沉,发展成面向服务编程的后端应用。
|
||||
|
||||
这种情况下,前后端更加彻底地解耦了,前端开发可以依赖Mock数据接口完全脱离后端限制,而后端的同学则可以面向数据接口开发,但这也产生了高网络I/O的数据网关层。
|
||||
|
||||
Node.js的异步非阻塞和JavaScript天然亲近前端工程师的特性,自然地接过数据网关层。因此也诞生了Node.js的BFF层(Backend For Frontend),将后端数据和后端接口编排,适配成前端需要的数据结构,提供给前端使用。
|
||||
|
||||
我们的程序员好朋友小程也跟进了这个潮流,将“待办任务”Web服务重构成了第二个版本。他将原先的应用拆解成了2个项目:前端项目采用React+AntDesignPro+Umi.js[4] 的单页应用,后端项目还是采用Express。我们本专栏的示例也采用这个技术架构一步一步教你在云上部署SPA+FaaS混合框架演进。
|
||||
|
||||
<img src="https://static001.geekbang.org/resource/image/dd/09/dd608f746a18d6172b7057f083ad2c09.png" alt="" title="BFF示意图">
|
||||
|
||||
如上图所示,BFF层充当了中间胶水层的角色,粘合前后端。未经加工的数据,我们称为元数据Raw Data,对于普通用户来说元数据几乎不可读。所以我们需要将有用的数据组合起来,并且加工数据,让数据具备价值。对于数据的组合和加工,我们称之为**数据编排**。
|
||||
|
||||
BFF层通常是由善于处理高网络I/O的Node.js应用负责。传统的服务端运维Node.js应用还是比较重的,需要我们购买虚拟机,或者使用应用托管PaaS平台。
|
||||
|
||||
因为BFF层只是做无状态的数据编排,所以我们完全可以用FaaS用完即毁型模型替换掉BFF层的Node.js应用,也就是最近圈子里老说的那个新名词SFF(Serverless For Frontend)。
|
||||
|
||||
好,到这儿,我们已经理解了BFF到SFF的演进过程,现在我们再串下新的请求链路逻辑。前端的一个数据请求过来,函数触发器触发我们的函数服务;我们的函数启动后,调用后端提供的元数据接口,并将返回的元数据加工成前端需要的数据格式;我们的FaaS函数完全就可以休息了。具体如下图所示。
|
||||
|
||||
<img src="https://static001.geekbang.org/resource/image/43/4b/43d5ae274d0169bbc0cc4aece791054b.png" alt="" title="SFF示意图">
|
||||
|
||||
另外,除了我们自己的后端应用数据接口,互联网上还有大量的数据供我们使用。比如疫情期间,你要爬取下各个地区的疫情数据、天气数据,这些工作,也都可以放到FaaS上轻松搞定,并且基本还能免费,因为目前各大云服务商都提供了免费的额度,这个我刚给你讲过了。
|
||||
|
||||
编排后端接口,编排互联网上的数据,这俩场景我想你也很容易想到。不过,我觉得,编排云服务商的各种服务才能让你真正体会到那种触电的感觉。我第一次体验之后,就对我同事说:“变天了,真的变天了,喊了这么多年的云计算时代真的来了。”
|
||||
|
||||
## 服务编排
|
||||
|
||||
**服务编排和数据编排很像,主要区别是对云服务商提供的各种服务进行组合和加工。**在FaaS出现之前,就有服务编排的概念,但服务编排受限于服务支持的SDK语言版本,常见的情况是我们用yaml文件或命令行来编排服务。我们要使用这些服务或API,都要通过自己熟悉的编程语言去找对应的SDK,在自己的代码中加载SDK,使用秘钥调用SDK方法进行编排。就和数据编排一样,服务端运维部署成本非常高,而且如果没有SDK,则需要自己根据平台提供的接口或协议实现SDK。
|
||||
|
||||
现在有了FaaS,FaaS拓展了我们可以使用SDK边界,这是什么意思呢?比如小程的“待办任务”Web服务需要发送验证码邮件,我们可以用一个用完即毁型FaaS函数,调用云服务商的SDK发送邮件;再用一个常驻进程型FaaS函数生成随机字符串验证码,生成后记录这个验证码,并且调用发送邮件的FaaS将验证码发给用户邮箱;用户验证时,我们再调用常驻进程型FaaS的方法校验验证码是否正确。
|
||||
|
||||
我还是用阿里云来举例,我们查阅阿里云的邮件服务文档,发现它只支持Java、PHP和Python的SDK。我们一直都是在讲Node.js,这里没有Node.js的SDK,怎么办?如果我们根据阿里云邮箱服务的文档,自己开发Node.js的SDK,那肯定是饶了弯路,费了没用的力气。
|
||||
|
||||
因为我们发送邮件的用完即毁型FaaS函数功能很单一,所以我们完全可以参考邮件服务的PHP文档,就用PHP的SDK创建一个FaaS服务来发送邮件的。你会发现使用PHP邮件服务的成本居然如此之低。
|
||||
|
||||
<video poster="https://media001.geekbang.org/af58590691bc48c9b99ded23575e854c/snapshots/94bc417d5b8a472080439dd788527ede-00005.jpg" preload="none" controls=""><source src="https://media001.geekbang.org/customerTrans/7264ef1853aeb04326c1b89724de902a/4926d6b2-1719cc01371-0000-0000-01d-dbacd.mp4" type="video/mp4"></video>
|
||||
|
||||
你会看到在这个例子中,我用了我并不太熟悉PHP语言编排了邮件发送服务。不知道你意识到没有,这个也是FaaS一个亮点:语言无关性。它意味着你的团队不再局限于单一的开发语言了,你们可以利用Java、PHP、Python、Node.js各自的语言优势,混合开发出复杂的应用。
|
||||
|
||||
FaaS服务编排被云服务商特别关注正是因为它具备的这种开放性。使用FaaS可以创造出各种各样复杂的服务编排场景,而且还与语言无关,这大大增加了云服务商各种服务的使用场景。当然,这对开发者也提出了要求,它要求开发者去更多地了解云服务商提供的各种服务。
|
||||
|
||||
甚至我还知道,西雅图就有创业团队利用FaaS服务编排能力做了一套开源框架:Pulumi[5],并且还拿到了融资。感兴趣的话,你可以去他们的官网看看。
|
||||
|
||||
## 总结
|
||||
|
||||
好,到这里,我们这节课的内容就讲完了。我再来总结一下这节课的关键点。
|
||||
|
||||
1. FaaS的进程模型有两种:常驻进程型和用完即毁型。常驻进程型是为了适应传统MVC架构设计的,它看起来并不自然;如果你从现在开始玩FaaS的话,我当然首选推荐用完即毁型,它可以最大限度发挥FaaS的优势。
|
||||
1. 追溯历史,我给你梳理了前后端分离发展出的BFF,然后BFF又可以被SFF替代。不管是内部的接口编排,还是外部一些数据的编排,FaaS都可以发挥出极大优势,你看看我视频演示的例子就懂了。
|
||||
1. 从数据编排再进一步,我们可以利用FaaS和云服务商云服务的能力,做到服务编排,编排出更加强大的组合服务场景,提升我们的研发效能。并且通过我这么长时间的体验,我还想感叹说,依赖云服务商的各种能力,再通过FaaS编排开发一个项目时,往往可以做到事半功倍。
|
||||
|
||||
## 作业
|
||||
|
||||
今天的作业和上一讲类似,我视频中给你做了个简单的Demo,你可以随便找个云平台去run一下试试,百闻不如一见,体验完之后,你可以在留言区谈谈你的感想。另外,如果今天这节课让你有所收获,也欢迎你把它分享给更多的朋友。
|
||||
|
||||
我Demo中的代码地址:[https://github.com/pusongyang/sls-send-email](https://github.com/pusongyang/sls-send-email)
|
||||
|
||||
## 参考资料
|
||||
|
||||
[1] Express是Node.js著名的Web服务框架<[https://expressjs.com/](https://expressjs.com/)>。
|
||||
|
||||
[2] React 是Facebook开源的MVVM框架<[https://zh-hans.reactjs.org/](https://zh-hans.reactjs.org/)>。
|
||||
|
||||
[3] AntDesignPro是蚂蚁开源的React组件库<[https://pro.ant.design/](https://pro.ant.design/)>。
|
||||
|
||||
[4] Umi.js是蚂蚁开源的React企业级解决方案脚手架<[https://umijs.org/](https://umijs.org/)>。
|
||||
|
||||
[5] Pulumi <[https://pulumi.io/](https://pulumi.io/)>
|
||||
117
极客时间专栏/geek/Serverless入门课/基础篇/04 | 原理:FaaS应用如何才能快速扩缩容?.md
Normal file
117
极客时间专栏/geek/Serverless入门课/基础篇/04 | 原理:FaaS应用如何才能快速扩缩容?.md
Normal file
@@ -0,0 +1,117 @@
|
||||
<audio id="audio" title="04 | 原理:FaaS应用如何才能快速扩缩容?" controls="" preload="none"><source id="mp3" src="https://static001.geekbang.org/resource/audio/da/6c/da76ed035c1ff81951b72652dbce4a6c.mp3"></audio>
|
||||
|
||||
你好,我是秦粤。上一讲我们介绍了FaaS的两种进程模型:用完即毁型和常驻进程型,这两种进程模型最大的区别就是在函数执行阶段,函数执行完之后函数实例是否直接结束。同时,我还给你演示了用完即毁型的应用场景,数据编排和服务编排。
|
||||
|
||||
这里我估计你可能会有点疑虑,这两个场景用常驻进程型,应该也可以实现吧?当然可以,但你还记得不,我多次强调用完即毁型是FaaS最纯正的用法。那既然介绍了两种进程模型,为什么我要说用完即毁型FaaS模型比常驻进程型纯正?它背后的逻辑是什么?你可以停下来自己想想。
|
||||
|
||||
要真正理解这个问题,我们需要引入进来复杂互联网应用架构演进的一个重要知识点:扩缩容,这也是我们这节课的重点。
|
||||
|
||||
为了授课需要,我还是会搬出我们之前提到的创业项目“待办任务”Web网站。这一次,需要你动动手,在自己本地的机器上运行下这个项目。项目的代码我已经写好了,放到GitHub上了,你需要把它下载到本地,然后阅读README.md安装和启动我们的应用。
|
||||
|
||||
GitHub地址:[https://github.com/pusongyang/todolist-backend](https://github.com/pusongyang/todolist-backend)
|
||||
|
||||
我给你简单介绍下我们目前这个项目的功能。这是一个后端项目,前端代码不是我们的重点,当然如果你有兴趣,我的REAME.md里面也有前端代码地址,你可以在待办任务列表里面创建、删除、完成任务。
|
||||
|
||||
技术实现上,待办任务数据就存储在了数组里。宏观上看,它是个典型的Node.js传统MVC应用,Control函数就是app.get和app.post;Model我们放在内存里,就是Todos对象;View是纯静态的单页应用代码,在public目录。
|
||||
|
||||
你先想一下,假如我们让200个用户**同时并发访问**你本地开发环境的“待办任务”Web网站首页index.html,你本地的Web网站实例,会出现什么样的场景?如果方便的话,你可以用Apache[1] 提供的ab工具,压测一下我们的项目。
|
||||
|
||||
```
|
||||
# 模拟1000个请求,由200个用户并发访问我们启动的本地3001端口
|
||||
ab -n 1000 -c 200 http://localhost:3001/
|
||||
|
||||
```
|
||||
|
||||
我来试着描述下你PC此时的状态,首先客户端与PC建立了200个TCP/IP的连接,这时PC还可以勉强承受得住。然后200个客户端同时发起HTTP请求"/ GET",我们Web服务的主进程,会创建“CPU核数-1”个子进程并发,来处理这些请求。注意,这里CPU核数之所以要减一,是因为有一个要留给主进程。
|
||||
|
||||
例如4核CPU就会创建3条子进程,并发处理3个客户端请求,剩下的客户端请求排队等待;子进程开始处理"/ GET",命中路由规则,进入对应的Control函数,返回index.html给客户端;子进程发送完index.html文件后,被主进程回收,主进程又创建一个新的子进程去处理下一个客户端请求,直到所有的客户端请求都处理完。具体如下图所示。
|
||||
|
||||
<img src="https://static001.geekbang.org/resource/image/bb/b1/bb998d5ac7b84cf0468932afad2448b1.png" alt="" title="PC状态示意图">
|
||||
|
||||
理解了这一点,接下来的问题就很简单了。如果我问你,为了提升我们客户端队列的处理速度,我们应该怎么做?我想答案你应该已经脱口而出了。
|
||||
|
||||
## 纵向扩缩容与横向扩缩容
|
||||
|
||||
是的,我们很容易想到最直接的方式就是增加CPU的核数。要增加CPU的核数,我们可以通过升级单台机器配置,例如从4核变成8核,那并发的子进程就有7个了。
|
||||
|
||||
除了直接增加CPU的核数,我们还可以增加机器数(还是增加一个4核的),我们用2台机器,让500个客户端访问一台,剩下500个客户端访问另外一台,这样我们并发的子进程也能增加到6个。
|
||||
|
||||
<img src="https://static001.geekbang.org/resource/image/65/51/652b85301f140dc4d3b2b0e35fafa151.png" alt="" title="纵向扩缩容 & 横向扩缩容">
|
||||
|
||||
我画了张图,你可以看看。增加或减少单机性能就是纵向扩缩容,纵向扩缩容随着性能提升成本曲线会陡增,通常我们采用时要慎重考虑。而增加或减少机器数量就是横向扩缩容,横向扩缩容成本更加可控,也是我们最常用的默认扩缩容方式。这里我估计很多人知道,为了照顾初学者,所以再啰嗦下。
|
||||
|
||||
你理解了这一点,我们就要增加难度了。因为index.html只是单个文件,如果是数据呢?无论是纵向还是横向扩缩容,我们都需要重启机器。现在待办列表的数据保存在内存中,它每次重启都会被还原到最开始的时候,那我们要如何在扩缩容的时候保存我们的数据呢?
|
||||
|
||||
在讲解这个问题前,我们还是需要简化一下模型。我们先从宏观架构角度去看“待办任务”Web服务端数据流的网络拓扑图,数据请求从左往右,经过我们架构设计的各个节点后,最终获取到它要的数据,然后聚合数据并返回。那在这个过程中,哪些节点可以扩缩容,哪些节点不容易扩缩容呢?
|
||||
|
||||
<img src="https://static001.geekbang.org/resource/image/d3/54/d39538136a0fafcc3fa4c7e0d5c8f554.png" alt="" title="“待办任务”Web服务端数据流网络拓扑图">
|
||||
|
||||
## Stateful VS Stateless
|
||||
|
||||
网络拓扑中的节点,我们可以根据是否保存状态分为Stateful和Stateless。Stateful就是有状态的节点,Stateful节点用来保存状态,也就是存储数据,因此Stateful节点我们需要额外关注,需要保障稳定性,不能轻易改动。例如通常数据库都会采用主-从结构,当主节点出问题时,我们立即切换到从节点,让Stateful节点整体继续提供服务。
|
||||
|
||||
Stateless就是无状态的节点,Stateless不存储任何状态,或者只能短暂存储不可靠的部分数据。Stateless节点没有任何状态,因此在并发量高的时候,我们可以对Stateless节点横向扩容,而没有流量时我们可以缩容到0(是不是有些熟悉了?)。Stateful节点则不行,如果面对流量峰值峰谷的流量差比较大时,我们要按峰值去设计Stateful节点来抗住高流量,没有流量时我们也要维持开销。
|
||||
|
||||
<img src="https://static001.geekbang.org/resource/image/e2/40/e2a28f3a4dbd473fe2db11c7116b9140.png" alt="" title="“待办任务”Web服务端数据流网络拓扑图">
|
||||
|
||||
在我们“待办任务”的项目中,数据库就是典型Stateful节点,因为它要持久化保存用户的待办任务。另外负载均衡也是Stateful节点,就跟我们思维试验中保存客户端队列的主进程一样,它要保存客户端的链接,才能将我们Web应用的处理结果返回给客户端。
|
||||
|
||||
回到我们的进程模型,**用完即毁型是天然的Stateless**,因为它执行完就销毁,你无法单纯用它持久化存储任何值;**常驻进程型则是天然的Stateful**,因为它的主进程不退出,主进程可以存储部分值。
|
||||
|
||||
<img src="https://static001.geekbang.org/resource/image/c5/51/c5ee951df1c8c5f9e88d5d44ffdb2551.png" alt="" title="进程模型实例">
|
||||
|
||||
如上图所示,我们将待办任务列表的数据存储在了主进程的内存中,而在FaaS中,即使我们在常驻进程型的主进程中保存了值,它也可能会被云服务商回收。即便我们购买了预留实例,但扩容出来的节点与节点之间,它们各自内存中的数据是无法共享的,这个我们上节课有讲过。
|
||||
|
||||
所以我们要让常驻进程型也变成Stateless,我们就要避免在主进程中保存值,或者只保存临时变量,而将持久化保存的值,移出去交给Stateful的节点,例如数据库。
|
||||
|
||||
<img src="https://static001.geekbang.org/resource/image/f6/27/f637868794ce276e85fb209b845da527.png" alt="" title="进程模型实例">
|
||||
|
||||
我们将主进程节点中的数据独立出来,主进程不保存数据,这时我们的应用就变成Stateless。数据我们放入独立出来的数据库Stateful节点,网络拓扑图就是上面这张图。这个例子也就变成了我们上节课讲常驻进程型FaaS的例子,我们在主进程启动时连接数据库,通过子进程访问数据库数据,但这样做的弊端其实也很明显,它会直接增加冷启动时间。那有没有更好的解决方案呢?
|
||||
|
||||
换一种数据持久化的思路,我们为什么非要自己连接数据库呢?我们对数据的增删改查,无非就是子进程复用主进程建立好的TCP链接,发送数据库语句,获取数据。咱们大胆想象下,如果向数据库发送指令,变成HTTP访问数据接口POST、DELETE、PUT、GET,那是不是就可以利用上一课的数据编排和服务编排了?
|
||||
|
||||
是的,铺垫了这么多,就是为了引出我们今天的主角:BaaS化。数据接口的POST、DELETE、PUT、GET其实就是语义化的RESTful API[2] 的HTTP方法。用MySQL举例,那POST对应CREATE指令,DELETE对应DELETE指令,PUT对应UPDATE指令,GET对应SELECT指令,语义上是一一对应的,因此我们可以天然地将MySQL的操作转为RESTful API操作。
|
||||
|
||||
为了防止有同学误解,我觉得我还是需要补充一下。传统数据库方式,因为TCP链路复用和通信字段冗余低,同样的操作会比HTTP快。FaaS可以直连数据库,但传统数据通过IP形式连接往往很难生效,因为云上环境都是用VPC切割的。所以FaaS直连数据库,我们通常要采用云服务商提供的数据库BaaS服务,但目前很多BaaS服务还不成熟。
|
||||
|
||||
再进一步考虑,既然FaaS不适合用作Stateful的节点,那我们是不是可以将Stateful的操作全部变成数据接口,外移?这样我们的FaaS就可以用我们上一课讲的数据编排,自由扩缩容了。
|
||||
|
||||
## 后端应用BaaS化
|
||||
|
||||
BaaS这个词我们前面已经讲过了,在我看来,BaaS化的核心思想就是将后端应用转换成**NoOps的数据接口**,这样FaaS在SFF层就可以放开手脚,而不用再考虑冷启动时间了。其实我们上一课在讲SFF的时候,后端应用就是一定程度的BaaS化。后端应用接口化只是BaaS化的一小部分,BaaS化最重要的部分是后端数据接口应用的开发人员也可以不再关心服务端运维的事情。
|
||||
|
||||
<img src="https://static001.geekbang.org/resource/image/f4/c2/f47b84ac700130339e3422908a9931c2.png" alt="" title="SFF示意图">
|
||||
|
||||
回顾一下,[[第 1 课]](https://time.geekbang.org/column/article/224559) 中我们说的**Serverless应用 = FaaS+BaaS**,相信此刻你一定会有不一样的感悟了吧。
|
||||
|
||||
BaaS化的概念容易理解,但实际上要实践,将我们的网站后端改造BaaS化,就比较困难,这其中主要的难点在于后端的运维体系如何Serverless化,改造后端BaaS化的内容相比FaaS的SFF要复杂得多。在本专栏后续的课程中,我将通过我们的创业项目“待办任务”Web服务逐步演进,带你一起学习后端BaaS化,不过你也不必有压力,因为我们在学习FaaS的过程中已经掌握的知识点,也是适用于后端BaaS化的。
|
||||
|
||||
另外值得一提的是,云服务商也在大力发展BaaS,例如AWS提供的DynamoDB服务或Aurora服务。数据库就是BaaS化的,我们无需关心服务端运维,也无需关心IP,我们只要通过域名和密钥访问我们的DB,就像使用数据编排一样。而且BaaS的阵营还在不停壮大,不要忘了我们手中还有服务编排这一利器。
|
||||
|
||||
## 总结
|
||||
|
||||
用完即毁型之所以比常驻进程型更加纯正,就是因为常驻进程型往往容易误导我们,让我们以为它像PaaS一样受控,可以用作Stateful节点,永久存储数据。实际上,在FaaS中即使我们采用常驻进程型,我们的函数实例还是会被云服务商回收。
|
||||
|
||||
就像我们的“待办任务”Web网站的例子,将数据Todos放在内存中,我们每次重启都会重置一样。我们用数据编排的思路,将后端对数据库的操作转为数据接口,那我们就可以将FaaS中的数据存储移出到后端应用上,采用上一节课讲的数据编排跟我们的后端进行交互。但后端应用我们不光要做成数据接口,还要BaaS化,让后端工程师在开发过程中,也能不用关心服务端运维。
|
||||
|
||||
现在我们再来回顾一下这节课的知识点:
|
||||
|
||||
1. 扩缩容我们可以选择纵向扩缩容和横向扩缩容,纵向扩缩容就是提升单机性能,价格上升曲线陡峭,我们通常要慎重选择;横向扩缩容就是提升机器数量,价格上升平稳,也是我们常用的默认扩缩容方式。
|
||||
1. 在网络拓扑图中,Stateful是存数据的节点;Stateless是处理数据的节点,不负责保存数据。只有Stateless节点才能任意扩缩容,Stateful节点因为是保存我们的重要数据,所以我们要谨慎对待。如果我们的网络拓扑节点想自由扩缩容,则需要将这个节点的数据操作外移到专门的Stateful节点。
|
||||
1. 我们的FaaS访问Stateful节点,那我们就希望Stateful节点对FaaS提供数据接口,而不是单纯的数据库指令,因为数据库连接会增加FaaS的额外开支。另外为了方便后端工程师开发,我们需要将Stateful节点BaaS化,BaaS化的内容,我们将在后续的课程中展开。
|
||||
|
||||
## 作业
|
||||
|
||||
本节课我们创业项目“待办任务”中的数据处理并没有按照RESTFul API的HTTP语义化来开发,不太规范。作业中的GitHub仓库,这个版本我已经将请求方式转为语义化的RESTFul API了,你可以对比一下master分支中的代码,看看语义化带来的好处。另外我引入一个本地数据库lowdb[3],在你第一次启动后,创建本地数据库文件db.json,我们的增删改查不会因为重启项目而丢失了,但是在FaaS上我们却无法使用db.json文件,原因是FaaS的实例文件系统是只读的。因此FaaS版本,我们用了内存来替换文件系统。
|
||||
|
||||
作业初始化项目地址:[https://github.com/pusongyang/todolist-backend/tree/lesson04-homework](https://github.com/pusongyang/todolist-backend/tree/lesson04-homework)
|
||||
|
||||
给你的作业是,你要将这个项目部署到云上函数服务,注意FaaS的版本是index-faas.js。如果你条件允许的话,最好用自己的域名关联。我们[[第 1 课]](https://time.geekbang.org/column/article/224559) 已经讲过FaaS官方提供的域名受限,只能下载,这个链接就是我用FaaS部署的“待办任务”:[http://todo.jike-serverless.online/list](http://todo.jike-serverless.online/list)
|
||||
|
||||
期待你的作业。如果今天的内容让你有所收获,也欢迎你转发给你的朋友,邀请他一起学习。
|
||||
|
||||
## 参考资料
|
||||
|
||||
[1] [http://httpd.apache.org/](http://httpd.apache.org/)<br>
|
||||
[2] [https://restfulapi.net/http-methods/](https://restfulapi.net/http-methods/)<br>
|
||||
[3] [https://github.com/typicode/lowdb](https://github.com/typicode/lowdb)
|
||||
Reference in New Issue
Block a user