mirror of
https://github.com/cheetahlou/CategoryResourceRepost.git
synced 2025-11-17 22:53:42 +08:00
mod
This commit is contained in:
166
极客时间专栏/Serverless入门课/基础篇/01|定义:到底什么是Serverless?.md
Normal file
166
极客时间专栏/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
极客时间专栏/Serverless入门课/基础篇/02 | 原理:通过一个案例,理解FaaS的运行逻辑.md
Normal file
119
极客时间专栏/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
极客时间专栏/Serverless入门课/基础篇/03 | 原理:FaaS的两种进程模型及应用场景.md
Normal file
125
极客时间专栏/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
极客时间专栏/Serverless入门课/基础篇/04 | 原理:FaaS应用如何才能快速扩缩容?.md
Normal file
117
极客时间专栏/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)
|
||||
266
极客时间专栏/Serverless入门课/实战篇/08 | 搭建私有Serverless(一):K8s和云原生CNCF.md
Normal file
266
极客时间专栏/Serverless入门课/实战篇/08 | 搭建私有Serverless(一):K8s和云原生CNCF.md
Normal file
@@ -0,0 +1,266 @@
|
||||
<audio id="audio" title="08 | 搭建私有Serverless(一):K8s和云原生CNCF" controls="" preload="none"><source id="mp3" src="https://static001.geekbang.org/resource/audio/ba/10/bad826855608b5a3e2dcde8b8bd08210.mp3"></audio>
|
||||
|
||||
你好,我是秦粤。上节课我们只是用Docker部署了index.js,如果我们将所有拆解出来的微服务都用Docker独立部署,我们就要同时管理多个Docker容器,也就是Docker集群。如果是更复杂一些的业务,可能需要同时管理几十甚至上百个微服务,显然我们手动维护Docker集群的效率就太低了。而容器即服务CaaS,恰好也需要集群的管理工具。我们也知道FaaS的底层就是CaaS,那CaaS又是如何管理这么多函数实例的呢?怎么做才能提升效率?
|
||||
|
||||
我想你应该听过Kubernetes,它也叫K8s(后面统一简称K8s),用于自动部署、扩展和管理容器化应用程序的开源系统,是Docker集群的管理工具。为了解决上述问题,其实我们就可以考虑使用它。K8s的好处就在于,它具备跨环境统一部署的能力。
|
||||
|
||||
这节课,我们就试着在本地环境中搭建K8s来管理我们的Docker集群。但正常情况下,这个场景需要几台机器才能完成,而通过Docker,我们还是可以用一台机器就可以在本地搭建一个低配版的K8s。
|
||||
|
||||
下节课,我们还会在今天内容的基础上,用K8s的CaaS方式实现一套Serverless环境。通过这两节课的内容,你就可以完整地搭建出属于自己的Serverless了。
|
||||
|
||||
话不多说,我们现在就开始,希望你能跟着我一起多动手。
|
||||
|
||||
## PC上的K8s
|
||||
|
||||
那在开始之前,我们先得把安装问题解决了,这部分可能会有点小困难,所以我也给你详细讲下。
|
||||
|
||||
首先我们需要安装[kubectl](https://kubernetes.io/zh/docs/tasks/tools/install-kubectl/),它是K8s的命令行工具。
|
||||
|
||||
你需要在你的PC上安装K8s,如果你的操作系统是MacOS或者Windows,那么就比较简单了,桌面版的Docker已经自带了[K8s](https://www.docker.com/products/kubernetes);其它操作系统的同学需要安装[minikube](https://kubernetes.io/zh/docs/tasks/tools/install-minikube)。
|
||||
|
||||
不过,要顺利启动桌面版Docker自带的K8s,你还得解决国内Docker镜像下载不了的问题,这里请你先下载[第8课的代码](https://github.com/pusongyang/todolist-backend/tree/lesson08)。接着,请你跟着我的步骤进行操作:
|
||||
|
||||
1. 开通阿里云的[容器镜像仓库](https://cr.console.aliyun.com/);
|
||||
1. 在阿里云的容器镜像服务里,找到镜像加速器,复制你的镜像加速器地址;
|
||||
1. 打开桌面版Docker的控制台,找到Docker Engine。
|
||||
|
||||
```
|
||||
{
|
||||
"registry-mirrors" : [
|
||||
"https://你的加速地址.mirror.aliyuncs.com"
|
||||
],
|
||||
"debug" : true,
|
||||
"experimental" : true
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
1. 预下载K8s所需要的所有镜像,执行我目录下的docker-k8s-prefetch.sh,如果你是Windows操作系统,建议使用gitBash[1];
|
||||
|
||||
```
|
||||
chmode +x docker-k8s-prefetch.sh
|
||||
./docker-k8s-prefetch.sh
|
||||
|
||||
```
|
||||
|
||||
1. 上面拉取完运行K8s所需的Docker镜像,你就可以在桌面版Docker的K8s项中,勾选启动K8s了。
|
||||
|
||||
**现在,K8s就能顺利启动了,启动成功后,请你继续执行下面的命令。**
|
||||
|
||||
查看安装好的K8s系统运行状况。
|
||||
|
||||
```
|
||||
kubectl get all -n kube-system
|
||||
|
||||
```
|
||||
|
||||
查看K8s有哪些配置环境,对应~/.kube/config。
|
||||
|
||||
```
|
||||
kubectl config get-contexts
|
||||
|
||||
```
|
||||
|
||||
查看当前K8s环境的整体情况。
|
||||
|
||||
```
|
||||
kubectl get all
|
||||
|
||||
```
|
||||
|
||||
按照我的流程走,在大部分的机器上本地运行K8s都是没有问题的,如果你卡住了,请在留言区告知我,我帮你解决问题。
|
||||
|
||||
## K8s介绍
|
||||
|
||||
安装完K8s之后,我们其实还是要简单地了解下K8s,这对于你后面应用它有重要的意义。
|
||||
|
||||
我想你应该知道,K8s是云原生Cloud Native[2] 的重要组成部分(目前,K8s的文档[3]已经全面中文化,建议你多翻阅),而云原生其实就是一套通用的云服务商解决方案。在我们的前几节课中,就有同学问:“如果我使用了某个运营商的Serverless,就和这个云服务商强制绑定了怎么办?我是不是就没办法使用其他运营商的服务了?”这就是服务商锁定vendor-lock,而云原生的诞生就是为了解决这个问题,通过云原生基金会CNCF(Cloud Native Computing Foundation)[4],我们可以得到一整套解锁云服务商的开源解决方案。
|
||||
|
||||
那K8s作为云原生Cloud Native的重要组成部分之一,它的作用是什么呢?这里我先留一个悬念,通过理解K8s的原理,你就能清楚这个问题,并充分利用好K8s了。
|
||||
|
||||
我们先来看看K8s的原理图:
|
||||
|
||||
<img src="https://static001.geekbang.org/resource/image/70/f8/7084735b118636816d1284a80e67d0f8.png" alt="">
|
||||
|
||||
通过图示我们可以知道,PC本地安装kubectl是K8s的命令行操作工具,通过它,我们就可以控制K8s集群了。又因为kubectl是通过加密通信的,所以我们可以在一台电脑上同时控制多个K8s集群,不过需要指定当前操作的上下文context。这个也就是云原生的重要理念,我们的架构可以部署在多套云服务环境上。
|
||||
|
||||
在K8s集群中,Master节点很重要,它是我们整个集群的中枢。没错,Master节点就是Stateful的。Master节点由API Server、etcd、kube-controller-manager等几个核心成员组成,它只负责维持整个K8s集群的状态,为了保证职责单一,Master节点不会运行我们的容器实例。
|
||||
|
||||
Worker节点,也就是K8s Node节点,才是我们部署的容器真正运行的地方,但在K8s中,运行容器的最小单位是Pod。一个Pod具备一个集群IP且端口共享,Pod里可以运行一个或多个容器,但最佳的做法还是一个Pod只运行一个容器。这是因为一个Pod里面运行多个容器,容器会竞争Pod的资源,也会影响Pod的启动速度;而一个Pod里只运行一个容器,可以方便我们快速定位问题,监控指标也比较明确。
|
||||
|
||||
在K8s集群中,它会构建自己的私有网络,每个容器都有自己的集群IP,容器在集群内部可以互相访问,集群外却无法直接访问。因此我们如果要从外部访问K8s集群提供的服务,则需要通过K8s service将服务暴露出来才行。
|
||||
|
||||
### 案例:“待办任务”K8s版本
|
||||
|
||||
现在原理我是讲出来了,但可能还是有点不好理解,接下来我们就还是套进案例去看,依然是我们的“待办任务”Web服务,我们现在把它部署到K8s集群中运行一下,你可以切身体验。相信这样,你就非常清楚这其中的原理了。不过我们本地搭建的例子中,为了节省资源只有一个Master节点,所有的内容都部署在这个Master节点中。
|
||||
|
||||
还记得我们上节课构建的Docker镜像吗?我们就用它来部署本地的K8s集群。
|
||||
|
||||
我们通常在实际项目中会使用YAML文件来控制我们的应用部署。YAML你可以理解为,就是将我们在K8s部署的所有要做的事情,都写成一个文件,这样就避免了我们要记录大量的kubectl命令执行。不过,K8s也细心地帮我们准备了K8s对象和YAML文件互相转换的能力。这种能力可以让我们快速地将一个K8s集群中部署的结构导出YAML文件,然后再在另一个K8s集群中用这个YAML文件还原出同样的部署结构。
|
||||
|
||||
我们需要先确认一下,我们当前的操作是在正确的K8s集群上下文中。对应我们的例子里,也就是看当前选中的集群是不是docker-desktop。
|
||||
|
||||
```
|
||||
kubectl config get-contexts
|
||||
|
||||
```
|
||||
|
||||
<img src="https://static001.geekbang.org/resource/image/91/bc/910eae7773766239c81400991d4568bc.png" alt="">
|
||||
|
||||
如果不对,则需要执行切换集群:
|
||||
|
||||
```
|
||||
kubectl config use-context docker-desktop
|
||||
|
||||
```
|
||||
|
||||
然后需要我们添加一下拉取镜像的证书服务:
|
||||
|
||||
```
|
||||
kubectl create secret docker-registry regcred --docker-server=registry.cn-shanghai.aliyuncs.com --docker-username=你的容器镜像仓库用户名 --docker-password=你的容器镜像仓库密码
|
||||
|
||||
```
|
||||
|
||||
这里我需要解释一下,通常我们在镜像仓库中可以设置这个仓库:公开或者私有。如果是操作系统的镜像,设置为公开是完全没有问题的,所有人都可以下载我们的公开镜像;但如果是我们自己的应用镜像,还是需要设置成私有,下载私有镜像需要验证用户身份,也就是Docker Login的操作。因为我们应用的镜像仓库中,包含我们的最终运行代码,往往会有我们数据库的登录用户名和密码,或者我们云服务的ak/sk,这些重要信息如果泄露,很容易让我们的应用受到攻击。
|
||||
|
||||
当你添加完secret后,就可以通过下面的命令来查看secret服务了:
|
||||
|
||||
```
|
||||
kubectl get secret regcred
|
||||
|
||||
```
|
||||
|
||||
另外我还需要再啰嗦一下,secret也不建议你用YAML文件设置,毕竟放着你用户名和密码的文件还是越少越好。
|
||||
|
||||
做完准备工作,对我们这次部署的项目而言就很简单了,只需要再执行一句话:
|
||||
|
||||
```
|
||||
kubectl apply -f myapp.yaml
|
||||
|
||||
```
|
||||
|
||||
这句话的意思就是,告诉K8s集群,请按照我的部署文件myapp.yaml,部署我的应用。具体如下图所示:
|
||||
|
||||
<img src="https://static001.geekbang.org/resource/image/f9/31/f92f4b5abd24b1e25ea269d1c2528b31.png" alt="">
|
||||
|
||||
通过获取容器的运行状态,对照上图我粗略地讲解一下我们的myapp.yaml文件吧。
|
||||
|
||||
- 首先我们指定要创建一个service/myapp,它选中打了"app:myapp"标签的Pod,集群内访问端口号3001,并且暴露service的端口号30512。
|
||||
- 然后我们创建了部署服务deployment.apps/myapp,它负责保持我们的Pod数量恒久为1,并且给Pod打上"app:myapp"的标签,也就是负责我们的Pod持久化,一旦Pod挂了,部署服务就会重新拉起一个。
|
||||
- 最后我们的容器服务,申明了自己的Docker镜像是什么,拉取镜像的secret,以及需要什么资源。
|
||||
|
||||
现在我们再回看K8s的原理图,不过这张是实现“待办任务”Web服务版本的:
|
||||
|
||||
<img src="https://static001.geekbang.org/resource/image/a1/4c/a11f760ad9e3d8ba42da0d878a61a04c.png" alt="">
|
||||
|
||||
首先我们可以看出,使用K8s仍然需要我们上节课的Docker发布流程:build、ship、run。不过现在有很多云服务商也会提供Docker镜像构建服务,你只需要上传你的Dockerfile,就会帮你构建镜像并且push到镜像仓库。云服务商提供的镜像构建服务的速度,比你本地构建要快很多倍。
|
||||
|
||||
而且相信你也发现了,K8s其实就是一套Docker容器实例的运行保障机制。我们自己Run一个Docker镜像,会有许多因素要考虑,例如安全性、网络隔离、日志、性能监控等等。这些K8s都帮我们考虑到了,它提供了一个Docker运行的最佳环境架构,而且还是开源的。
|
||||
|
||||
还有,既然我们本地都可以运行K8s的YAML文件,那么我们在云上是不是也能运行?你还记得前面我们留的悬念吧,现在就解决了。
|
||||
|
||||
通过K8s,我们要解开云服务商锁定vendor-lock就很简单了。我们只需要将云服务商提供的K8s环境添加到我们kubectl的配置文件中,就可以让我们的应用运行在云服务商的K8s环境中了。目前所有的较大的云服务商都已经加入CNCF,所以当你掌握K8s后,就可以根据云服务商的价格和自己喜好,自由地选择将你的K8s集群部署在CNCF成员的云服务商上,甚至你也可以在自己的机房搭建K8s环境,部署你的应用。
|
||||
|
||||
到这儿,我们就部署好了一个K8s集群,那之后我们该如何实时监控容器的运行状态呢?K8s既然能管理容器集群,控制容器运行实例的个数,那应该也能实时监测容器,帮我们解决扩缩容的问题吧?是的,其实上节课我们已经介绍到了容器扩缩容的原理,但并没有给你讲如何实现,那接下来我们就重点看看K8s如何实现实时监控和自动扩缩容。
|
||||
|
||||
## K8s如何实现扩缩容?
|
||||
|
||||
首先,我们要知道的一点就是,K8s其实还向我们隐藏了一部分内容,就是它自身的服务状态。而我们不指定命名空间,默认的命名空间其实是default空间。要查看K8s集群系统的运行状态,我们可以通过指定namespace为kube-system来查看。K8s集群通过namespace隔离,一定程度上,隐藏了系统配置,这可以避免我们误操作。另外它也提供给我们一种逻辑隔离手段,将不同用途的服务和节点划分开来。
|
||||
|
||||
<img src="https://static001.geekbang.org/resource/image/fb/13/fb15835cb57a3e7d587aa2d401123213.png" alt="">
|
||||
|
||||
没错,K8s自己的服务也是运行在自己的集群中的,不过是通过命名空间,将自己做了隔离。这里需要你注意的是,这些服务我不建议你尝试去修改,因为它们涉及到了K8s集群的稳定性;但同时,K8s集群本身也具备扩展性:我们可以通过给K8s安装组件Component,扩展K8s的能力。接下来我先向你介绍K8s中的性能指标metrics组件[5]。
|
||||
|
||||
我的代码根目录下已经准备好了metric组件的YAML文件,你只需要执行安装就可以了:
|
||||
|
||||
```
|
||||
kubectl apply -f metrics-components.yaml
|
||||
|
||||
```
|
||||
|
||||
安装完后,我们再看K8s的系统命名空间:
|
||||
|
||||
<img src="https://static001.geekbang.org/resource/image/34/ff/34fe0c9268d5913607fd25464227efff.png" alt="">
|
||||
|
||||
对比你就能发现,我们已经安装并启动了metrics-server。那么metrics组件有什么用呢?我们执行下面的命令看看:
|
||||
|
||||
```
|
||||
kubectl top node
|
||||
|
||||
```
|
||||
|
||||
<img src="https://static001.geekbang.org/resource/image/f0/08/f0e5f436d48c47899ebcdf5045ed2a08.png" alt="">
|
||||
|
||||
安装metrics组件后,它就可以将我们应用的监控指标metrics显示出来了。没错,这里我们又可以用到上一讲的内容了。既然我们有了实时的监控指标,那么我们就可以依赖这个指标,来做我们的自动扩缩容了:
|
||||
|
||||
```
|
||||
kubectl autoscale deployment myapp --cpu-percent=30 --min=1 --max=3
|
||||
|
||||
```
|
||||
|
||||
上面这句话的意思就是,添加一个自动扩缩容部署服务,cpu水位是30%,最小维持1个Pod,最大维持3个Pod。执行完后,我们就发现会多了一个部署服务。
|
||||
|
||||
```
|
||||
kubectl get hpa
|
||||
|
||||
```
|
||||
|
||||
<img src="https://static001.geekbang.org/resource/image/56/2d/56a7a23302c7feabf3df14ae341bf52d.png" alt="">
|
||||
|
||||
接下来,我们就可以模拟压测了:
|
||||
|
||||
```
|
||||
kubectl run -i --tty load-generator --image=busybox /bin/sh
|
||||
$ while true; do wget -q -O- http://10.1.0.16:3001/api/rule; done
|
||||
|
||||
```
|
||||
|
||||
这里我们用一个K8s的Pod,启动busybox镜像,执行死循环,压测我们的MyApp服务。不过我们目前用Node.js实现的应用可以扛住的流量比较大,单机模拟的压测,轻易还压测不到扩容的水位。
|
||||
|
||||
<img src="https://static001.geekbang.org/resource/image/1e/76/1eaa1c830583326cb0bd5fa919270c76.png" alt="">
|
||||
|
||||
## 总结
|
||||
|
||||
这节课我向你介绍了云原生基金会CNCF的重要成员:Kubernetes。**K8s是用于自动部署、扩展和管理容器化应用程序的开源系统。**云原生其实就是一套通用的云服务商解决方案。
|
||||
|
||||
然后我们一起体验了在本地PC上,通过Docker desktop搭建K8s。搭建完后,我还向你介绍了K8s的运行原理:K8s Master节点和Worker节点。其中,Master节点,负责我们整个K8s集群的运作状态;Worker节点则是具体运行我们容器的地方。
|
||||
|
||||
之后,我们就开始把“待办任务”Web服务,通过一个K8s的YAML文件来部署,并且暴露NodePort,让我们用浏览器访问。
|
||||
|
||||
为了展示K8s如何通过组件Component扩展能力,接着我们介绍了K8s中如何安装使用组件metrics:我们通过一个YAML文件将metrics组件安装到了K8s集群的kube-system命名空间中后,就可以监控到应用的运行指标metrics了。给K8s集群添加上监控指标metrics的能力,我们就可以通过autoscale命令,让应用根据metrics指标和水位来扩缩容了。
|
||||
|
||||
最后我们启动了一个BusyBox的Docker容器,模拟压测了我们的“待办任务”Web服务。
|
||||
|
||||
总的来说,这节课我们的最终目的就是在本地部署一套K8s集群,通过我们“待办任务”Web服务的K8s版本,让你了解K8s的工作原理。我们已经把下节课的准备工作做好了,下节课我们将在K8s的基础上部署Serverless,可以说,实现属于你自己的Serverless,你已经完成了一半。
|
||||
|
||||
## 作业
|
||||
|
||||
这节课是实战课,所以作业就是我们今天要练习的内容。请在你自己的电脑上安装K8s集群,部署我们的“待办任务”Web服务到自己的K8s集群,并从浏览器中访问到K8s集群提供的服务。
|
||||
|
||||
另外,你可以尝试一下,手动删除我们部署的MyApp Pod。
|
||||
|
||||
```
|
||||
kubectl delete pod/你的pod名字
|
||||
|
||||
```
|
||||
|
||||
但你很快就会发现,这个Pod会被K8s重新拉起,而我们要清除部署的MyApp的所有内容其实也很简单,只需要告诉K8s删除我们的myapp.yaml文件创建的资源就可以了。
|
||||
|
||||
```
|
||||
kubectl delete -f myapp.yaml
|
||||
|
||||
```
|
||||
|
||||
快来动手尝试一下吧,期待你也能分享下今天的成果以及感受。另外,如果今天的内容让你有所收获,也欢迎你把它分享给身边的朋友,邀请他加入学习。
|
||||
|
||||
## 参考资料
|
||||
|
||||
[1] [https://gitforwindows.org/](https://gitforwindows.org/)
|
||||
|
||||
[2] [https://www.cncf.io/](https://www.cncf.io/)
|
||||
|
||||
[3] [https://kubernetes.io/zh/](https://kubernetes.io/zh/)
|
||||
|
||||
[4] [https://github.com/cncf/landscape](https://github.com/cncf/landscape)
|
||||
|
||||
[5] [https://github.com/kubernetes-incubator/metrics-server/](https://github.com/kubernetes-incubator/metrics-server/)
|
||||
@@ -0,0 +1,266 @@
|
||||
<audio id="audio" title="09 | 搭建私有Serverless(二):基于K8s的Serverless" controls="" preload="none"><source id="mp3" src="https://static001.geekbang.org/resource/audio/a2/47/a29155c426f683d24ad4bc302d15f447.mp3"></audio>
|
||||
|
||||
你好,我是秦粤。上节课我向你介绍了云原生基金会CNCF的重要成员K8s,它是用于自动部署、扩展和管理容器化应用程序的开源系统。通过实践,我们在本地搭建K8s,并将“待办任务”Web服务案例部署到了本地K8s上。K8s这门技术,我推荐你一定要学习下,不管是前端还是后端,因为从目前的发展趋势来看,这门技术必定会越来越重要。
|
||||
|
||||
今天这节课我们就继续学习如何搭建私有的Serverless环境。具体来说,我们会在上节课部署本地K8s的基础上,搭建Serverless底座,然后继续安装组件,扩展K8s的集群能力,最终让本地K8s集群支持Serverless。
|
||||
|
||||
## 搭建Serverless的前提
|
||||
|
||||
在开始之前,我们先得清楚一个问题,之前在基础篇讲Serverless Computing,也就是FaaS的时候,也有同学提问到,“微服务、Service Mesh和Serverless到底是什么关系?”
|
||||
|
||||
这些概念确实很高频地出现在我们的视野,不过你不要有压力,我在学习Serverless的时候也被这些概念所困扰。我们可以先回顾下微服务,我们在用微服务做BaaS化时,相信你也发现了微服务中有很多概念和Serverless的概念很接近。
|
||||
|
||||
Service Mesh简单来说就是让微服务应用无感知的微服务网络通讯方案。我们可以将Serverless架构的网络通讯也托管给ServiceMesh。通过Service Mesh,Serverless组件可以和K8s集群复杂且精密地配合在一起,最终支撑我们部署的应用Serverless化。我们看下下面这张架构图:
|
||||
|
||||
<img src="https://static001.geekbang.org/resource/image/9d/31/9d7da97db12f285d62b317c3ff894731.png" alt="">
|
||||
|
||||
通过图示,我们可以清楚地看到:Serverless的底层可以基于Service Mesh来搭建。但Service Mesh其实只是实现Serverless网络通讯的其中一种方案,除此之外还有RSocket、gRPC、Dubbo等方案,而我选择Service Mesh的原因是这套方案是可以基于K8s组件的,而且还提供了可视化工具帮助我们理解Serverless的运行机制,比如:如何做到零启动?如何控制灰度流量?等等。如果要实践,Service Mesh无疑是首选。
|
||||
|
||||
## Serverless底座:Service Mesh
|
||||
|
||||
有人把Kubernetes、 Service Mesh和Serverless技术称为云原生应用开发的三驾马车,到现在,我估摸着你也理解这其中的缘由了吧。这里我还是要说明下,我们后面几节课里引入了很多新名词,这些名词我基本都是走马观花大致给你过了下,让你有个宏观的了解。有时间的话,你还是应该再深入进去看看。
|
||||
|
||||
那么言归正传,我们现在具体看下Service Mesh的应用原理。
|
||||
|
||||
我们在讲微服务的时候,只是讲了拆解与合并的理论指导,并没有涉及到具体实现。那如果切换到实现的话,业界其实就有很多的微服务框架了,但大多数都是限定语言的SDK,尤其是Java的微服务框架特别多。
|
||||
|
||||
而SDK版本的微服务框架,其实很重的一块都在于微服务之间网络通讯的实现。例如服务请求失败重试,调用多个服务实例的负载均衡,服务请求量过大时的限流降级等等。这些逻辑往往还需要微服务的开发者关心,而且在每种SDK语言中都需要重复实现一遍。那有没有可能将微服务的网络通信逻辑从SDK中抽离出来呢?让我们的微服务变得更加轻量,甚至不用去关心网络如何通讯呢?
|
||||
|
||||
答案就是Service Mesh。我们举例来说明,还是用咱们的“待办任务”Web服务来举例。
|
||||
|
||||
我们如果把拆解后的应用部署到K8s集群Pod中,过程就如下图所示,MyApp应用会通过HTTP直接去调用集群内的用户微服务和待办任务微服务。
|
||||
|
||||
<img src="https://static001.geekbang.org/resource/image/a0/00/a0a9b05966e8761be2e72ba4532ec700.png" alt="">
|
||||
|
||||
但HTTP直接访问,带来的安全性问题又怎么办呢?如果有人在我们的集群中启动一个BusyBox容器,那不就直接可以对我们的用户微服务和待办任务微服务进行攻击了吗?还有,当我们有多个用户微服务实例时,我们又该如何分配流量呢?
|
||||
|
||||
所以通常我们使用传统微服务架构SDK,它里面会有大量的这种逻辑,而且有很多策略需要我们自己在调用SDK时去判断开启,我们的代码也将会和微服务架构的SDK大量地耦合在一起。
|
||||
|
||||
服务网格Service Mesh则是换了一种思路,它将微服务中的网络通信逻辑抽离了出来,通过无侵入的方式接管我们的网络流量,让我们不用再去关心那么重的微服务SDK了。下面我们看看Service Mesh是怎么解决这个问题的。
|
||||
|
||||
<img src="https://static001.geekbang.org/resource/image/d1/75/d11343ddee25c4c28d6df0f822558975.png" alt="">
|
||||
|
||||
通过图示可知,Service Mesh可以分为数据面板和控制面板,数据面板负责接管我们的网络通讯;控制面板则控制和反馈网络通讯的状态。Service Mesh将我们网络的通讯通过注入一个边车Sidecar全部承接了过去。
|
||||
|
||||
数据面板中我们的应用和微服务,看上去直接跟Sidecar通信,但实际上,Sidecar可以通过流量劫持来实现,所以通常我们的应用和微服务是无感的,不用有任何的改动,只是使用HTTP请求数据。
|
||||
|
||||
控制面板则复杂一些,它也是Service Mesh的运作核心。pilot是整个Control Plane的驾驶员,它负责服务发现、流量管理和扩缩容;citadel则是控制面板的守护堡垒,它负责安全证书和鉴权;Mixer则是通讯官,将我们控制面板的策略下发,并且收集每个服务的运行状况。
|
||||
|
||||
现在你应该清楚Service Mesh是怎么回事了,它是怎么把微服务的网络通信逻辑从SDK中抽离出来的,我们又为什么说Service Mesh就是Serverless的网络通讯底座。
|
||||
|
||||
## Serverless底座搭建:K8s组件Istio
|
||||
|
||||
那接下来我们就要动手搭建Service Mesh了。
|
||||
|
||||
首先,我们得扩大一下Docker Desktop操作系统的占用资源,这是因为我们后面在搭建的过程中需要创建一堆容器。我推荐你将CPUs配置到4核,内存至少8GB,这是从我的经验出发一个较为合适的参数。
|
||||
|
||||
<img src="https://static001.geekbang.org/resource/image/50/ec/50d7e965a9734aacf39bb7f77e55a1ec.png" alt="">
|
||||
|
||||
设置好后,我们需要用到CNCF的另外一位重要成员Istio[1] 了,它是在K8s上Service Mesh的实现方式,用于连接、保护、控制和观测服务。有关它的详细介绍,我们在这就不展开了,不清楚的话可以查看我们的参考资料。
|
||||
|
||||
Istio的安装脚本,我已经放在我们这节课的[Github仓库代码](https://github.com/pusongyang/todolist-backend/tree/lesson09)中了。下面我们要进入根目录下的install-istio,这里Istio官方其实提供了Istio 1.4.3 K8s安装包,但为了简化大家的操作,我直接放到项目代码中了。
|
||||
|
||||
然后kubectl apply安装Istio。
|
||||
|
||||
```
|
||||
kubectl apply -f .
|
||||
|
||||
```
|
||||
|
||||
安装完毕后,我们就可以通过命名空间istio-system来查看Istio是否安装成功。
|
||||
|
||||
```
|
||||
kubectl get all -n istio-system
|
||||
|
||||
```
|
||||
|
||||
相信你也发现了吧,这跟我们上节课中安装metrics组件一样,Istio也是采用K8s组件Component的方式来安装的。
|
||||
|
||||
<img src="https://static001.geekbang.org/resource/image/a3/b5/a3cadbeebf92e839ac0b4a76c35d26b5.png" alt="">
|
||||
|
||||
不过使用了Istio以后,还有一个细节需要我们注意:默认Istio不会开启Sidecar注入,这个需要我们手动开启。
|
||||
|
||||
我们可以通过给命名空间default打上label,让我们部署的Pod自动打开Sidecar。
|
||||
|
||||
```
|
||||
kubectl label ns default istio-injection=enabled
|
||||
|
||||
```
|
||||
|
||||
Istio会根据我们的标签去判断,当前部署的应用是否需要开启Sidecar。
|
||||
|
||||
```
|
||||
kubectl describe ns default
|
||||
|
||||
```
|
||||
|
||||
<img src="https://static001.geekbang.org/resource/image/19/42/19d917ec41aa365693d4830d88c0ad42.png" alt="">
|
||||
|
||||
好了,我们现在可以部署我们的应用了。我们将项目目录下的Dockerfile、Dockerfile-rule、Dockerfile-user都用Docker构建一遍,并且上传到我们的Registry。
|
||||
|
||||
```
|
||||
docker build --force-rm -t registry.cn-shanghai.aliyuncs.com/jike-serverless/todolist:lesson09 -f Dockerfile .
|
||||
docker build --force-rm -t registry.cn-shanghai.aliyuncs.com/jike-serverless/rule:lesson09 -f Dockerfile-rule .
|
||||
docker build --force-rm -t registry.cn-shanghai.aliyuncs.com/jike-serverless/user:lesson09 -f Dockerfile-user .
|
||||
docker push registry.cn-shanghai.aliyuncs.com/jike-serverless/todolist:lesson09
|
||||
docker push registry.cn-shanghai.aliyuncs.com/jike-serverless/rule:lesson09
|
||||
docker push registry.cn-shanghai.aliyuncs.com/jike-serverless/user:lesson09
|
||||
|
||||
```
|
||||
|
||||
然后修改项目istio-myapp目录中的YAML文件,改成你自己仓库中的URI。接着在istio-myapp目录下执行,kubectl apply“点”部署所有YAML文件。
|
||||
|
||||
```
|
||||
kubectl apply -f .
|
||||
|
||||
```
|
||||
|
||||
部署完成后,我们通过kubectl describe查看一下MyApp服务暴露的端口号:
|
||||
|
||||
```
|
||||
kubectl describe service/myapp
|
||||
|
||||
```
|
||||
|
||||
<img src="https://static001.geekbang.org/resource/image/03/97/0331db708282a70543c4c4ef3bcab997.png" alt="">
|
||||
|
||||
接下来我们用浏览器访问[http://localhost:31947](http://localhost:31947%EF%BC%8C),就可以看到我们新部署的MyApp应用了。
|
||||
|
||||
你也许会好奇,这跟我们之前的有什么不同,Istio都做了什么?
|
||||
|
||||
我们将Istio的控制面板服务kiali也通过Service暴露到端口上,访问一下看看。
|
||||
|
||||
```
|
||||
kubectl expose deployment.apps/kiali --type=NodePort --port=20001 --name='kiali-local' -n istio-system
|
||||
|
||||
```
|
||||
|
||||
然后查看一下kiali的本地端口号。
|
||||
|
||||
```
|
||||
kubectl describe svc kiali-local -n istio-system
|
||||
|
||||
```
|
||||
|
||||
接着用浏览器打开kiali,进入后你就可以看到我们应用调用微服务的网络拓扑图了。怎么样,是不是很惊艳?
|
||||
|
||||
<img src="https://static001.geekbang.org/resource/image/c8/7c/c86557779fd349ee8032a62d2bcfc57c.png" alt="">
|
||||
|
||||
Service Mesh可以协助我们处理应用和微服务网络的连接、保护、控制和观测问题。综上,再次总结下,Serverless的底层,我们完全可以依赖Service Mesh来处理复杂的网络问题。
|
||||
|
||||
最后演示完,我们就可以通过kubectl delete清除刚刚部署的应用,注意要在istio-myapp目录下执行。
|
||||
|
||||
```
|
||||
kubectl delete -f .
|
||||
|
||||
```
|
||||
|
||||
到这儿,部署好Istio Service Mesh,Serverless的底层部署,我们就已经实现了一大半。
|
||||
|
||||
## Serverless完整实现:K8s组件Knative
|
||||
|
||||
现在,就还剩最后一步,安装组件了。如果你能想到,在Service Mesh的底座上,还需要加上哪些组件才能满足我们Serverless的需求,说明你已经真正地理解了K8s的组件Component的威力了。
|
||||
|
||||
接下来我们就基于Istio,看看Serverless在K8s的架构方案上还需要添加哪些内容。
|
||||
|
||||
Knative是通过整合:工作负载管理(和动态扩缩)以及事件模型来实现的Serverless标准,也叫容器化Serverless。
|
||||
|
||||
Knative社区的主要贡献者有:Google、Pivotal、IBM、Red Hat,可见其阵容强大。还有,CloudFoundry、OpenShift这些PaaS提供商都在积极地参与Knative的建设。参考资料中我还放了“由阿里云提供Knative”的[电子书](https://github.com/cloudnativeapp/meetup/blob/master/Knative%20%E4%BA%91%E5%8E%9F%E7%94%9F%E5%BA%94%E7%94%A8%E5%BC%80%E5%8F%91%E6%8C%87%E5%8D%97.pdf),这里作为福利送给你。
|
||||
|
||||
接下来我们看看Knative是怎么做到Serverless的吧。
|
||||
|
||||
Knative在Istio的基础上,加上了流量管控和灰度发布能力、路由Route控制、版本Revision快照和自动扩缩容,就组成了Server集合;它还将触发器、发布管道Pipeline结合起来组成了Event集合。
|
||||
|
||||
<img src="https://static001.geekbang.org/resource/image/f9/3d/f963da41d4c978542e685e74c9c5903d.png" alt="">
|
||||
|
||||
我们近几节课都有操作K8s,其实你应该能感觉到,它还是有些复杂的。Knative提供的应用托管服务可以大大降低直接操作K8s资源的复杂度和风险,提升应用的迭代和服务交付效率。
|
||||
|
||||
我们还是以“待办任务”Web服务为例,看看Knative是怎么做到的吧。
|
||||
|
||||
首先进入项目install-knative目录,执行kubectl apply安装Knative。
|
||||
|
||||
```
|
||||
kubectl apply -f .
|
||||
|
||||
```
|
||||
|
||||
安装完毕,你就可以通过命名空间knative-eventing和knative-serving,看到我们都安装了哪些组件。
|
||||
|
||||
```
|
||||
kubectl get all -n knative-serving
|
||||
kubectl get all -n knative-eventing
|
||||
|
||||
```
|
||||
|
||||
Knative帮我们部署了一套自动运维托管应用的服务,因此我们要部署应用,才能看到效果。我们再进入项目knative-myapp目录,执行kubectl apply。
|
||||
|
||||
```
|
||||
kubectl apply -f .
|
||||
|
||||
```
|
||||
|
||||
到这,我们的应用就部署起来了。要访问应用也比Istio要简单,通过get kservice我们就可以查看部署的应用域名。
|
||||
|
||||
```
|
||||
kubectl get ksvc
|
||||
|
||||
```
|
||||
|
||||
这个域名其实是通过Istio-ingress部署的,我们再看看Istio-ingressgateway的信息。
|
||||
|
||||
```
|
||||
kubectl get svc istio-ingressgateway -n istio-system
|
||||
|
||||
```
|
||||
|
||||
<img src="https://static001.geekbang.org/resource/image/84/90/84bacc4deff3166b7a444dc5bec12590.png" alt="">
|
||||
|
||||
我们可以看到,它绑定到我们本地IP:localhost上了,所以我们只需要添加一下Host,绑定我们MyApp的域名到127.0.0.1就可以了。
|
||||
|
||||
```
|
||||
// Mac上host文件地址是/etc/hosts
|
||||
127.0.0.1 myapp.default.example.com
|
||||
|
||||
```
|
||||
|
||||
然后我们就可以用浏览器直接访问我们的域名myapp.default.example.com,进入“待办任务”Web服务了。如果你多刷新几次页面就可以看到右上角我的名字,有时是“秦粤”有时是“粤秦D”。这其实是因为我的user服务开启了2个版本,而且我设置了50%的流量进入版本v1,50%的流量进入版本v2。另外我们还需要在页面上多点击几下,这是为了让我们查看Kiali的网络拓扑图时有数据。
|
||||
|
||||
目前Knative还没有官方的控制台,所以最后我们再看看Istio中Kiali的网络拓扑图,如果忘记了如何访问Kiali请看上一节,介绍Istio中的内容。
|
||||
|
||||
<img src="https://static001.geekbang.org/resource/image/7f/26/7f2b922b186b38f4eb324967b094de26.png" alt="">
|
||||
|
||||
你看,网络拓扑图是不是变得更复杂了?当然,因为Knative也是隐藏了一堆的内容,来简化应用和微服务的部署。好了,实践完,我们就再来看看原理,请看下图:
|
||||
|
||||
<img src="https://static001.geekbang.org/resource/image/f4/1f/f4a79ce34e82d0f72cf5a0ed3153e81f.png" alt="">
|
||||
|
||||
我来解释下这张图。为了让你更好地识别,我用红色表示Knative添加的组件,橙色表示Istio的组件。首先我们看到Knative又给每个Pod里面添加了一个伴生容器queue,它是专门用来实时采集反馈我们部署应用容器的监控指标metrics的,收集到的信息会反馈到autoscale,由autoscale决定是否需要扩缩容。
|
||||
|
||||
然后我们看看请求,从上面的操作,我们知道浏览器的HTTP请求是访问Istio-ingressGateway的,我们还需要绑定域名。这是因为Istio-ingressGateway是域名网关,它会验证请求的域名。我们的MyApp应用,接收到请求会调用用户微服务和待办任务微服务,这时就会访问activator了,这个是Knative比较重要的组件,它会hold住我们的请求,并查看一下目前有没有活跃的服务,如果没有,它会负责拉起一个服务,等服务启动后,再将请求转接过去。这也就是为什么Serverless可以缩容到0的原因。
|
||||
|
||||
另外,我再啰嗦一下,当我们的autoscale配置了可以缩容到0,如果一段时间没有请求,那么我们每个应用都会处于很低的水位,这时autoscale就会缩容到0了。当然我们的MyApp应用不可以缩容到0,因为它需要接收入口请求。
|
||||
|
||||
当MyApp有流量进来请求用户服务时,此时activator就会临时接收请求,让请求等待;然后通知autoscale扩容到1;等待autoscale扩容到1完毕后,activator再让用户容器处理等待的请求。
|
||||
|
||||
而我们在Knative中每次发布服务,都会被Revision组件拍一个快照,这个快照可以用于我们管理版本号和灰度流量分配。我们“待办任务”Web服务中的用户微服务的2个版本流量切换,其实就是利用Revision和Route实现的。
|
||||
|
||||
## 总结
|
||||
|
||||
这节课我首先介绍了Service Mesh。Service Mesh通过Pod给我们的应用容器注入了一个伴生容器Sidecar,配合控制面板来接管我们应用和微服务的网络流量,从而实现网络流量的连接、保护、控制和观测。
|
||||
|
||||
接着我介绍了CNCF的另一位重要成员:Istio,它是基于K8s组件实现的Service Mesh。我们在Istio的基础上又搭建了Knative,它也是基于K8s组件实现的一套Serverless方案。
|
||||
|
||||
总结来说就是,Service Mesh是一种微服务网络通讯方案,它通过Sidecar和控制面板接管了上层的网络通讯,可以让上层开发者无感知地使用网络通讯。我们可以复用Service Mesh的网络通讯能力,部署Serverless架构。通过Service Mesh、Serverless组件和K8s集群复杂且精密地配合,支撑我们部署的应用Serverless化。
|
||||
|
||||
另外我需要提醒你一下,Knative是容器Serverless方案,所以你在容器里面运行函数、微服务或者应用都可以,这完全取决于你的Dockerfile里面“编排”的是什么内容。我们这节课介绍的Knative,可以让你既部署FaaS也部署BaaS。还记得我们[[第1课]](https://time.geekbang.org/column/article/224559) 讲过:FaaS和BaaS都是基于CaaS实现的,Knative正是一种容器服务Serverless化的产物。我们将[第 1 课]和这节课的架构图映射一下,相信你对Serverless的实现会有新的体会。
|
||||
|
||||
<img src="https://static001.geekbang.org/resource/image/77/64/7771e2621ac14fddad00657a1909c564.jpg" alt="">
|
||||
|
||||
## 作业
|
||||
|
||||
这节课是实战课,所以作业就是我们今天课程中的内容。请在上节课安装的K8s集群的基础上,先安装Istio组件,部署Istio版本的“待办任务”Web服务;再安装Knative组件,部署Knative版本的“待办任务”Web服务。实践结束后,你可以在Docker Desktop中关闭K8s集群,这样就能清掉我们这两节课创建的所有资源了。
|
||||
|
||||
大胆尝试一下吧,期待你能与大家分享成果。如果今天的内容让你有所收获,也欢迎你把文章分享给更多的朋友。
|
||||
|
||||
## 参考资料
|
||||
|
||||
[1] [https://istio.io/](https://istio.io/)
|
||||
158
极客时间专栏/Serverless入门课/实战篇/10 | 经验:Serverless架构应该如何选型?.md
Normal file
158
极客时间专栏/Serverless入门课/实战篇/10 | 经验:Serverless架构应该如何选型?.md
Normal file
@@ -0,0 +1,158 @@
|
||||
<audio id="audio" title="10 | 经验:Serverless架构应该如何选型?" controls="" preload="none"><source id="mp3" src="https://static001.geekbang.org/resource/audio/53/0a/531db92429abb485932dce72ce709c0a.mp3"></audio>
|
||||
|
||||
你好,我是秦粤。通过前面的两节实践课,我们体验了在本地环境中搭建K8s,并且我们利用K8s的组件扩展能力,在本地的K8s上安装了Istio和Knative。正如我在前面课程中所说的,K8s可以让我们的集群架构,轻松迁移到其他集群上,那么今天我就带你将我们本地K8上部署的“待办任务”Web服务部署到云上的K8s集群。
|
||||
|
||||
实践课里还有这么个小细节,不知道你注意没,我们使用Knative时,应用和微服务部署都需要关心项目应用中的Dockerfile,而我在使用FaaS函数时,连Dockerfile都不用管了,其实这就是Serverless带来的变革之一。当然,现在有很多应用托管PaaS平台,也做了Serverless化,让我们部署一个应用时只需要关心Release分支的代码合并,例如Heroku、SAE等等。
|
||||
|
||||
这里我需要先解释一下,K8s集群的运维工作对于很多个人开发者来说,是有些重的。我们通常了解基本知识,用kubectl调用K8s集群就可以了。咱们课程里,我是为了让你更好理解Serverless的工作原理,所以才向你介绍Knative在K8s上的搭建和使用过程。
|
||||
|
||||
实际工作中K8s集群的运维,还是应该交给专业的运维人员。另外,云服务商的K8s集群,都会提供控制面板,一键安装组件。我们在使用Serverless的部署应用时,不用关心底层“被透明化”的类似Knative、Istio等等插件能力,这也是Serverless应用的价值所在,虽然它本身的底层构建在复杂且精密的各种服务上,但我们使用Serverless却极其精简。
|
||||
|
||||
在开始部署K8s上云之前,我们要先选择一个云服务商。正如我们上节课所说,K8s整体架构迁移能力,可以帮我们破解Vendor-lock,只要我们部署的云服务商是CNCF的成员,支持K8s集群就可以了。实际上目前几乎所有的云服务商都加入了CNCF阵营。因此,我们的K8s版本的“待办任务”Web服务,可以任意选择云服务商部署,你完全可以横向对比云服务商的各项指标去选择适合自己的。当我们有了选择权,也反向促进了云服务商的良性竞争。
|
||||
|
||||
## 云服务商
|
||||
|
||||
我们先看看2019年的[全球云服务商的市场占有率](https://www.canalys.com/newsroom/canalys-worldwide-cloud-infrastructure-Q4-2019-and-full-year-2019)数据,我也将按照这个数据排名,依次向你介绍云服务商和他的主要特色:
|
||||
|
||||
1. 亚马逊的AWS市占率32.4%。亚马逊凭借庞大复杂的全球电商业务,让其机房做到了覆盖全球,并引领云服务的发展,提供最全面的生态和最高稳定性的服务。云服务商老大的地位近年内都难以撼动。
|
||||
1. 微软的Azure市占率17.6%。依赖微软Windows全家桶的优势和近年的JavaScrpit技术社区的收购或者并购,他的市场地位紧跟亚马逊之后。整体云服务产品的报价也紧盯AWS,所有服务价格略低于AWS。
|
||||
1. 阿里巴巴的阿里云,市占率6.0%。国内市场占有率第一,随着阿里电商业务出海,阿里云机房也部署到了海外。在国内云里生态建设得比较完备,每年都经受双十一流量的洗礼,不断打磨稳定性。客服响应速度是一大亮点。
|
||||
1. 谷歌的谷歌云,市场占有率5.4%。谷歌是后起之秀,凭借15年提出CNCF云原生白皮书,通过建立规范和开源生态,迅速切入云服务领域并占有一席之地。价格策略上紧盯AWS,并依靠Google的搜索引擎对大规模集群调度能力的积累。云服务商中最高的物理机资源利用率,让谷歌云的价格做到了云服务商中的最低。
|
||||
1. 其他云,市场占有率38.5%。腾讯云、华为云等其他的云服务商都归并到了这里,还有一些专门做专有云服务的,比如CDN全球加速的Akamai,PaaS应用托管的Heroku等等。值得一提的是腾讯云,腾讯云从2019年开始大力发展Serverless,并积极和Serverless生态合作,估计是希望以此为突破点提升自己的市场占有率。
|
||||
|
||||
下面是我按我目前(注意只是目前)掌握的数据和认知整理的表格,在选择云服务商时你可以作为参考。其中访问限制应该是最优先考虑的,国内运营部署的应用,肯定是要首选国内云服务商。
|
||||
|
||||
<img src="https://static001.geekbang.org/resource/image/d3/63/d3f31145aa72d7390db032b81b947663.png" alt="">
|
||||
|
||||
当我们完成云服务商的选择后,理论上我们可以通过Docker容器,创建我们所需要的各种服务,例如Redis、MySQL、Kafka等等。具体怎么做,你应该很熟悉了,先去Docker Hub 官网,找到我们所需要的服务镜像,在这个镜像的基础上加上我们自己的用户名和密码,生成私有Docker镜像上传到我们的Registry,然后在K8s集群中就可以部署了。这也是前面我们讲到的Docker容器带来的颠覆式体验。
|
||||
|
||||
不过,我们在重度使用Docker技术的同时,也必须深入了解Docker和我们所用的具体镜像的限制。比如,如何解决应用镜像硬盘持久化的问题、如何解决MySQL镜像的容器扩缩容的问题、Kafka镜像集群如何搭建等等。这些都是新技术引入的新的问题,而且解决方案和传统运维虚拟机也不一样。
|
||||
|
||||
另外,为了提升我们的研发效能,我们还应该进一步了解云服务商还能为我们提供哪些能力,节省我们的时间和成本。当我们开发一个云上项目时,云服务商已经为我们准备了各种行业解决方案,来提升我们的开发速度,例如文件存储服务、视频媒体流转码服务、物联网MQTT解决方案等等。利用这些服务和我们前面讲的服务编排,可以进一步加速我们的研发速度。
|
||||
|
||||
## 云服务如何选型?
|
||||
|
||||
现在云服务商都提供数以百计的各种服务,但大体上我们可以分为以下3类:IaaS、PaaS和SaaS。
|
||||
|
||||
<img src="https://static001.geekbang.org/resource/image/af/42/af06bd8f061823956d558be91b446442.png" alt="">
|
||||
|
||||
我们先看看图示中的金字塔,这里我需要引入新的概念服务级别协议SLA:服务提供商与受服务用户之间具体达成了承诺的服务指标——质量、可用性、责任。看上去有些绕,简单来说,就是服务不达标,我们可以向云服务商索赔损失。
|
||||
|
||||
我们前面课程中所说的,消息队列的稳定性达到10个9,其实就是指SLA指标数据可靠性为99.99999999%,但是,消息队列的服务可用性其实是99.95%,也就是说消息队列服务服务一年中不可使用时间为4.38小时,一旦不可用时间超过了这个要求,云服务商则需要向客户赔付(如果这部分知识你没接触过的话,可以看下赵成老师的[这篇文章](https://time.geekbang.org/column/article/212722))。
|
||||
|
||||
因此对于云服务商来说,要维持资源的高可用性,必须保证资源调度及时,宁可浪费部分资源,也不能牺牲用户的可用性。而云服务的价格则和物理机虚拟化比例强相关,虚拟化比例越低,说明你对这个资源独占性越高,当然价格也就越高。物理机虚拟化比例,也是云服务商的资源调度能力,对云服务商来说核心指标就是CPU利用率。
|
||||
|
||||
了解了一些前提,接下来我们具体看看这3类。
|
||||
|
||||
- IaaS层是面向运维人员,服务器或虚拟机服务。可用性也最高,通常可以到4个9,99.99%。可控性高,虚拟机从操作系统开始,你可以登录虚拟机,并且任意安装各种自己所需的函数库和二进制包。资源的物理机虚拟化比例,通常是2:1的,性能是最稳定的。
|
||||
- PaaS层是面向开发者,通常部署在IaaS层之上,服务种类最为繁多。可用性低于IaaS,通常是99.95%。可控性中等,PaaS通常都提供特定的服务,例如应用托管、数据库等等,我们只能通过提供的控制台登录。资源的物理机虚拟化比例,通常是4:1,性能较稳定。
|
||||
- SaaS层是面向终端用户,通常部署在PaaS层之上。可用性低于PaaS,通常是99.9%。可控性低,SaaS直接面向用户提供服务,我们只能登录后台操作部分数据进行增删改查。资源的物理机虚拟化比例不太确定,但肯定超过8:1,性能一般。
|
||||
|
||||
FaaS则是一个特例,虽然它也属于面向开发者的,但利用FaaS极速的冷启动特性,它并不需要关心底层的高可用性。反而用它可以填满闲置的机器资源,提升物理机的资源利用率。这也是为什么在云服务商这么高的运作成本下,FaaS还能免费提供给大家使用。
|
||||
|
||||
<img src="https://static001.geekbang.org/resource/image/41/9b/4162456b4550db3be9be6daead865d9b.png" alt="">
|
||||
|
||||
我介绍SLA,主要是希望你能对云服务商提供的服务层级有个认识。我们在设计和运维自己的应用时,需要综合考虑到可用性和价格。FaaS是性价比最高的,所以我们在日常使用时,如果有适合FaaS的场景,应该尽可能地使用FaaS。
|
||||
|
||||
如果要深入了解云服务,我的经验是可以从云服务商网站的行业解决方案出发。先粗略了解一下,有哪些行业解决方案,便于我们掌握云服务商的能力边界。如果感觉比较凌乱的话,最好自己用“脑图”梳理一次。另外再说句题外话,我不建议你学习别人的脑图,因为脑图都是自己梳理思考的过程,你自己大脑的Map不一定适合别人,别人的Map也不适合你。
|
||||
|
||||
言归正传,我们自己在云上搭建K8s集群主要有2种方式:购买虚拟机自建和购买K8s集群。当然首推购买K8s集群,可以节省我们更多成本。K8s集群的Master节点,阿里云K8s集群是不收费的,而我们自己搭建则需要至少一台虚拟机。虚拟机自建,比较适合大型或拥有强大运维团队的互联网公司。但无论是自建还是购买K8s集群,我们搭建的K8s集群的底层都是IaaS。
|
||||
|
||||
## 云上部署K8s集群Knative
|
||||
|
||||
了解完选型相关的知识,接下来我们还是动手实操一下。
|
||||
|
||||
我们这节课的K8s例子,选择了阿里云的Serverless K8s集群:ASK。这个K8s集群的特点是,Master节点是免费的,只收取网关的费用,Worker节点是虚拟节点,而我们Pod中的容器是通过ECI容器创建的。传统的K8s集群ACK的Worker节点,需要我们自己购买虚拟机授权K8s集群,初始化成Worker节点。ECI是轻量级的Docker容器,同时具备高性能和低价格的优势。另外,ASK的Knative功能是新上线公测的,推荐它还是因为性价比。
|
||||
|
||||
我们使用K8s集群,同样可以自己安装Istio,再安装Knative,只需要注意K8s集群的版本就可以了。但云服务商提供的K8s集群,通常都已经帮你准备好了控制台操作。所以实际上我们使用云端的K8s集群,要比本地搭建还要简单。所以,我们只需要在ASK控制台,左边Knative(公测)中选择我们的K8s集群,点击“一键部署”就可以了。当然如果你选择的云服务商不支持“一键部署”,你可以通过查看K8s集群的版本号,选择对应的Istio版本和Knative版本,按照我们上节课所讲的内容,自行安装K8s组件。
|
||||
|
||||
另外为了方便新手,我还是需要提示一下如何在本地同时管理多个K8s集群。
|
||||
|
||||
首先我们打开本地的kubectl的配置文件:$HOME/.kube/config,我们可以看到,这个K8s集群的配置文件主要分为3个部分:clusters、contexts、users。
|
||||
|
||||
```
|
||||
apiVersion: v1
|
||||
clusters:
|
||||
- cluster:
|
||||
certificate-authority-data: xxx
|
||||
server: https://kubernetes.docker.internal:6443
|
||||
name: docker-desktop
|
||||
- cluster:
|
||||
certificate-authority-data: xxx
|
||||
server: https://k8s集群IP:6443
|
||||
name: kubernetes
|
||||
contexts:
|
||||
- context:
|
||||
cluster: docker-desktop
|
||||
user: docker-desktop
|
||||
name: docker-desktop
|
||||
- context:
|
||||
cluster: kubernetes-ask
|
||||
user: kubernetes-ask-admin
|
||||
name: kubernetes-admin-id
|
||||
current-context: docker-desktop
|
||||
kind: Config
|
||||
preferences: {}
|
||||
users:
|
||||
- name: docker-desktop
|
||||
user:
|
||||
client-certificate-data: xxx
|
||||
client-key-data: xxx
|
||||
- name: kubernetes-admin
|
||||
user:
|
||||
client-certificate-data: xxx
|
||||
client-key-data: xxx
|
||||
|
||||
|
||||
```
|
||||
|
||||
我们将云上K8s给我们提供的集群凭据的cluster、context、user,分别添加到config文件对应的clusters、contexts、users部分下面,就可以了。
|
||||
|
||||
我们再次查看kubectl config get contexts,就可以看到新添加的云上K8s集群了。
|
||||
|
||||
```
|
||||
kubectl config get-contexts
|
||||
|
||||
```
|
||||
|
||||
剩下的操作就跟我们上节课保持一致了。我们只需要在knative-myapp里面执行kubectl apply,就可以让我们的例子运行在云上的K8s集群了。
|
||||
|
||||
我们想访问云上K8s集群版本的“待办任务”Web服务时,同样也是用kubectl查看kservice,我们的域名。
|
||||
|
||||
```
|
||||
kubectl get kservice
|
||||
|
||||
```
|
||||
|
||||
<img src="https://static001.geekbang.org/resource/image/d3/20/d3810957c110ba84a71085d20a510820.png" alt="">
|
||||
|
||||
紧接着通过查看ingress-gateway了解K8s集群的外网入口IP。
|
||||
|
||||
```
|
||||
kubectl get svc -n knative-serving
|
||||
|
||||
```
|
||||
|
||||
<img src="https://static001.geekbang.org/resource/image/3c/b2/3cdd4a0a5affe9724a7c15755aa7beb2.png" alt="">
|
||||
|
||||
我们在本地通过Host绑定域名和EXTERNAL-IP,就可以访问了。我再啰嗦一句:如果是你自己的域名,你可以通过修改域名的DNS解析A值,指向这个EXTERNAL-IP就可以了。
|
||||
|
||||
<img src="https://static001.geekbang.org/resource/image/e6/55/e6d014f06a7a6047fef335052372b555.png" alt="">
|
||||
|
||||
我们同样也可以通过命名空间namespace,看看这个K8s集群中都给我们安装了哪些组件。还记得我们讲过的FaaS的HTTP触发器认证方式吗?我们部署在云上的K8s集群,调用我们的FaaS函数,就可以通过我们自己的容器实现函数鉴权的算法,走函数鉴权流程了。
|
||||
|
||||
到这儿,云上部署K8s集群Knative这个例子我们就实践完了,不知道你有没有跟着我一起动手操作?最后,还有一点需要提示你一下,如果你为了体验我们这节课的内容,在云上自己购买了K8s集群测试,那等部署完成后,云上的K8s集群你一定要清理干净了,除了通过kubectl delete清除我们部署的应用,还要在云上删除K8s集群和worker节点,否则还会持续产生费用。
|
||||
|
||||
## 总结
|
||||
|
||||
这节课我们学习了如何让本地的Knative应用打破云服务商的锁定,部署上云。因为CNCF的K8s集群的可移植性,我们可以在CNCF的云服务商成员中任意选择。我根据我自己的经验,总结了一份云服务商的对比表格,这个表格的内容对比了我们自身业务的特点,还有价格等因素,让我们自由选择适合自己的云服务商。
|
||||
|
||||
我们在云上创建好K8s集群,使用K8s集群就跟我们本地使用是一样的,而且很多云服务商还提供“一键部署”让我们快速安装K8s组件。最后我们就可以将Knative的应用部署上云了。
|
||||
|
||||
然而我们虽然可以用Knative解决Container Serverless的云服务商锁,但却无法解决云服务商用FaaS构建起的新壁垒。每个云服务商FaaS的Runtime都不一样,我们的函数代码要兼容多个云服务商部署,要写很多额外的代码,处理兼容性的问题。那么下节课我们就再看看如何破解FaaS的新Vendor-lock。
|
||||
|
||||
## 作业
|
||||
|
||||
这节课,建议你创建一个云上的K8s集群,并且将我们上节课的内容部署到云上的K8s集群。感受一下云上的K8s如何打通部署和提供给互联网用户访问。
|
||||
|
||||
期待你的实践总结,欢迎留言与我交流。如果今天的内容让你有所收获,也欢迎你把文章分享给更多的朋友。
|
||||
222
极客时间专栏/Serverless入门课/实战篇/11 | 经验:Serverless开发最佳实践.md
Normal file
222
极客时间专栏/Serverless入门课/实战篇/11 | 经验:Serverless开发最佳实践.md
Normal file
@@ -0,0 +1,222 @@
|
||||
<audio id="audio" title="11 | 经验:Serverless开发最佳实践" controls="" preload="none"><source id="mp3" src="https://static001.geekbang.org/resource/audio/b5/02/b5748378ca4f1036894f1df5c7bf0702.mp3"></audio>
|
||||
|
||||
你好,我是秦粤。上节课,我们了解了利用K8s集群的迁移和扩展能力,可以解决云服务商锁定的问题。我们还横向对比了各大云服务商的特点和优势,纵向梳理了云服务商提供的各种服务能力。最后我们可以看到,利用Knative提供的Container Serverless能力,我们可以任意迁移部署我们的应用架构,选择适合我们的云服务商。
|
||||
|
||||
但同时我们也发现,FaaS相对于Knative,反而是我们的瓶颈,我们无法平滑地迁移FaaS的函数。云服务商大力发展FaaS,其实部分原因也是看中了FaaS新建立起来的Vendor-lock,因此目前各大运营商都在拼FaaS的体验和生态建设。
|
||||
|
||||
那这节课,我们就来看看FaaS是如何解除云服务商锁定的吧。但在正式开始之前呢,我们先得了解一些FaaS的使用场景,以免一些同学误会,要是你的实践是为了Serverless而去Serverless,那就不好了,我们还是应该从技术应用的角度出发。
|
||||
|
||||
## FaaS场景
|
||||
|
||||
我从[[第5课]](https://time.geekbang.org/column/article/229905) 开始就在讲FaaS和BaaS的底层实现原理Container Serverless,是希望通过底层实现原理,帮助你更好地掌握Serverless的思想。但在日常工作中,使用Serverless,只需要借助云服务商提供的Serverless化的FaaS和BaaS服务就可以了。
|
||||
|
||||
就像我上节课中所讲的,FaaS因为可以帮助云服务商利用碎片化的物理机算力,所以它是最便宜的。在日常工作中,我十分鼓励你去使用FaaS。
|
||||
|
||||
那么说到使用FaaS,以我的经验来说,我们可以将FaaS的最佳使用场景分为2种:事件响应和Serverless应用。
|
||||
|
||||
### FaaS事件响应
|
||||
|
||||
FaaS最擅长的还是处理事件响应,所以我们应该先了解清楚,你选择的云服务商的FaaS函数具体支持哪些触发器,例如阿里云的触发器列表[1]、腾讯云的触发器列表[2]。因为这些触发器通常都是云服务商根据自身Serverless的成功案例所制定的下沉方案,也就是说这些触发器都具备完整的使用场景。基于FaaS支持的触发器,设计事件响应函数,我们就可以最大限度地享受FaaS的红利,而且不用担心用法不正确。
|
||||
|
||||
### Serverless应用
|
||||
|
||||
那如果FaaS是作为Serverless应用的使用场景,我们则需要关注大型互联网公司的成功案例所沉淀出来的成熟方案。这样既可以轻松简单地搭建出Serverless应用,还能帮我们节省不少费用。
|
||||
|
||||
因为我们尝试用FaaS去解构自己的整个应用时,如果超出了目前FaaS的适应边界,那么技术挑战还是比较大的。所以我并不推荐你将整个现存的应用完全Serverless化,而是应该去找出适用FaaS事件响应的场景去使用FaaS。
|
||||
|
||||
如果你关注Serverless就不难发现,至少目前它还处在发展阶段,很多解决方案还在摸索之中。大厂具备技术实力,也乐于在拓展Serverless边界的同时发现痛点、解决痛点并占领技术高地。那如果你是一般用户,最好的选择还是持续关注Serverless动向,及时了解哪些方案可以为自己所用。当然,这是Serverless的舒适圈,但技术挑战也意味着机遇,我确实也看到有不少同学因为拓展Serverless的边界而被吸收进入大厂,去参与Serverless共建。
|
||||
|
||||
## FaaS痛点
|
||||
|
||||
以上就是我对FaaS应用场景的一些看法和建议。虽然FaaS最擅长是事件响应,但是如果可以改造Serverless应用,无疑FaaS的附加值会更高。接下来我们就看看FaaS在Serverless应用过程中切实存在的痛点,相信随着课程一路跟下来的同学一定深有体会。
|
||||
|
||||
- 首先,调试困难,我们每次在本地修改和线上运行的函数都无法保持一致,而且线上的报错也很奇怪,常见的错误都是超时、内存不足等等,让我们根本无法用这些信息去定位问题。
|
||||
- 其次,部署麻烦,每次改动都要压缩上传代码包,而且因为每个云服务商的函数Runtime不一致,导致我们也很难兼容多云服务商。
|
||||
- 最后,很多问题客服无法跟进,需要自己到处打听。
|
||||
|
||||
但如果你有仔细阅读文档的话,你就会发现各大云服务商都会提供命令行调试工具,例如阿里云提供的"fun"工具[3]、腾讯云合作的Serverless Framework等等。不过云服务商提供的工具,都是云服务商锁定的。为了破解FaaS的新Vendor-lock,我从目前热门的Serverless社区中,选择了3个Serverless应用框架和1个平台,介绍给你帮助你破解Vendor-lock,同时也能更好地解决痛点。
|
||||
|
||||
## Serverless应用框架
|
||||
|
||||
### Serverless Framework [4]
|
||||
|
||||
Serverless框架作为第一款支持跨云服务商的Serverless应用解决方案,一旦进入首页,我们就可以看到:Serverless X Tencent Cloud。其实最早的Serverless框架官方例子是AWS的,不过从去年开始被腾讯云强势入驻了。正如我上节课中所讲,腾讯云将Serverless作为切入云服务商市场的突破口,正在大力推进Serverless腾讯云的生态建设。
|
||||
|
||||
Serverless框架具备先发优势,而且已经有很多成熟的应用方案,我们可以直接拿来使用。我们可以通过Serverless Component用yaml文件编排整个Serverless应用架构。
|
||||
|
||||
另外Serverless Framework也支持将应用部署到其他Provider:AWS、阿里云、Azure,还有Knative等等,正如以下图示。不过这里有个小问题:就是我们只有在[英文版Serverless框架的官网](https://www.serverless.com/framework/docs/providers/)上才能看到其他云服务商的Provider和文档介绍([中文版Serverless框架官网](https://www.serverless.com/cn)一并给出)。
|
||||
|
||||
<img src="https://static001.geekbang.org/resource/image/8b/19/8b389343945134f7a0782ae1eb2d6919.jpg" alt="">
|
||||
|
||||
Serverless Framework官方本身主要解决跨云服务商Serverless应用的部署运维问题。它利用类似K8s组件Component的概念,让开发者可以自己开发并且共享组件。通过组件来支持多语言的模板,而且Serverless组件的扩展能力还让Serverless框架具备生态优势。
|
||||
|
||||
目前,Serverless的确是前端参与的比较多,因此在Serverless应用框架中,除了Node.js外,其他语言的选择并不多。下面我就再介绍2款Node.js新的Serverless框架。
|
||||
|
||||
### Malagu [5]
|
||||
|
||||
Malagu是基于 TypeScript 的 Serverless First、可扩展和组件化的应用框架。为了提升Serverless的开发体验,只专注Node.js Serverless应用。
|
||||
|
||||
首先,Malagu的理念是支持前后端一体化,基于JSON RPC,前端像调用本地方法一样调用后端方法。前后端支持RPC和MVC两种通信形式,MVC可以满足传统纯后端Rest风格接口的开发。
|
||||
|
||||
其次,Malagu框架本身也是基于组件化实现的,将复杂的大型项目拆解成一个个Malagu组件,可以提高代码的复用能力、降低代码的维护难度。
|
||||
|
||||
最后,命令行工具插件化,默认提供初始化、运行、构建、部署能力,通过插件可以扩展命令行的能力。
|
||||
|
||||
我有幸与Malagu的开发者共事过,Malagu汲取了众家所长,我觉得这是一个值得关注的Serverless框架。而且目前Malagu已经有很多合作伙伴,他们正在积极使用和推广Malagu的解决方案。
|
||||
|
||||
### Midway FaaS [6]
|
||||
|
||||
接着是我今天想给你介绍的重点,就是:Midway Faas和它的工具链"f"。Midway FaaS 是用于构建 Node.js 云函数的 Serverless 框架,可以帮助你在云原生时代大幅降低维护成本,更专注于产品研发。
|
||||
|
||||
Midway FaaS诞生于Node.js老牌框架Midway,我也有幸与Midway的开发者一起共事过。Midway FaaS 是阿里巴巴集团发起的开源项目,由一个专业的 Node.js 架构团队进行维护。目前已大规模应用于阿里集团各 BU 线上业务,稳定承载了数千万的流量。
|
||||
|
||||
Midway FaaS有这样一些特点,也可以说是特色。
|
||||
|
||||
- **跨云厂商:**一份代码可在多个云平台间快速部署,不用担心你的产品会被云厂商所绑定。
|
||||
- **代码复用:**通过框架的依赖注入能力,让每一部分逻辑单元都天然可复用,可以快速方便地组合以生成复杂的应用。
|
||||
- **传统迁移:**通过框架的运行时扩展能力,让Egg.js 、Koa、Express.js等传统应用无缝迁移至各云厂商的云函数。
|
||||
|
||||
我们的“待办任务”Web服务,也将采用这个方案进行重构。
|
||||
|
||||
首先,我们通过命令行全局安装工具链"f"。
|
||||
|
||||
```
|
||||
npm install -g @midwayjs/faas-cli
|
||||
|
||||
```
|
||||
|
||||
然后,拉取我们最新的[lesson11代码分支](https://github.com/pusongyang/todolist-backend/tree/lesson11),注意这个分支跟之前的分支相比改动比较大。因此,我们需要重新安装NPM依赖包。
|
||||
|
||||
```
|
||||
npm install
|
||||
|
||||
```
|
||||
|
||||
前端代码还是在public目录,后端代码在src目录,项目里面的代码逻辑也比较简单,你可以自己看一下。我们需要讲解的是比较核心的配置文件f.yml,这里有我放的f.yml的文件结构。
|
||||
|
||||
```
|
||||
service: fc-qinyue-test
|
||||
|
||||
provider:
|
||||
name: aliyun
|
||||
runtime: nodejs10
|
||||
|
||||
functions:
|
||||
index:
|
||||
handler: index.handler
|
||||
events:
|
||||
- http:
|
||||
path: /*
|
||||
|
||||
getUser:
|
||||
handler: user.get
|
||||
events:
|
||||
- http:
|
||||
path: /user/234534
|
||||
method: get
|
||||
|
||||
package:
|
||||
include:
|
||||
- public/*
|
||||
|
||||
aggregation:
|
||||
agg-demo-all:
|
||||
deployOrigin: false
|
||||
functions:
|
||||
- index
|
||||
- getUser
|
||||
|
||||
custom:
|
||||
customDomain:
|
||||
domainName: lesson11-ali.jike-serverless.online
|
||||
|
||||
|
||||
```
|
||||
|
||||
这里我们主要关注的是Provider,目前Midway FaaS支持阿里云和腾讯云,后续还会陆续增加其他云服务商。我们切换云服务商只需要修改Provider就可以了。其次,你也可以看到Functions部分,我们定义了所有的入口函数和路径path的映射关系。最后我们在部署环节,自己指定了域名。我们在配置域名解析时,根据云服务商提供的方式,通常都是修改域名的CNAME,指向云服务提供的FaaS地址就可以了。
|
||||
|
||||
Midway FaaS的强大之处就在于我们可以借助它提供的工具链"f",根据我们的f.yml文件配置内容,从而直接在本地模拟云上事件触发,调试我们的应用。
|
||||
|
||||
这里你只需要执行:
|
||||
|
||||
```
|
||||
f invoke -p
|
||||
|
||||
```
|
||||
|
||||
我们就可以通过浏览器访问:[http://127.0.0.1:3000](http://127.0.0.1:3000) 调试我们的FaaS函数代码了。并且因为执行就在我们本地,所以调试时,我们可以借助现代IDE提供的Node.js debug能力逐行调试。而我们部署时也只需要一行代码:
|
||||
|
||||
```
|
||||
f deploy
|
||||
|
||||
```
|
||||
|
||||
然后Midway FaaS就会根据我们的f.yml文件配置内容,帮我们部署到云上。
|
||||
|
||||
下面这2个URL地址,就是我用Midway FaaS分别部署在阿里云和腾讯云上的“待办任务”Web服务。
|
||||
|
||||
阿里云“待办任务”Web服务:[http://lesson11-ali.jike-serverless.online/](http://lesson11-ali.jike-serverless.online/)
|
||||
|
||||
腾讯云“待办任务”Web服务:[http://lesson11-tx.jike-serverless.online/](http://lesson11-tx.jike-serverless.online/)
|
||||
|
||||
这里我需要提醒你一下,这2个云服务都是以阿里云的OTS服务作为数据库存储。这也是FaaS服务编排的强大之处,现在有些云服务商提供的BaaS服务是支持其他云服务商通过ak/sk访问的。另外如下图所示,如果我们采用ak/sk编排云服务商的HTTP API服务,那么利用FaaS就可以实现混合云编排服务了。
|
||||
|
||||
<img src="https://static001.geekbang.org/resource/image/62/37/623069c983b6359bd5418ee1f35cc337.jpg" alt="">
|
||||
|
||||
如果你留意过GitHub,你会发现Midway FaaS和Malagu都是非常新的GitHub仓库。Serverless作为一门新兴技术,还有非常大的想象空间。
|
||||
|
||||
另外阿里云也推出了自己的云开发平台,而且将Serverless应用的生态集成了进去。目前Midway FaaS和Malagu也都在其中作为应用创建模板供你使用。
|
||||
|
||||
### 阿里云开发平台 [7]
|
||||
|
||||
阿里云开发平台就是由咱们专栏特别放送中的被采访者,杜欢(笔名风驰)负责的。
|
||||
|
||||
他的核心理念呢,就是将大厂成熟的Serverless应用场景抽象成解决方案,并且透出到云开发平台上来,为各个中小企业和个人开发者赋能。
|
||||
|
||||
正如下图所示,云开发平台是将大厂的行业应用场景沉淀成了解决方案,解析生成Serverless架构,这样我们就可以把云开发平台上实例化的“解决方案”快速应用到我们的Serverless应用中来。基于成熟Serverless的应用,“反序列化”快速沉淀为解决方案,这也是云服务平台会收录Midway FaaS和Malagu的原因。
|
||||
|
||||
而且我相信以后还会有更多的Serverless应用解决方案被收录到云开发平台中的。另外阿里云开发平台并不限制开发语言,但截止到咱们这节课的发布时间,阿里云开发平台的模板仅支持Node.js和Java的Serverless应用,其它语言还需要等成功案例沉淀。
|
||||
|
||||
<img src="https://static001.geekbang.org/resource/image/bd/06/bd7cfa2c4711ad7c0533d5a198323306.jpg" alt="">
|
||||
|
||||
## 总结
|
||||
|
||||
这节课我向你介绍了如何打破FaaS的Vendor-lock,并且介绍了我经验中FaaS的最佳使用场景:事件响应和Serverless应用。事件响应无疑就是FaaS的最佳使用场景;而Serverless应用则需要我们在云服务商提供的FaaS服务上,利用我们专栏前面学习到的知识和原理,挑战将完整的应用完全Serverless化。
|
||||
|
||||
为了解决FaaS的痛点和云服务商锁定,我向你介绍了3个框架和一个平台。
|
||||
|
||||
- 目前由腾讯云主导的Serverless Framework,它支持多语言,具备组件扩展性。目前仅在英文版官网可以看到其支持的云服务商Provider文档。
|
||||
- 由阿里云FC团队主导的Malagu解决方案,目前仅支持Node.js,同样具备扩展性。目前正在积极的和很多开发团队合作共建Serverless应用生态。
|
||||
- 由阿里巴巴Node.js框架Midway团队主导的Midway FaaS解决方案,它仅支持Node.js,扩展性由Midway团队官方保障。Midway团队的社区响应迅速,很多GitHub上的issue会在第一时间响应。
|
||||
- 由阿里云主导的阿里云开发平台,它支持多语言,具备模板扩展性,扩展性由官方提供的模板保障。目前收录了Serverless生态比较成熟的方案,可以快速部署到阿里云FC上。虽然开发平台有一定的云服务商锁定嫌疑,但其采用的模板却可以支持解除云服务商锁定,例如Midway FaaS。
|
||||
|
||||
作为本专栏的最后一课,我还想再啰嗦一句。Serverless确实是一门新兴技术,其未来无限可能,接下来该由你探索了!
|
||||
|
||||
>
|
||||
<p>想要我的财宝吗?想要的话就给你,去找出来吧,这世上所有的一切都放在那里。<br>
|
||||
世界吗?没错!去追求自由。<br>
|
||||
去超越吧!在信念的旗帜的带领下。</p>
|
||||
—— 海贼王 罗杰
|
||||
|
||||
|
||||
## 作业
|
||||
|
||||
Serverless技术实践还是很重要的,最后一节课的作业我们就继续实操。请你将这节课“待办任务”Web服务的Midway FaaS版本,在本地调试运行起来,并且通过 `f deploy` 把它部署到阿里云或者腾讯云上。
|
||||
|
||||
期待你的成果,有问题欢迎随时留言与我交流。如果今天的内容让你有所收获,也欢迎你把文章分享给更多的朋友。
|
||||
|
||||
## 参考资料
|
||||
|
||||
[1] [https://help.aliyun.com/document_detail/74707.html](https://help.aliyun.com/document_detail/74707.html?)
|
||||
|
||||
[2] [https://cloud.tencent.com/document/product/583/31927](https://cloud.tencent.com/document/product/583/31927)
|
||||
|
||||
[3] [https://help.aliyun.com/document_detail/140283.html](https://help.aliyun.com/document_detail/140283.html?)
|
||||
|
||||
[4] [https://www.serverless.com/cn/framework/docs/getting-started/](https://www.serverless.com/cn/framework/docs/getting-started/)
|
||||
|
||||
[5] [malagu框架项目地址](https://github.com/alibaba/malagu);[malagu框架详细文档](https://www.yuque.com/cellbang/malagu)
|
||||
|
||||
[6] [https://github.com/midwayjs/midway-faas](https://github.com/midwayjs/midway-faas)
|
||||
|
||||
[7] [https://workbench.aliyun.com/](https://workbench.aliyun.com/)
|
||||
61
极客时间专栏/Serverless入门课/开篇词/开篇词 | 说来说去,到底Serverless要解决什么问题?.md
Normal file
61
极客时间专栏/Serverless入门课/开篇词/开篇词 | 说来说去,到底Serverless要解决什么问题?.md
Normal file
@@ -0,0 +1,61 @@
|
||||
<audio id="audio" title="开篇词 | 说来说去,到底Serverless要解决什么问题?" controls="" preload="none"><source id="mp3" src="https://static001.geekbang.org/resource/audio/f5/6e/f52633b211fe2715f15034ee0646106e.mp3"></audio>
|
||||
|
||||
你好,我是秦粤,欢迎你和我一起学习Serverless。说起Serverless这个词啊,我估计你应该不陌生,就算你没详细了解过,但我确定你肯定在过去几年时间里听别人说过。去年我在参加[GMTC全球大前端技术大会](https://gmtc.infoq.cn/2019/shenzhen/track/675)的时候,也惊讶地发现国内几个大公司都已经有成熟的应用案例了,所以我当时就感叹说:“Serverless终于要飞入寻常百姓家了。”
|
||||
|
||||
### 三个问题
|
||||
|
||||
作为一名Serverless的拥趸者,过去几年时间里,我总是喜欢向朋友和同事推荐Serverless技术。不过,在“推销”的过程中,他们经常会问我一些问题,那在今天的开篇词里,我就来统一回答下这些共性的问题吧,我估计你也会问到。
|
||||
|
||||
**问题一:说来说去,到底Serverless要解决什么问题?**
|
||||
|
||||
我不知道你有没有算过你们公司每年在服务器上的开销,反正在创业之前我是没算过,总觉得这钱算了也没必要,还是应该多花心思在怎么挣钱上,但后面当我真正自己开公司之后,才知道柴米贵。
|
||||
|
||||
咱们就拿自己部署一套博客来说吧,常见的Node.js MVC架构,需要购买云服务商的Linux虚拟机、RDS关系型数据库,做得好的话还要购买Redis缓存、负载均衡、CDN等等,再专业一点,可能还会考虑容灾和备份,这么算下来一年最小开销都在1万元左右。但如果你用Serverless的话,这个成本可以直接降到1000元以下。
|
||||
|
||||
Serverless是对运维体系的极端抽象,就像iPhone当年颠覆诺基亚一样,它给应用开发和部署提供了一个极简模型。这种高度抽象的模型,可以让一个零运维经验的人,几分钟就部署一个Web应用上线,并对外提供服务。也就是说,你根本不需要再学习怎么在Linux上装Web服务器,怎么配置负载均衡等等这些繁琐的偏运维方向的工作。
|
||||
|
||||
所以,你要问我Serverless解决了什么问题,一句话总结就是它可以帮你省钱、省力气。
|
||||
|
||||
**问题二:为什么阿里巴巴、腾讯这样的公司都在关注Serverless?**
|
||||
|
||||
首先,Serverless可以有效降低企业中中长尾应用的运营成本。中长尾应用就是那些每天大部分时间都没有流量或者有很少流量的应用,你可以想想你们公司是不是也有很多。这一点我特别有感触,尤其是企业在落地微服务架构后,一些边缘的微服务被调用的概率其实很低。而这个时候,我们往往又很难通过人工来控制中长尾应用,因为这里面不少应用还是被强依赖的,不可以直接下线处理。Serverless之前,这些中长尾应用至少要独占1台虚拟机;现在有了Serverless的极速冷启动特性,企业就可以节省这部分开销。
|
||||
|
||||
其次,Serverless可以提高研发效能。我们专栏会讲到Serverless应用架构的设计,其中,SFF(Serverless For Frontend)可以让前端同学自行负责数据接口的编排,微服务BaaS化则让我们的后端同学更加关注领域设计。可以说,这是一个颠覆性的变化,它能够进一步放大前端工程师的价值。
|
||||
|
||||
最后我想说Serverless作为一门新兴技术,未来的想象空间很大。我看到有创业公司用FaaS来做基础设施编排和云服务编排;也有外包公司利用Serverless应用架构的快速迭代能力,提升开发效率,降低出错率,还可以给自己沉淀领域的解决方案;还有包括风险投资方也在逐渐开始关注Serverless领域,毕竟这也是一个新的风口。我讲大企业的使用方式只是希望给你一些灵感,不想过多限制你的想象,Serverless的疆域边界还在等你去扩展。
|
||||
|
||||
这里是GMTC会议上几个大公司的分享资料,你感兴趣的话,可以先看看。
|
||||
|
||||
- [阿里跨境供应链前端架构演进与 Serverless 实践](https://static001.geekbang.org/con/55/pdf/1710853715/file/%E7%BC%AA%E4%BC%A0%E6%9D%B0.pdf)
|
||||
- [Serverless 前端工程化落地与实践](https://static001.geekbang.org/con/55/pdf/3151321591/file/%E7%8E%8B%E4%BF%8A%E6%9D%B0%20%20Serverless%20%E5%89%8D%E7%AB%AF%E5%B7%A5%E7%A8%8B%E5%8C%96%E8%90%BD%E5%9C%B0%E4%B8%8E%E5%AE%9E%E8%B7%B5.pdf)
|
||||
- [从前端和云厂商的视角看 Serverless 与未来的开发生态](https://static001.geekbang.org/con/55/pdf/1359804153/file/%E6%9D%9C%E6%AC%A2%20%20%E4%BB%8E%E5%89%8D%E7%AB%AF%E5%92%8C%E4%BA%91%E5%8E%82%E5%95%86%E7%9A%84%E8%A7%86%E8%A7%92%E7%9C%8B%20Serverless%20%E4%B8%8E%E6%9C%AA%E6%9D%A5%E7%9A%84%E5%BC%80%E5%8F%91%E7%94%9F%E6%80%81.pdf)
|
||||
|
||||
**问题三:Serverless对前端工程师来说会有什么机遇?为什么我们要学习Serverless?**
|
||||
|
||||
相对其他工种而言,Serverless给前端工程师带来的机遇更大,它能让前端工程师也享受到云服务的红利。如果说Node.js语言的出现解放了一波前端工程师的生产力,那Node.js+Serverless又可以进一步激发前端工程师的创造力。口说无凭,到时候你看完咱们专栏里的例子就能体会到这句话的意思了。
|
||||
|
||||
另外,我觉得学习Serverless是成为云开发者的最适合的切入点。无论你是零基础,还是资深服务端运维,都可以从Serverless上学习到现代服务端运维体系的重要思想(我在第一节课就会讲)。
|
||||
|
||||
回答完这几个问题,我想你对Serverless已经有了初步了解。接下来我也不想绕弯子,我就再和你聊聊我准备怎么给你讲这门课吧。
|
||||
|
||||
### 课程设计
|
||||
|
||||
基础篇,我会继续带你理解Serverless要解决什么问题,以及Serverless的边界和定义。搞清楚了来龙去脉,我们会进入动手环节,我会通过一个例子来给你讲解Serverless引擎盖下的工作原理,以及FaaS的一些应用场景。
|
||||
|
||||
进阶篇,我们将一起学习FaaS的后端解决方案BaaS,以及我们自己现有的后端应用如何BaaS化。为了更好地展现Serverless的发展历程和背后的思考,我也为你准备了一个基于Node.js的待办任务的Web应用,你要做好准备,这里我会给你布置很多动手作业。
|
||||
|
||||
GitHub地址:[https://github.com/pusongyang/todolist-backend](https://github.com/pusongyang/todolist-backend)
|
||||
|
||||
实战篇,我会通过Google开源的Kubernetes向你演示本地化Serverless环境如何搭建,并根据我的经验,和你聊聊Serverless架构应该如何选型,以及目前Serverless开发的最佳实践。
|
||||
|
||||
<img src="https://static001.geekbang.org/resource/image/8b/83/8bde1e4a6ae3adb4f5ab5f410a9b1e83.jpg" alt="" title="学习路径图">
|
||||
|
||||
最后,我再来介绍下我自己吧。我叫蒲松洋,秦粤是我的花名。2006年从电子科技大学毕业后,我就进入了UT斯达康(现在这公司已经谢幕,它是小灵通的主要生产厂商)做通讯相关的工作,当时的职位是PHP前端工程师。2013年,我跳槽加入百度,从PHP前端工程师转为了Node.js前端工程师。2015年开始又和朋友折腾创业,用Node.js做智能家居IoT。2016年底,创业没成,我又回到了国内某一线互联网公司,负责Node.js应用治理和Node.js微服务架构设计。
|
||||
|
||||
我在用Node.js做微服务时发现,微服务本身提出了很多理念,但微服务在服务端运维却缺少给力的支撑平台,后来我们就试着用容器集群搭建了自己的Container Serverless。结果证明它不但可以支撑Node.js微服务运维,还可以提高Node.js中长尾应用的资源利用率。再后面,大家都意识到了Serverless带来的便利性,于是我们团队也就整体参与到了公司Serverless整体建设中了。
|
||||
|
||||
在研究并落地Serverless技术的过程中,我发现国外的Serverless开源社区其实比国内更加活跃。自从2014年AWS推出第一个FaaS服务Lambda后,国外的很多公司都在积极推进Serverless的生态发展,并且开始占领高地,制定了很多的规范。而放眼国内,目前还只有为数不多的大型互联网公司在重点跟进,其他人基本上只是在观望或者看热闹。
|
||||
|
||||
因此,我也特别希望通过这个专栏能够带你真正理解Serverless,并让你尽快享受到技术的红利。Serverless 肯定是未来云计算发展的重点方向,作为工程师,特别是前端工程师,我们应该思考的是如何抓住这波机遇,如何利用FaaS+各种创意,组合碰撞出各种化学反应,去为公司、为自己创造更大的价值。
|
||||
|
||||
以上就是今天的全部内容。有关Serverless,不知道你的看法是怎样的?如果你有什么疑惑,或者在实践中遇到了哪些困难?欢迎在留言区中提出,我们共同探讨。
|
||||
47
极客时间专栏/Serverless入门课/开篇词/特别放送 | 为什么阿里要举集团之力趟坑Serverless?.md
Normal file
47
极客时间专栏/Serverless入门课/开篇词/特别放送 | 为什么阿里要举集团之力趟坑Serverless?.md
Normal file
@@ -0,0 +1,47 @@
|
||||
<audio id="audio" title="特别放送 | 为什么阿里要举集团之力趟坑Serverless?" controls="" preload="none"><source id="mp3" src="https://static001.geekbang.org/resource/audio/d2/ee/d2ecde527136a457ee99cdb9b53c6aee.mp3"></audio>
|
||||
|
||||
你好,我是专栏编辑冬青。在课程正式开始之前,我要先向你推荐一篇InfoQ记者蔡芳芳的文章。为了探究Serverless的价值、生态位以及局限之处,蔡芳芳采访了阿里高级前端技术专家杜欢,并表达了自己的看法。以下为具体内容。
|
||||
|
||||
作为阿里巴巴经济体前端 Serverless 研发升级项目的负责人,杜欢过去两年花了大量时间推进集团内部的 Serverless 研发模式升级工作。这是一项牵涉整个阿里集团层面的技术升级工作。阿里 2018 年正式启动内部 Serverless 资源底座的准备工作,2019 年基于搭建好的底座建设上层前端框架,到 2019 年双十一,阿里已经在部分电商导购业务上开始实践这套新的研发模式。
|
||||
|
||||
Serverless 兴起于 2017 年,在最近两年伴随着云原生概念的推广愈发火爆。在这波 Serverless 浪潮里,阿里是国内走得最前面、说得也最多的一个。但为什么阿里会上升到整个集团的高度来推进 Serverless 研发模式升级,我们仍然感到好奇。对此,杜欢表示:“ 这件事本身好像是一件技术的事情,但其实它背后就是钱的事情,都是跟钱相关的。”
|
||||
|
||||
杜欢告诉 InfoQ 记者,为了保障业务的稳定性和可用性,阿里对每一个应用上线都有相应的规范和规则。哪怕是一个很小的内部应用,一天可能只有一两个访问量,上线也需要遵守既有的规范,这势必会消耗一些固定资源。单个应用消耗的资源可能很有限,但所有应用消耗的资源累积起来也是一个不小的数字。阿里内部自己做了分析发现,除了主要的核心应用之外,已经上线的应用中超过 80% 都是非核心的中长尾应用。目前阿里经济体的体量和业务量已经达到非常大的量级,在现有的研发模式下,这些中长尾应用会带来比较大的资源浪费。
|
||||
|
||||
其次,现有的研发形态并不能最大化地发挥部分工作岗位的价值,这同样是一种浪费。以导购类型的业务为例,开发这样一个业务通常需要前端开发工程师和后端开发工程师一起配合,但这两个开发岗位在该业务形态下并不能很好地发挥自己的全部价值。对于后端工程师来说,他在这个业务里要做的事情更多只是把现有的一些服务能力、数据组合在一起,变成一个新的数据提供给前端;而前端开发工程师负责把这个数据在页面上展示出来。这样的工作比较机械化,也没有太大的挑战,并不利于后端工程师的个人成长和岗位价值发挥;但在现有的研发模式下,由于缺乏前后端的连接点,前端工程师又不能去做这些比较简单的后端工作,业务上线也不可能给到前端工程师时间和机会去学习再实践。
|
||||
|
||||
而 Serverless 既可以满足资源最大化利用的需求,也能够调优行业内的开发岗位分层结构,让每个开发岗位都能够在最适合自己的地方发挥最大的价值。杜欢表示,阿里正是因为看到了经济体内存在的上述问题,在尝试寻找相应解决方案的过程中发现 Serverless 可能是一个好的解决方案,才开始研究 Serverless,研究后认为确实可行,才启动了“阿里巴巴经济体前端 Serverless 研发升级项目”这样一个项目,拉上大家一起来共建。
|
||||
|
||||
那么 Serverless 是只对阿里这样的公司才适用吗?什么样的公司、应用或场景应该选用 Serverless 的架构模式?
|
||||
|
||||
在杜欢看来,这是一个“伪问题”。他直言,并不存在什么样的场景和模式适合 Serverless,Serverless 应该被广泛地运用在不同的场景和实际开发需求中。从云厂商的角度来看,云计算未来一定会成为整个社会和商业的基础设施,届时使用云计算就应该像现在我们使用水电煤一样简单,不需要了解水从哪里来、怎么过滤、怎么铺设管道等一系列问题,只需要打开水龙头接一杯水而已。
|
||||
|
||||
而 Serverless 的概念正好可以帮助云计算朝这个方向往前走一步,它提倡的是人们不需要关心应用逻辑以外的服务相关的事情,包括管理、配置、运维等,用多少就付多少。从这个角度来看,Serverless 是真正让云计算变成社会商业基础设施的一个实现路径,也更接近现在业内提倡的云原生的方式,因此人们在使用云计算的过程中自然就应该按照 Serverless 的方式来使用。
|
||||
|
||||
杜欢认为,大家今天对是不是该用 Serverless 还有疑问,主要是因为还没有看到足够多 Serverless 成功应用的案例。这也是阿里巴巴先从自己内部实践 Serverless 研发模式升级的另外一个原因,希望通过这件事达成两个目的,一是向大家普及 Serverless 的概念,二是从自己的实践过程中总结出一套好的实践方式并共享出来,帮助大家更好地了解应该怎么落地 Serverless。
|
||||
|
||||
### 前端开发者看好、后端开发者观望,Serverless 为何如此?
|
||||
|
||||
过去这一年 Serverless 在前端领域被频繁提及,很多开发者非常看好,并认为它一定是未来前端大趋势之一,相比之下非前端领域还是观望态度居多。杜欢告诉记者,之所以会出现热度差,是因为 Serverless 的概念天然弥补了前端开发工程师的不足,但却跟现在很多后端开发工程师的能力有一定程度的重叠。
|
||||
|
||||
在杜欢看来,绝大多数后端开发工程师的成长路径是先做后端业务逻辑,慢慢了解应用的每个环节,再逐步成长为后端架构师,能 Hold 住整个应用架构。但云原生 Serverless 的出现,一定程度上会使这种后端架构能力变得普及,并且是以平台或服务的方式,而不是以人的方式。这乍看起来对于整个后端岗位可能是一种冲击,但杜欢认为 Serverless 并不会完全取代后端。当前整个开发生态依然缺少优秀的架构服务,尤其是低成本、可持续发展、针对特定行业优化的架构服务。未来后端工程师还是会成长为架构师,但是可能不是通用架构师,而是偏业务解决方案的行业架构师。此外,云厂商也需要专门构建 Serverless 方案的架构师,这是后端工程师的另一个新机会。
|
||||
|
||||
杜欢告诉 InfoQ 记者,Serverless 对前端和后端带来的影响总体都是正向的。对于前端来说,Serverless 不仅补足了前端工程师现有的能力,还可能使整个前端行业的定位发生变化。原来经常有人会认为前端的工作很简单,面向 UI 做好开发就行,剩下的工作可以交给后端。但是云端的应用开发模型出来之后,也就是前端和 Serverless 结合之后,大家对前端的诉求就不仅仅是开发一个页面了,而是要能交付整个应用的开发。
|
||||
|
||||
前端工程师除了要保持在 UI、交互逻辑方面的优势,还要理解整个业务和业务背后的意图,这意味着未来前端行业的思考模式会变成面向业务的思考模式。与此同时,前端的协同和开发模式、上下游流程也会发生变化,原来前端可能很少跟产品经理、设计打交道,未来前端要对整个应用负责,就需要天天跟产品经理、设计打交道。后端则要在最底层提供更深的能力付出,比如如何按照一亿流量的支出支撑十亿流量,这是更大的挑战。
|
||||
|
||||
### 目前 Serverless 最佳实践模式尚未出现
|
||||
|
||||
当前谈 Serverless,很多人会提函数计算、FaaS,但杜欢认为当前 Serverless 尚未出现一个最佳实践模式。在他看来,现在已经有一些 Serverless 的框架开始涌现,云厂商对 Serverless 的支持也越来越完善,确实是时候去尝试实践 Serverless 了。但要说最佳实践,就必须有足够多的人去实践过并表示认同,而现在实践 Serverless 的人本来就很少、实现的业务也很少,因此还没有哪一种方式谈得上是最佳实践。
|
||||
|
||||
不过杜欢提到 Serverless 实践过程中有一点需要重点关注——学习曲线是否足够平缓。要成为最佳实践模式,至少要做到能让开发者以一种方式专注于业务代码的开发,无需关注运行平台的差异性,一处编写可以处处运行,开发者只要掌握一种方式就可以在不同业务之间没有学习成本地切换。阿里巴巴近期开源的函数运行时框架 Midway FaaS Run Time 就是朝着这样的目标设计出来的,但还需要更多人尝试并认可,才有可能在未来变成最佳实践。
|
||||
|
||||
杜欢表示,目前 Serverless 在国内的发展和采用依然处于初期阶段,经过这两年的概念普及,大部分人都已经注意到并接受了 Serverless,但业务实践偏少,仍在不断探索之中。相比之下,国外整体要领先 1-2 年,国外几个大的云厂商前期对整个研发生态的教育和布道做得比较多,应用也比较早。现在国外已经出现不少 Serverless 框架,比较知名包括 Serverless.com 和 Zeit.com。
|
||||
|
||||
但对于 Serverless 未来的发展,杜欢信心十足。在他看来,未来云计算的普惠,一定是通过 Serverless 的方式去放大和落地实现的,而基于云端的应用模式也一定会是未来创新创业的选择。问题在于如何做好从传统开发模式到云开发模式的迁移。其中非技术层面的挑战主要来自于开发者,前后端工程师对 Serverless 的态度可能不一致,有的后端工程师会觉得 Serverless 抢了自己的工作很难接受,他没有看到更深层次对自己有收益的地方;有的前端工程师认为 Serverless 只是增加了自己要做的事情,而不能看到这个东西对自己提出了更高的要求,那他未来也未必能够胜任这项工作。
|
||||
|
||||
而技术层面的挑战主要包括两块:首先,云厂商自身要提供更多的 Serverless 能力,或者说现有云计算的能力要有更多被转换成能以 Serverless 的方式提供服务,未来云厂商要越来越多地提供这方面的支持;其次是研发模式,未来对于云时代的原住民来说,所有东西都在云上,开发方式必然会发生变化,如何去解决这些问题,让一个新人加入一项新业务之后可以更快地写下第一行代码,这是另一项挑战。
|
||||
|
||||
展望 2020 年 Serverless 的发展趋势,杜欢说道:“2020 年 Serverless 会进入初步实践阶段,还不能称之为大规模实践,可能到 2021 年才会进入大规模实践阶段。在这个过程当中,云厂商会进一步补充更多的 Serverless 服务,包括一些后端的 BaaS 服务,把基础打得更牢一点。”
|
||||
|
||||
今天的分享就到这里,下节课我们正式开始学习!
|
||||
@@ -0,0 +1,8 @@
|
||||
|
||||
你好,我是蒲松洋。
|
||||
|
||||
到今天为止《Serverless入门课》这个专栏就结课了,很感谢大家的支持,也非常开心能与各位交流技术。为认真学习的你点赞!
|
||||
|
||||
为了让你更好地检测自己的学习成果,我特意做了一套期末测试题。题目共有20道,单选+多选,满分为100分,快来挑战一下吧!
|
||||
|
||||
[<img src="https://static001.geekbang.org/resource/image/28/a4/28d1be62669b4f3cc01c36466bf811a4.png" alt="">](http://time.geekbang.org/quiz/intro?act_id=145&exam_id=318)
|
||||
107
极客时间专栏/Serverless入门课/结束语/结束语 | 带你整体回顾我们的Serverless案例.md
Normal file
107
极客时间专栏/Serverless入门课/结束语/结束语 | 带你整体回顾我们的Serverless案例.md
Normal file
@@ -0,0 +1,107 @@
|
||||
<audio id="audio" title="结束语 | 带你整体回顾我们的Serverless案例" controls="" preload="none"><source id="mp3" src="https://static001.geekbang.org/resource/audio/15/25/15af71e6a0f875c66dfe3acaef8edb25.mp3"></audio>
|
||||
|
||||
你好,我是秦粤。在经过了11节课的学习后,相信此刻,你对Serverless一定有了一些新的认识。那到了尾声,今天这节课我们就结合“待办任务”Web服务的演进过程,带你整体回顾一下本专栏的内容,希望能对你自身沉淀知识有所助益。
|
||||
|
||||
一路认真学习并动手实践课后作业的同学其实很容易发现,这个专栏并不是教大家写代码的,而是一堂服务端技术架构课。我们的实践内容和作业,主要也是让你通过部署项目代码体验一下运维的工作,更深刻地理解**“Serverless是对服务端运维的极端抽象”**这句话。
|
||||
|
||||
下面我们就分几个阶段去回顾“待办任务”Web服务这个大案例。
|
||||
|
||||
## “待办任务”Web服务
|
||||
|
||||
我们的代码都在[GitHub](https://github.com/pusongyang/todolist-backend)上,我建议你一定要跟着我的节奏run一下。
|
||||
|
||||
## All-in-one
|
||||
|
||||
第一个版本[master分支](https://github.com/pusongyang/todolist-backend/tree/master),以下是这个版本的示意图。
|
||||
|
||||
<img src="https://static001.geekbang.org/resource/image/27/2d/2780a9325c2f6622f1df2f5beb5e0d2d.png" alt="">
|
||||
|
||||
你可以看到这个master分支的版本,采用的是Express.js框架,这是一个典型的MVC架构。而且所有的请求,无论index.html、数据API请求,还是静态资源,都放在了一个文件index.js中处理。
|
||||
|
||||
这里我特意给出了2个文件:index.js和index-faas.js。index.js是用于本地开发和调试的,而index-faas.js是用于部署到阿里云函数服务的。我们可以对比一下,其实不难发现这2个文件只有细微的差别。
|
||||
|
||||
<img src="https://static001.geekbang.org/resource/image/d5/bc/d5688867c9df2f654d9b88726a59bdbc.png" alt="">
|
||||
|
||||
index.js因为我们在本地调试,所以它需要在我们本地IP上监听一个端口号:3001。通过端口号将用户的HTTP请求转入到我们上面注册的Express函数中。
|
||||
|
||||
index-faas.js因为部署在云上的函数服务中,它是通过事件触发的,因此它从函数服务提供的Runtime中获取了fc-express库的Server对象,这个Server对象其实是一个适配器,它将函数服务HTTP事件对象适配成Express的request、response和context对象,这样我们的其他代码就可以复用index.js了。
|
||||
|
||||
“待办任务”Web服务,第二个版本是[lesson04-homework分支](https://github.com/pusongyang/todolist-backend/tree/lesson04-homework),以下是这个版本的示意图。
|
||||
|
||||
<img src="https://static001.geekbang.org/resource/image/28/ea/28a16351251300466abb9a85373833ea.jpg" alt="">
|
||||
|
||||
这个版本也是index.js接收所有的请求,不同于上一个版本的是,待办任务的数据我们存储在了lowdb仓库中。我们对比一下index.js和index-faas.js。
|
||||
|
||||
<img src="https://static001.geekbang.org/resource/image/78/a0/788a8c76e377d0e8d89a9b52bdf243a0.png" alt="">
|
||||
|
||||
可以看出我们在本地能利用机器的硬盘持久化Todos数据,但是在函数服务上却不行,就像我们专栏中介绍的FaaS实例要求我们无状态Stateless,因为我们的函数实例每次启动都是一个全新的机器,然后加载我们的代码。你如果尝试在函数服务上使用index.js的写文件的方式,将数据放入db.json,你就会发现我们的函数实例的机器硬盘是只读的。我需要指明一下,即使我们可以读写/tmp目录,但每次启动的函数实例都是无法保存状态的。
|
||||
|
||||
“待办任务”Web服务,第三个版本是[lesson05分支](https://github.com/pusongyang/todolist-backend/tree/lesson05),以下是这个版本的示意图。
|
||||
|
||||
<img src="https://static001.geekbang.org/resource/image/06/34/06ad7192b848202ff40d87a806addd34.jpg" alt="">
|
||||
|
||||
为了解决前面lesson04中的函数服务的数据持久化问题,我们引入了消息队列。当然我实际代码中是采用了表格存储OTS,而且我们将数据库操作的rule抽离出来,放在了一个独立的文件rule-faas中,rule-faas提供RESTful的HTTP API给客户端访问。这样做是为了index-faas和rule-faas独立部署,避免它们的逻辑耦合在一起,当触发扩缩容时造成资源浪费。例如用户的前端资源请求量大时,也会触发实例扩容,如果rule-faas没有独立部署,就会导致额外的数据库对象创建。
|
||||
|
||||
这种All in one的做法最简单直接,所有的内容都放在一起,部署和调试都很方便。而且我们本地开发和云端的差异很小,方便我们去验证、测试函数服务的一些功能。
|
||||
|
||||
## 静态资源分离
|
||||
|
||||
“待办任务”Web服务,第四个版本是[lesson06分支](https://github.com/pusongyang/todolist-backend/tree/lesson06),以下是这个版本的示意图。
|
||||
|
||||
<img src="https://static001.geekbang.org/resource/image/bd/c1/bd4ec8888be3e7c7ebcc2576943910c1.jpg" alt="">
|
||||
|
||||
这个版本和上个版本相比,最大的改变是,我们将静态资源从public中移出,部署到CDN上了。另外为了增加安全性,避免我们的rule服务直接被HTTP请求篡改,我们引入了微服务概念中的JWT。用户需要先登录(/api/currentUser),Cookie获取到JWT,才能顺利通过rule.js的JWT token校验。
|
||||
|
||||
## Docker容器
|
||||
|
||||
“待办任务”Web服务,第五个版本是[lesson07分支](https://github.com/pusongyang/todolist-backend/tree/lesson07),以下是这个版本的示意图。
|
||||
|
||||
<img src="https://static001.geekbang.org/resource/image/9b/c7/9ba6567c2542c5bdecc128e22c4dcfc7.jpg" alt="">
|
||||
|
||||
这个版本和上一个版本相比,区别就是将所有的内容都放回到index.js,并且运行在容器中了。然后我们在代码中引入Dockerfile,构建我们的第一个容器镜像。这里我啰嗦一下,为了简化我们这节课的体验,我将rule.js合并到index.js中了。这样做其实也是动态化网络的思想,我们没必要为了划分微服务而去划分微服务。在实际工作中,合并、拆解节点应该是常见的操作,具体根据我们的业务需求来就行。
|
||||
|
||||
## Kubernetes
|
||||
|
||||
“待办任务”Web服务,第六个版本是[lesson08分支](https://github.com/pusongyang/todolist-backend/tree/lesson08),以下是这个版本的示意图。
|
||||
|
||||
<img src="https://static001.geekbang.org/resource/image/77/7b/7712e04162e0dd3aeb90aee8cacd427b.jpg" alt="">
|
||||
|
||||
为了更好地管理Docker容器,我们这个版本引入了K8s。通过上面的示意图我们可以看出,K8s通过策略配置或指令将我们的“待办任务”Web服务的生命周期管理了起来。我们应用开发者除了关心Dockerfile,不用再关心我们的容器重启、奔溃、扩缩容等等问题了,但我们应用在K8s集群的运维状态,还是需要运维人员手动维护的。
|
||||
|
||||
## Knative
|
||||
|
||||
“待办任务”Web服务,第七个版本是[lesson09分支](https://github.com/pusongyang/todolist-backend/tree/lesson09),以下是这个版本的示意图。
|
||||
|
||||
<img src="https://static001.geekbang.org/resource/image/31/45/312b6f1e4ea5b578eaabd057fb98e645.jpg" alt="">
|
||||
|
||||
这个版本和上个版本相比,我们在K8s集群中安装了Istio组件和Knative组件。这2个组件都会给我们应用的Pod注入伴生容器Sidecar,就像一个监护人,监视着我们应用容器的真实状态,利用控制面板和数据面板的配合,自动化运维我们的应用在K8s集群中的状态。
|
||||
|
||||
研发人员还是只关心Dockerfile,所以对于研发人员来说是DevOps。运维人员只需要在K8s集群中安装好Knative组件,并维护Knative组件就可以了。应用的状态由Knative自动维护,所以我们也称之为Container Serverless。要注意这里的应用可以是单个函数,也可以是微服务,也可以是数据库,具体内容完全由你的Dockerfile去编排。所以我们可以看出,FaaS和BaaS的底层就是由容器服务实现的,但具体的容器方案,可能是Docker,也可能是虚拟机。每个云服务商都有自己的策略,不过我们通过Knative可以了解到Container Serverless的工作原理。
|
||||
|
||||
## Serverless应用
|
||||
|
||||
“待办任务”Web服务,第八个版本是[lesson11分支](https://github.com/pusongyang/todolist-backend/tree/lesson11),以下是这个版本的示意图。
|
||||
|
||||
<img src="https://static001.geekbang.org/resource/image/af/8b/af135fcbd55185ddfa1aa0082436e98b.jpg" alt="">
|
||||
|
||||
这个版本是个重大的重构,因为跟我们前面的写法都不一样了。我们的“待办任务”Web服务采用了Midway-FaaS框架,依托Midway-FaaS去适配云服务商,支持我们的应用可以自由选择部署在阿里云或腾讯云的FaaS上。而且这个版本,我们的代码写成的TypeScript,逻辑也更加清晰了。这个也是现在FaaS部署Serverless应用的最佳体验,大厂将自己的成功的Serverless业务,做成方案沉淀,作为框架输出。我们依赖这些Serverless框架,可以快速开发迭代我们自己的Serverless应用,享受FaaS的红利。
|
||||
|
||||
以上就是贯穿咱们专栏的案例——“待办任务”Web服务的整个演进过程了。
|
||||
|
||||
## 结语
|
||||
|
||||
我搜集了部分关注Serverless技术的同学的提问,在这里我也想统一回答一下,你也可以看看自己是否也有同样的疑问。
|
||||
|
||||
就像我[[第1课]](https://time.geekbang.org/column/article/224559) 中所讲的,Serverless是对现代互联网服务端运维体系的极端抽象,对开发者的变革较大。降低了服务端运维的门槛,就意味着即使服务端运维经验是零,也可以将自己开发的应用快速部署上云,这点对前端工程师是很大的利好。
|
||||
|
||||
对于后端工程师和运维工程师,掌握FaaS和服务编排,无疑也是一大利器。FaaS的低成本、高可用,还有事件响应机制,都可以在现有的后端微服务或者应用架构中发挥出巨大的优势。
|
||||
|
||||
对于云服务商,FaaS还可以利用碎片化的物理机计算资源,提升资源利用率,而且还可以帮助云服务商提升云服务的利用率。
|
||||
|
||||
有同学让我讲一下大厂的成功案例,其实大家看到的很多FaaS创建的模板,都是来自于大厂案例的沉淀。FaaS诞生的过程,其实是大厂或云服务厂商将自己的应用运维能力逐渐下沉的一个过程,并不是先有了FaaS,大家再去思考什么场景适合FaaS。这也是为什么我要用“待办任务”Web服务这个案例,一步步升级运维体验,向你讲解整个Serverless的发展史。
|
||||
|
||||
最后,我想说,我并不想用我们的最佳实践来束缚你的思想。我一直觉得,Serverless虽然是在大厂运维能力的基础上诞生并成长来的,但是利用Serverless,再结合我们的想象力,是可以创造出更多的可能的。总的来说,一句话,不要让它束缚我们的想象力,Serverless+AI、Serverless+IoT、Serverless+游戏等等,才应该是我们下一步要探索的方向。
|
||||
|
||||
未来,加油!
|
||||
|
||||
[<img src="https://static001.geekbang.org/resource/image/b1/f0/b1b69daf49fa62a7e1c1ace450e1c5f0.jpg" alt="">](https://jinshuju.net/f/zYEwTp)
|
||||
10
极客时间专栏/Serverless入门课/结束语/结课问卷获奖用户名单.md
Normal file
10
极客时间专栏/Serverless入门课/结束语/结课问卷获奖用户名单.md
Normal file
@@ -0,0 +1,10 @@
|
||||
|
||||
你好!
|
||||
|
||||
截至今天,本专栏有奖收集结课问卷阶段就结束了,十分感谢你的参与。现在我们来公布一下获奖用户名单。
|
||||
|
||||
在这里,我首先要感谢各位同学给我们的反馈,你们的声音可以促使我们精益求精。在这些反馈中,我们看到了很多非常有价值的信息,也收获了很多的支持与肯定。在此,我们精选出了反馈最为具体、丰富,最有实际价值的 5 位用户,送出“极客时间原创手机折叠支架”,或者“价值 99 元的极客时间课程阅码”。中奖名单如下:
|
||||
|
||||
<img src="https://static001.geekbang.org/resource/image/10/55/10f364f6c99b00ad6409fd93fd04b055.jpg" alt="">
|
||||
|
||||
恭喜这 5 位同学,也再次感谢所有参与调研的同学。希望大家今后还能多多支持,给予宝贵意见。
|
||||
157
极客时间专栏/Serverless入门课/进阶篇/05 | 后端BaaS化(上):NoOps的微服务.md
Normal file
157
极客时间专栏/Serverless入门课/进阶篇/05 | 后端BaaS化(上):NoOps的微服务.md
Normal file
@@ -0,0 +1,157 @@
|
||||
<audio id="audio" title="05 | 后端BaaS化(上):NoOps的微服务" controls="" preload="none"><source id="mp3" src="https://static001.geekbang.org/resource/audio/d6/11/d63a54ce8d097aca2b5ff55a41dc1d11.mp3"></audio>
|
||||
|
||||
你好,我是秦粤。现在我们知道了在网络拓扑图中,只有Stateless节点才能自由扩缩容,而Stateful节点因为保存了重要数据,我们要谨慎对待,因此很难做扩缩容。
|
||||
|
||||
FaaS连接并访问传统数据库会增加额外的开销,我们可以采用数据编排的思想,将数据库操作转为RESTful API。顺着这个思路,我引出了后端应用的BaaS化,一句话总结,后端应用BaaS化就是将后端应用转换成NoOps的数据接口。那怎么理解这句话呢?后端应用BaaS化,究竟应该怎么做?接下来的几节课,我们会展开来讲。
|
||||
|
||||
我们先回忆一下上节课的“待办任务”Web应用,这个项目前端是单页应用,中间用了FaaS做SFF数据网关,后端数据接口还要BaaS化。这个案例会贯穿我们的课程,所以你一定要动手run一下。为了让你对我们的项目有个宏观上的认识,我还是先交付你一张大图。
|
||||
|
||||
<img src="https://static001.geekbang.org/resource/image/ba/c9/bab7e22b588d69cbe0197d36696411c9.jpg" alt="" title="“待办任务”Web应用架构图">
|
||||
|
||||
这个架构的优势是什么呢?我们将这个图变个形,你就更容易理解了。
|
||||
|
||||
<img src="https://static001.geekbang.org/resource/image/66/8f/66aeb01686c94478b2847be5bb2a398f.jpg" alt="" title="架构变形示意图">
|
||||
|
||||
咱从左往右看上面这张图。用户从浏览器打开我们网站时,前端应用响应返回index.html;然后浏览器去CDN下载我们的静态资源,完成页面静态资源的加载;与此同时,浏览器也向前端应用发起数据请求;前端应用经过安全签名后,再将数据请求发送给SFF;SFF再根据数据请求,调用后端接口或服务,最终将结果编排后返回。
|
||||
|
||||
从图里你可以看到,除了数据库是Stateful的,其它节点都已经变成了Stateless。如果你公司业务量不大的话,这个架构其实已经足够了。就像传统的MVC架构一样,单点数据库,承载基本的并发量不是问题,而且数据也可以通过主从结构和客户端读写分离的方式来优化。
|
||||
|
||||
但MVC架构最大的问题就是累积,当一个MVC架构的应用,在经历长期迭代和运营后,数据库一定会变得臃肿,极大降低数据库的读写性能。而且在高并发达到一定量级,Stateful的数据库还是会成为瓶颈。那我们可以将自己的数据库也变成BaaS吗?
|
||||
|
||||
要解决数据库的问题,也可以选择我上节课和你说的云服务商提供的BaaS服务,比如DynamoDB。但云服务商BaaS服务究竟是怎么做到的?如果BaaS服务能力不全,不够满足我们的需要时怎么办?今天我就先带你看看传统的MVC应用中的数据库怎么改造成BaaS。
|
||||
|
||||
当然,BaaS化的过程有些复杂,这也正是我们后面需要用几节课才能跟你解释清楚的核心知识点。正如我们本节课的标题:后端应用BaaS化,就是NoOps的微服务。在我看来后端应用BaaS化,跟微服务高度重合,微服务几乎涵盖了我们BaaS化要做的所有内容。
|
||||
|
||||
所以我们先来学习一下微服务是什么。
|
||||
|
||||
## 微服务的概念
|
||||
|
||||
微服务的概念对很多做后端同学来说并不陌生,尤其是做Java的同学,因为早些年Java就提出SOA面向服务架构。微服务算是SOA的一个子集,2014年由ThoughtWorks的Martin Fowler提出。微服务设计之初是为了拆解巨石应用,巨石应用就是指那些生命周期较长的,累计了大量业务高度耦合和冗余代码的企业应用。
|
||||
|
||||
跟Serverless的概念还在发展中不同,微服务的概念在这么多年的发展中已经有了明确的定义了。下面是AWS官方的解释:
|
||||
|
||||
>
|
||||
微服务是一种开发软件的架构和组织方法,其中软件由通过明确定义的 API 进行通信的小型独立服务组成,这些服务由各个小型独立团队负责。微服务架构使应用程序更易于扩展和更快地开发,从而加速创新并缩短新功能的上市时间。
|
||||
|
||||
|
||||
那在我看来,微服务就是先拆后合,它将一个复杂的大型应用拆解成职责单一的小功能模块,各模块之间的数据模型相互独立,模块采用API接口暴露自己对外提供服务,然后再通过这些接口组合出原先的大型应用。拆解的好处是,小模块便于维护,可以快速迭代,跨应用复用。
|
||||
|
||||
我们的Serverless专栏并不打算给你详细地讲解微服务,但是希望你能一定程度上了解微服务。FaaS和微服务架构的诞生几乎是在同一时期,它俩的很多理念都是来自12要素(Twelve-Factor App)[1],所以微服务概念和FaaS概念高度相似,也有不少公司用FaaS实现微服务架构;不同的是,微服务的领军公司ThoughtWorks和NetFlix到处宣扬他们的微服务架构带来的好处,而且他们提出了一整套方法论,包括微服务架构如何设计,如何拆解微服务,尤其是数据库如何设计等等。
|
||||
|
||||
我们后端应用BaaS化,首先要将复杂的业务逻辑拆开,拆成职责单一的微服务。**因为职责单一,所以服务端运维的成本会更低。**而且拆分就像治理洪水时的分流一样,它能减轻每个微服务上承受的压力。
|
||||
|
||||
我在Serverless的专栏里向你介绍微服务,主要就是想引入微服务拆解业务的分流思想和微服务对数据库的解耦方式。
|
||||
|
||||
## 微服务10要素
|
||||
|
||||
谈起微服务,很多人都要说12要素[2],认为微服务也应该满足12要素。12要素当初是为了SaaS设计的,用来提升Web应用的适用性和可移植性。我在做微服务初期也学习过12要素,但我发现这个只能提供理论认识。
|
||||
|
||||
所以在2018年GIAC的大会上,我将我们团队在微服务架构落地中的经验总结为**微服务的10要素:API、服务调用、服务发现;日志、链路追踪;容灾性、监控、扩缩容;发布管道;鉴权。**
|
||||
|
||||
我们回想一下[[第1课]](https://time.geekbang.org/column/article/224559) 中小服的工作职责:
|
||||
|
||||
1. 无需用户关心服务端的事情(容错、容灾、安全验证、自动扩缩容、日志调试等等)。
|
||||
1. 按使用量(调用次数、时长等)付费,低费用和高性能并行,大多数场景下节省开支。
|
||||
1. 快速迭代&试错能力(多版本控制,灰度,CI&CD等等)。
|
||||
|
||||
你有没有发现跟微服务的要素有大量的重合。API就是RESTful的HTTP数据接口;服务调用你可以理解为就是HTTP请求;服务发现你可以理解为我们只能用域名调用我们的HTTP请求,不能用IP;日志、容灾、监控都不难理解;链路追踪,是微服务重要的一环,因为相对传统MVC架构,我们一个请求在后端的调用链增长了,为了快速定位问题,我们都需要打印整个调用链路的异常栈;发布管道和鉴权,和我们的FaaS也有很重要的关联,我将放在下一课讲解。
|
||||
|
||||
<img src="https://static001.geekbang.org/resource/image/45/f1/4558c4088c38623cf366e7d686654ff1.png" alt="" title="木桶效应示意图">
|
||||
|
||||
接下来,我不准备按照微服务的10要素一一操作,但我希望可以通过Serverless帮你建立知识体系的索引,所以关联的技术栈我都尽量点出来,你可以自己进一步了解学习。
|
||||
|
||||
我们再次拿出我们的创业项目“待办任务”Web网站,这次我将带你一起看看微服务如何让数据库解耦。
|
||||
|
||||
我们先分析一下“待办任务”Web服务。上一讲末尾我的作业其实已经为你准备好了,我们一起看看index.js文件,这是一个典型的MVC架构的应用。View层就是index.html和静态资源文件;Model层,我们引入lowdb用一个db.json文件代替数据库;Control层,就是app.METHOD,处理/api/*的数据逻辑。微服务是解决后端应用的,所以我们只需要关注Control层。
|
||||
|
||||
<img src="https://static001.geekbang.org/resource/image/25/a4/25981be7fb564da00a736bfe93f101a4.jpg" alt="" title="index.js文件">
|
||||
|
||||
API的数据处理,主要有两个,一个是/api/rule处理待办任务的,一个是/api/user负责用户登录态,而且我们“待办任务”主要的数据模型也就是待办任务和用户这两个。那我们可以将后端拆分出两个微服务:待办任务微服务和用户微服务。这里我要强调下,我只是为了向你演示微服务才这样做,在实际业务中,这么简单的功能,没必要过早地拆分。
|
||||
|
||||
<img src="https://static001.geekbang.org/resource/image/3a/ab/3abe89510aa6897b0b1f50e239c91dab.jpg" alt="" title="微服务拆解演示">
|
||||
|
||||
初步拆解,我们将index.js中的数据库移走了,而且拆分后各微服务的数据库比原来混杂在一起简单且容易维护多了。user.js负责用户相关业务逻辑,只维护用户信息的数据库,并且暴露RESTful的HTTP方法;rule.js负责待办任务的增删改查,只维护待办任务的数据库,并且暴露RESTful的HTTP方法;index.js只需要负责将请求HTTP返回给数据编排。
|
||||
|
||||
HTTP协议要满足我们的服务调用与发现,发布的user.js和rule.js必须使用域名,例如api.jike-serverless.online/user和api.jike-serverless.online/rule。这样新扩容的机器IP,只需要注册到这个域名下就可以被服务发现IP并调用了。
|
||||
|
||||
我们现在拆分后只是将数据库从index.js移出,分给到了user.js和rule.js,但两个节点到目前为止数据库仍然是Stateful的。我们如何再让这两个数据库节点变成Stateless呢?你可以按一下暂停键思考一下。
|
||||
|
||||
我们先想想,对于rule.js这个微服务来说,我要再扩容一个新的实例,而且让两边的实例保持一致,那么我们是否只需要让新的数据库和旧的数据库保持同步更新就可以了呢?
|
||||
|
||||
## 解耦数据库
|
||||
|
||||
没错,其实要解决这个问题的核心就是让数据启动时,可以更新到最新的数据库。在其中一个数据库更新时,通知另外一个数据库更新。
|
||||
|
||||
这里就要引出一个关键的Stateful对象:消息队列[3]。它是一个稳定的绝对值得信赖的Stateful节点,而且对你的应用来说消息队列是全局唯一的。解耦数据库的思路就是,通过消息队列解决数据库和副本之间的同步问题,有了消息队列,剩下的就简单了。
|
||||
|
||||
我们每个微服务中的数据库,例如MySQL,写的操作都会产生binary log,通过额外的进程将binary log变更同步到消息队列,并监听消息队列将binary log更新在本地执行,修改MySQL。比较著名的解决方案就是领英开源的Kafka,现在云服务商基本也都会提供Kafka服务。当然数据库和副本之间会有短暂的同步延迟问题,但问题其实也不大,因为我们通常对数据库进行写操作时也会锁表。
|
||||
|
||||
如果你要细究这个问题,那就会碰到分布式架构无法同时满足的CAP[4] 课题。我们在此也不深入展开CAP话题了,目前异步同步的时间差在很多场景下我觉得是可以接受的。
|
||||
|
||||
<img src="https://static001.geekbang.org/resource/image/96/36/966b47dfea82a3d841e45cd260270636.jpg" alt="" title="微服务拆解演示">
|
||||
|
||||
拿我们自己的例子来说也很简单,我们用消息队列RocketMQ,写操作我们都写入RocketMQ,rule.js进程中我们监听RocketMQ,一旦有rule写入的消息,我们就更新我们的lowdb数据。围绕消息队列建立的同步机制,让每个微服务的数据库和它的扩容副本之间自动同步了。对于微服务来说,它本身的数据库还是Stateful的,但在微服务外部看来,这个微服务是Stateless的。如下图所示:
|
||||
|
||||
<img src="https://static001.geekbang.org/resource/image/7a/3a/7ab3bebbffabd18282d54e17093d0c3a.jpg" alt="" title="同步机制">
|
||||
|
||||
跟传统的主从数据库方式不同的是,我们每个微服务的单点实例都是独享一个数据库的,rule这个微服务单点可以对外提供所有待办任务的RESTful API接口。
|
||||
|
||||
你要知道消息队列,例如RocketMQ的可靠性是99.99999999%,而我们常用的虚拟机ECS的可靠性也只不过99.95%。在高并发架构的设计中,我们通常会要求Stateful节点一定要稳定,而且越少越好,所以消息队列,对于微服务和FaaS来说,都是可以作为支撑业务的核心。我们依赖消息队列,会让整个架构更稳定。
|
||||
|
||||
## 总结
|
||||
|
||||
我们为了避免在FaaS中直接操作数据库,而将数据库操作变成BaaS服务。为了理解BaaS化具体的工作,我们引入了微服务的概念。微服务先将业务拆解成单一职责和功能的小模块便于独立运维,再将小模块组装成复杂的大型应用程序。这一点上跟我们要做的后端应用BaaS化相似度非常高。所以参考微服务,我们先将MVC架构的后端解成了两个微服务,再用微服务解耦数据库的操作,先将数据库拆解成职责单一的小数据库,再用消息队列同步数据库和副本,从而将数据变成了Stateless。
|
||||
|
||||
现在我们再来回顾一下这节课的重要知识点。
|
||||
|
||||
1. 微服务的概念:它就是将一个复杂的大型应用拆解成职责单一的小功能模块,各模块之间数据模型相互独立,模块采用API接口暴露自己对外提供的服务,然后再通过这些API接口组合成大型应用。这个小功能模块,就是微服务。
|
||||
1. 微服务10要素:API、服务调用、服务发现;日志、链路追踪;容灾性、监控、扩缩容;发布管道;鉴权。这跟我们要做的BaaS化高度重合,我们可以借助微服务来实现我们的BaaS化。
|
||||
1. 解耦数据库:通过额外进程让数据库与副本直接通过消息队列进行同步,所以对于微服务应用来说,只需要关注自身独享的数据库就可以了。微服务通过数据库解耦,将后端应用变成Stateless的了,但对后端应用本身而言,数据库还是Stateful的。
|
||||
|
||||
## 作业
|
||||
|
||||
由于Kafka云服务比较贵,所以这节课的作业我们采用表格存储[5]来模拟Kafka,以实现DB的同步功能。**你可以尝试自己部署一下rule-faas到一个新的域名。**下面是具体的代码地址:
|
||||
|
||||
- 仓库地址:[https://github.com/pusongyang/todolist-backend/tree/lesson05](https://github.com/pusongyang/todolist-backend/tree/lesson05)
|
||||
- 前端地址:[https://github.com/pusongyang/todolist-frontend/tree/lesson05](https://github.com/pusongyang/todolist-frontend/tree/lesson05)
|
||||
- 实现后效果:[http://lesson5.jike-serverless.online/list](http://lesson5.jike-serverless.online/list)
|
||||
|
||||
你可以根据自己的域名,替换一下前端文件中的这个地址:[http://faas.jike-serverless.online/api/rule](http://faas.jike-serverless.online/api/rule),或者用我的前端代码[lesson5](https://github.com/pusongyang/todolist-frontend/tree/lesson05)分支修改/src/pages/ListTableList/service.ts中的地址,自己 `npm run build` 一下,替换掉项目public目录,压缩上传到函数服务目录。
|
||||
|
||||
这次的部署会有些复杂,我们要部署两个FaaS服务,一个是rule-faas.js,用来处理/api/rule;另一个是index-faas.js,用来处理我们的静态资源请求。原理图如下所示:
|
||||
|
||||
<img src="https://static001.geekbang.org/resource/image/98/b0/987db6d15137ce820b56fcf97e91e8b0.jpg" alt="" title="部署示意图">
|
||||
|
||||
另外.aliyunConfig文件格式如下,你需要填入自己的配置情况,切记这个文件不可以上传到Git仓库:
|
||||
|
||||
```
|
||||
//.aliyunConfig文件,保存秘钥,切记不可以上传Git
|
||||
const endpoint = "https://rule.cn-shanghai.ots.aliyuncs.com";
|
||||
// AccessKey 阿里云身份验证,在阿里云服务器管理控制台创建
|
||||
const accessKeyId = "AccessKey";
|
||||
// SecretKey 阿里云身份验证,在阿里云服务器管理控制台创建
|
||||
const accessKeySecret = "SecretKey";
|
||||
// 在数据链表中查看
|
||||
const instancename = "rule";
|
||||
const tableName = 'todos';
|
||||
const primaryKey = [{ 'key': 'list' } ];
|
||||
|
||||
|
||||
module.exports = {endpoint, accessKeyId, accessKeySecret, instancename, tableName, primaryKey};
|
||||
|
||||
|
||||
```
|
||||
|
||||
期待你的作业和思考,如果今天的内容让你有所收获,也欢迎你把文章分享给身边的朋友,邀请他加入学习。
|
||||
|
||||
## 参考资料
|
||||
|
||||
[1] [https://en.wikipedia.org/wiki/Twelve-Factor_App_methodology](https://en.wikipedia.org/wiki/Twelve-Factor_App_methodology)
|
||||
|
||||
[2] [https://12factor.net/zh_cn/](https://12factor.net/zh_cn/)
|
||||
|
||||
[3] [https://help.aliyun.com/document_detail/29532.html?spm=5176.11065259.1996646101.searchclickresult.6936139bcS8BlU](https://help.aliyun.com/document_detail/29532.html?spm=5176.11065259.1996646101.searchclickresult.6936139bcS8BlU)
|
||||
|
||||
[4] [https://www.ruanyifeng.com/blog/2018/07/cap.html](https://www.ruanyifeng.com/blog/2018/07/cap.html)
|
||||
|
||||
[5] [https://ots.console.aliyun.com/](https://ots.console.aliyun.com/index#/list/cn-shanghai)
|
||||
135
极客时间专栏/Serverless入门课/进阶篇/06 | 后端BaaS化(中):业务逻辑的拆与合.md
Normal file
135
极客时间专栏/Serverless入门课/进阶篇/06 | 后端BaaS化(中):业务逻辑的拆与合.md
Normal file
@@ -0,0 +1,135 @@
|
||||
<audio id="audio" title="06 | 后端BaaS化(中):业务逻辑的拆与合" controls="" preload="none"><source id="mp3" src="https://static001.geekbang.org/resource/audio/33/90/33ccbfc021b2b37321afe8e1cb955390.mp3"></audio>
|
||||
|
||||
你好,我是秦粤。上一课中,我们学习了后端BaaS化的重要模块:微服务。现在我们知道微服务的核心理念就是先拆后合,拆解功能是为了提升我们功能的利用率。同步我们也了解了实现微服务的10要素,这10要素要真讲起来够单独开一门课的。如果你不熟悉,我向你推荐杨波老师的[《微服务架构核心20讲》](https://time.geekbang.org/course/intro/100003901)课程。
|
||||
|
||||
BaaS化的核心其实就是把我们的后端应用封装成RESTful API,然后对外提供服务,而为了后端应用更容易维护,我们需要将后端应用拆解成免运维的微服务。这个逻辑你要理解,这也是为什么我要花这么多篇幅给你谈微服务的关键原因。
|
||||
|
||||
上节课我们将“待办任务”Web服务的后端,拆解为用户微服务和待办任务微服务。但为什么要这样拆?是凭感觉,还是有具体的方法论?这里你可以停下来想想。
|
||||
|
||||
微服务的拆解和合并,都有一个度需要把握,因为我们在一拆一合之间,都是有成本产生的。如果我们拆解得太细,就必然会导致我们的调用链路增长。调用链路变长,首先影响的就是网络延迟,这个好理解,毕竟你路远了,可能“堵车”的地方也会变多;其次是运维成本的增加,调用链路越长,整个链条就越脆弱,因为其中一环出现问题,都会导致整个调用链条访问失败,而且我们排查问题也变得更加困难。
|
||||
|
||||
反过来看,如果我们拆解得太粗,调用链路倒是短了,但是这个微服务的复用性就差了,更别提因为高耦合带来的复杂且冗余的数据库表结构,让我们后续难以维护。我画了个图,你感受下。
|
||||
|
||||
<img src="https://static001.geekbang.org/resource/image/e3/81/e3809702efd712b18d28465b2512e681.png" alt="" title="拆解程度示意图">
|
||||
|
||||
## 拆之,领域驱动设计
|
||||
|
||||
那我们要合理地拆解微服务,应该怎么拆解呢?上节课其实我有提到,目前主流的解决方案就是领域驱动设计,也叫DDD[1]。DDD是Eric Evans在其2004年的同名书中提出来的一个思想,但一直仅仅局限在Java的圈子里,直到2014年,微服务兴起后大家才发现它可以指导微服务的拆分,这才走进了大多数人的视野。用一句话简单总结,DDD就是一套方法论:**通过对业务分层抽象,分析定义出领域模型,用领域模型驱动我们设计系统,最终将复杂的业务模型拆解为独立运维的领域模型。**
|
||||
|
||||
实际我自己在使用微服务开发的过程发现,微服务整体应该是一个动态网络结构[2],随着业务的发展,这个网络结构也会发生变化。DDD能帮助我们前期分析出一个较好的网络结构,但实际上,我们更应该思考的是如何整体优化动态网络:**减少核心节点,保护核心节点,降低网络深度等等。**
|
||||
|
||||
怎么理解动态网络优化呢?我们可以做个思维实验:假设我们将所有的功能都拆解成微服务,任意的微服务节点之间都可以相互调用,调用越频繁它们之间的距离就越近。那么我们考虑一下,当我们网站的访问请求流量稳定后,我们整个微服务节点组成的网络状态是怎么样的?
|
||||
|
||||
首先网络节点的相互制约总会让那些相互之间强依赖的、高耦合的节点,越走越近,最后聚集成一团节点。其次那些跟业务逻辑无关的节点,逐渐被边缘化,甚至消失。我们看这些聚集成团的节点,如果团里的点聚合太近了,其实是不适合拆分的,它们整体应该作成一个微服务。等这些节点太近的团合并成一个微服务节点后,我们再看那些聚集在一起、又不太近的节点就是一个个微服务了。
|
||||
|
||||
所以,我们在启动项目时,不用太过纠结应该如何去拆解微服务。而应该持续关注,并思考每个微服务节点的合理性。就像看待动态网络一样,持续地调整优化,去除核心节点。最终它会伴随你业务的发展阶段,达到各个阶段的稳定动态网络结构。
|
||||
|
||||
就像我们上节课“待办任务”Web服务一样,我们可以先简单地将我们的项目后端分为:用户微服务和待办任务微服务。当然这里我们目前的业务太简单了,用DDD去分析,也是大材小用。随着我这个项目的业务发展,我们添加的功能会越来越多。让微服务根据业务一起成长演变就可以了。这并不是说我们就放任微服务不管了,而是从整体网络的角度思考,去看我们的微服务如何演进。
|
||||
|
||||
<img src="https://static001.geekbang.org/resource/image/58/cf/589469933bdc3b0335f9754ff2f555cf.png" alt="" title="微服务演进过程">
|
||||
|
||||
## 合之,Streaming
|
||||
|
||||
看完拆解,我们再看合并。合并呢,换个高大上的词其实就是前面课程中提到的编排。目前为止,我们整个“待办任务”Web应用架构的设计基本完成了,而且所有节点都是Stateless的了。变成Stateless节点后,其实对于前端的同学来说,一点都不陌生,比如React的单向数据流中的State也要求我们Immutable,Immutable其实就是Stateless。
|
||||
|
||||
我们上面已经看到了,拆解后的架构是个动态网络,那我们应该怎么合并或者编排呢?当然你像SFF那样通过传统的函数,将每个HTTP数据的请求结果通过数组或对象加工处理,再将这些结果返回也是可以的。但我在这里想向你介绍另外一种编排思路,工作流。
|
||||
|
||||
<img src="https://static001.geekbang.org/resource/image/d2/57/d267851c983200b430dfb5d53fd4d557.gif" alt="" title="数据流演示图">
|
||||
|
||||
我们可以将用户的请求想象成我们的呼吸系统,我们的肺就是SFF,而微服务和FaaS节点就是需要氧气的各个器官。我们吸一口气,氧气进入肺部,血液循环将氧气按顺序流经我们每个器官,这就是请求链路。每个器官一接收到新鲜血液,就会吸取氧气返回二氧化碳,最终血液循环将二氧化碳带到肺部呼出,这个就是数据返回链路。我们的各个器官,就被请求链路通过新鲜血液到来的这个事件串联起来了,这个就是事件流,也就是用一个个事件去串联FaaS或微服务。
|
||||
|
||||
现在我们用[[第3课]](https://time.geekbang.org/column/article/227454) 讲的,PHP发邮件改造一下,举个例子。当用户注册时,我们完全可以将用户的信息和注册验证码存入数据库;PHP发邮件的FaaS触发器改为数据库插入新记录触发事件;用户从邮箱验证获取验证码,把验证码写到输入框后,点击验证,则是另一个HTTP触发器,触发FaaS函数校验验证码通过,修改数据库注册成功,并且返回302跳转到登录成功页面。具体流程可参考下图:
|
||||
|
||||
<img src="https://static001.geekbang.org/resource/image/71/38/712127c53b9ba90fe69cf00179862338.png" alt="" title="案例流程图">
|
||||
|
||||
当然现在这个解决方案也有成熟对应的云BaaS服务:Serverless工作流[3]。
|
||||
|
||||
## 安全门神
|
||||
|
||||
理解了拆合的思想,我们就可以将目前“待办项目”的架构再演进一下:静态文件我们用CDN托管,前端项目只负责域名支撑和index.html,剩下的请求直接访问FaaS微服务。这时候,我估计你会问,咱们数据的安全性如何保障呢?是的,到目前为止,我们的FaaS都一直在用匿名模式访问,完全没有任何安全防护可言,也就是说目前我们FaaS服务的接口一直都在互联网上“裸奔”。
|
||||
|
||||
### 鉴权
|
||||
|
||||
其实,FaaS提供的安全防护通常是放在触发器上的。触发器的授权类型或认证方式我们可以设置为:匿名anonymous或函数function。匿名方式就是不需要签名认证,匿名的用户也能访问;而函数方式,则是需要签名认证[4],这个签名认证的算法,参数需要用到我们账户的访问秘钥ak/sk[5],ak/sk相当于我们云账户的银行卡密码,这么重要的账户信息,我们只能限定在服务端使用,前端代码里绝对不可以出现。
|
||||
|
||||
也就是说,我们只能在服务端使用函数安全认证方式。如果是这种方案,我们的“待办任务”架构就演进成下图这样了。
|
||||
|
||||
<img src="https://static001.geekbang.org/resource/image/1f/b3/1f30750203dddab945fb1ff471e40ab3.png" alt="" title="“待办任务”架构图">
|
||||
|
||||
那有没有针对匿名认证方式的安全策略呢?当然有,这里我们同样需要借鉴一下微服务的鉴权设计:JSON Web Token,简称JWT[6]。JWT简单来说,就是将用户身份信息和签名信息,一起传到客户端去了。用户在请求数据时,带上这个JWT,服务端通过校验签名确定用户身份。JWT存在于客户端,JWT验证只需要通过服务端的sk和算法验证签名。同样,我画了张图,以帮助你理解。
|
||||
|
||||
<img src="https://static001.geekbang.org/resource/image/f1/f1/f187db1d21b27d58c55000272f4825f1.png" alt="" title="JWT示意图">
|
||||
|
||||
要解决后端互调的安全性,我们用VPC或IP白名单,都很容易解决。比较难处理的是前后端的信任问题,JWT正好就提供了一种信任链的解决思路。当然,关于鉴权也有一些云服务商推出了一些更加安全易用的BaaS服务,例如AWS的IAM和Cognito[7]。
|
||||
|
||||
安全性是我们考虑架构设计时重要的一环,因为安全架构设计的失败,会直接导致我们资产的损失。鉴权是识别用户身份,防止用户信息泄漏和恶意攻击使用的。但根据我统计的数据,我们在日常99%的问题,都发生在新版本上线的环节。
|
||||
|
||||
那我们该怎么稳定持续地快速迭代,发布新版本上线呢?我们可以回想一下[[第 1 课]](https://time.geekbang.org/column/article/224559),小程和小服的例子,小程最后实现NoOps后,小服则只要将代码合并到指定分支就可以发布上线了。那现实中,这点该怎么实现呢?
|
||||
|
||||
当我们的项目Serverless化以后,代码的质量变得尤为重要。你可以想想,Serverless化之前,你不小心上线了一个bug,影响的范围最大也就只有一个应用。但是Serverless化之后,如果是核心节点发布了严重的bug上线,那么影响的范围就是所有依赖它的线上应用了。
|
||||
|
||||
不过,你也不用太担心,微服务和FaaS都具备快速独立迭代的能力。以前我们一个应用的迭代周期通常要一周到两周。但对于Serverless化后的应用来说,每个节点借助独立运维的特性,可以随时随地的发布上线。
|
||||
|
||||
综上,我们知道了,微服务和FaaS都是快速迭代的,修复问题很快,但我们也不能每次都等问题出现,再去依赖这个能力呀。有没有什么办法可以提前发现问题,保证我们既快又稳?目前软件工程的最佳做法就是代码流水线的发布管道。
|
||||
|
||||
### 发布管道
|
||||
|
||||
发布管道的流水线主要有3个部分:
|
||||
|
||||
1. 代码发布前的验证,代码测试覆盖率CI/CD;
|
||||
1. 模拟流量回归测试通过,发布到灰度环境;
|
||||
1. 代码正式上线,灰度环境替换正式环境。流水线的每个节点产生的结果,都会作为下一个节点必要的启始参数。
|
||||
|
||||
<img src="https://static001.geekbang.org/resource/image/8e/4b/8e3a2a76cfcf51d2cc2eafaaf0a2db4b.png" alt="" title="发布管道流水线">
|
||||
|
||||
我们先看看上图,我来解释下这个流程。
|
||||
|
||||
- 我们的代码合并到指定分支后,通常我会用Develop分支。
|
||||
- Git的钩子就会触发后续的流水线,开始进入构建打包、测试流程。
|
||||
- 测试节点做的事情就是跑所有测试Case,并且统计覆盖率。
|
||||
- 覆盖率验证通过,代码实例用录制流量模拟验证。
|
||||
- 模拟验证通过,发布代码实例到灰度环境。
|
||||
- 线上根据灰度策略,将小部分流量导入灰度环境验证灰度版本。
|
||||
- 在灰度窗口期,比如两个小时,灰度验证没有异常则用灰度版本替换正式版本;反之则立即丢弃这个灰度版本,止损。
|
||||
|
||||
这套流程,目前规模大一些的互联网公司发布流程基本都在这样跑,如果你不是很了解,可以自己尝试用我们介绍的Serverless工作流或者云服务商提供的工作流工具[8]动手搭建下。
|
||||
|
||||
在这套流程的基础上,很多企业为了追求更高的稳定性,还会设定环境隔离的流水线和安全卡口。比如隔离测试环境和线上环境,测试环境用来复现故障。每次代码进入发布管道,都必须先在测试环境跑通,跑通后安全卡口放行,才能进入线上环境的流水线。
|
||||
|
||||
## 总结
|
||||
|
||||
这节课,我们继续讲后端的BaaS化。我们再梳理一下这节课的重要知识点吧。
|
||||
|
||||
1. 如何拆解BaaS应用,我们学习了微服务的重要拆解思想DDD:**通过对业务分层抽象,分析定义出领域模型,用领域模型驱动我们设计系统,最终将复杂的业务模型拆解为独立运维的领域模型。**另外我也介绍了另一种更适合初创企业的拆分思路:动态网络演进。
|
||||
1. 拆解完之后,我们就要考虑合并。这里我们介绍了代码编排以外的另一种编排方式:事件流编排,它就是通过一个个事件顺序将我们的微服务或FaaS串联起来。
|
||||
1. 为了解决拆解后,微服务之间的信任问题。我们先了解了FaaS触发器的安全方案:数字签名。还借鉴了微服务的鉴权做法JWT,将用户鉴权加密信息放在客户端,让鉴权服务变成Stateless。最后,为了让微服务又快又稳地发布版本,我们借鉴了微服务的发布管道:打造自动灰度流水线。
|
||||
|
||||
## 作业
|
||||
|
||||
这节课的作业就是我们JWT鉴权的“待办任务”Web应用,你来部署上线。
|
||||
|
||||
后端代码GitHub地址:[https://github.com/pusongyang/todolist-backend/tree/lesson06](https://github.com/pusongyang/todolist-backend/tree/lesson06)
|
||||
|
||||
前端代码GitHub地址:[https://github.com/pusongyang/todolist-frontend](https://github.com/pusongyang/todolist-frontend)
|
||||
|
||||
演示预览地址:[http://lesson6.jike-serverless.online/list](http://lesson6.jike-serverless.online/list)
|
||||
|
||||
期待你的作业,如果今天的内容让你有所收获,也欢迎你把文章分享给身边的朋友,邀请他加入学习。
|
||||
|
||||
## 参考资料
|
||||
|
||||
[1] [https://en.wikipedia.org/wiki/Domain-driven_design](https://en.wikipedia.org/wiki/Domain-driven_design)
|
||||
|
||||
[2] [https://en.wikipedia.org/wiki/Dynamic_network_analysis](https://en.wikipedia.org/wiki/Dynamic_network_analysis)
|
||||
|
||||
[3] [https://www.aliyun.com/product/fnf](https://www.aliyun.com/product/fnf)
|
||||
|
||||
[4] [https://github.com/aliyun/fc-nodejs-sdk/blob/master/lib/client.js?spm=a2c4g.11186623.2.15.16e016d7lo8NBQ#L840](https://github.com/aliyun/fc-nodejs-sdk/blob/master/lib/client.js?spm=a2c4g.11186623.2.15.16e016d7lo8NBQ#L840)
|
||||
|
||||
[5] [https://help.aliyun.com/document_detail/154851.html?spm=5176.2020520153.0.0.371a415dLXyltZ](https://help.aliyun.com/document_detail/154851.html?spm=5176.2020520153.0.0.371a415dLXyltZ)
|
||||
|
||||
[6] [https://jwt.io/](https://jwt.io/)
|
||||
|
||||
[7] [https://docs.aws.amazon.com/zh_cn/cognito/latest/developerguide/what-is-amazon-cognito.html](https://docs.aws.amazon.com/zh_cn/cognito/latest/developerguide/what-is-amazon-cognito.html)
|
||||
|
||||
[8] [https://www.aliyun.com/product/yunxiao/devops?spm=5176.10695662.1173276.1.6c724a38akCjgo](https://www.aliyun.com/product/yunxiao/devops?spm=5176.10695662.1173276.1.6c724a38akCjgo)
|
||||
201
极客时间专栏/Serverless入门课/进阶篇/07 | 后端BaaS化(下):Container Serverless.md
Normal file
201
极客时间专栏/Serverless入门课/进阶篇/07 | 后端BaaS化(下):Container Serverless.md
Normal file
@@ -0,0 +1,201 @@
|
||||
<audio id="audio" title="07 | 后端BaaS化(下):Container Serverless" controls="" preload="none"><source id="mp3" src="https://static001.geekbang.org/resource/audio/fc/31/fc3b6496365e041c16f88119b661ba31.mp3"></audio>
|
||||
|
||||
你好,我是秦粤。上节课,我重点给你讲了业务逻辑的拆和合,拆的话可以借助DDD的方法论,也可以用动态网络的思想让微服务自然演进;合的话,我们可以用代码编排,也可以用事件流来驱动。另外,我们还了解了微服务拆解后会带来的安全信任问题,这可以通过微服务的跨域认证JWT方案解决。我们还了解了后端应用要支持快速迭代或发布,可以参考微服务搭建灰度发布流水线,也就是发布管道。其实我们在使用FaaS过程中遇到的很多问题,都可以借助或参考微服务的解决方案。
|
||||
|
||||
现在我们再回顾一下BaaS化后的“待办任务”Web服务,我们已经将后端拆解为用户微服务和待办任务微服务,前端用户访问我们的FaaS服务,登录后获取到JWT,通过数据接口+JWT安全地访问我们的微服务。而且我们的FaaS和微服务都具备了快速迭代的能力。
|
||||
|
||||
<img src="https://static001.geekbang.org/resource/image/c8/15/c8ee82521e5c965d8955afe8c210b615.jpg" alt="" title="BaaS化后的“待办任务”Web服务">
|
||||
|
||||
到这里,我要指出我之前rule-faas.js的一个Bug,如果你之前亲自动手做过实验的话,估计也有发现。这个Bug的直接表现是用户初次请求数据时,如果触发了冷启动,返回的待办任务列表就会为空。
|
||||
|
||||
究其原因,是因为冷启动时连接数据库会有延时,这直接导致了第一个请求返回的待办任务列表还未同步到消息队列的数据。要解决这个bug,我们可以用之前讲过的预热FaaS或预留实例的方式,但你也要知道,FaaS函数扩容时,新启动的函数副本也会出现同样的问题。
|
||||
|
||||
我前面卖了很多关子,其实FaaS在设计时已经考虑到这个问题了,所以在FaaS的“函数配置”里,都会提供一项“函数初始化入口”的选项。但你会发现,它同时也会让你配置初始化时间,最少1秒。还记得我们在[[第 2 课]](https://time.geekbang.org/column/article/226574),讲函数执行阶段的一人一半的函数代码初始化时间吗?没错,就是那个时间。
|
||||
|
||||
当你配置了“函数初始化入口”,每次启动FaaS实例时,系统都会先等待初始化函数执行,而且在函数初始化时间结束后,才会继续执行函数。而从目前平台的配置来看,初始化时至少也需要1秒的时间(你去云服务商后台就能看到)。
|
||||
|
||||
对很多事件触发的应用场景,FaaS增加几秒的初始化时间,影响并不大。但对很多后端应用则不然,尤其是Java等语言,如果软件包比较大,启动和初始化的时间会更长。
|
||||
|
||||
要缩短或绕过初始化的时间,我们要尽可能地利用Runtime里面给我们提供的内置能力,例如我们BaaS化一直提倡使用服务编排和HTTP接口,就是因为云服务商SDK和HTTP协议,所有语言的Runtime里都内置了。
|
||||
|
||||
那什么是Runtime呢?Runtime其实就是运行我们代码所需要的函数库和二进制包。FaaS中的Runtime是云服务商预先设计好的,会放一些常用的函数库和二进制包进去,相当于是平台的能力。
|
||||
|
||||
但是当我们后端应用BaaS化后,想采用FaaS方案部署的话则会碰到Runtime这个拦路虎。FaaS虽然已经支持多数主流后端语言,但后端应用根据业务需求,要依赖特殊的函数库或二进制包,FaaS的官方Runtime就无法支持。而且像Java等语言,在代码包较大的情况下,FaaS启动速度很慢,也不适合。例如Node.js的Runtime,其实也包括我们自己安装的NPM包,所以相当于我们可以部分定制。但是你也注意到了,我们是整个目录包括node_modules 一起上传的,也就是说涉及编译的NPM包是无法跨操作系统生效的。这种场景下FaaS的Runtime不可控就会成为我们难以绕过的问题了。当我们遇到FaaS无法解决的场景,我们就可以考虑下降一层,使用FaaS的底层支撑技术Docker容器了。
|
||||
|
||||
<img src="https://static001.geekbang.org/resource/image/09/d7/09bb10fa7d1a95f88828bc413c7c9bd7.png" alt="" title="广义Serverless">
|
||||
|
||||
还记得我们[[第 1 课]](https://time.geekbang.org/column/article/224559) 中的广义Serverless的图吗?基础设施中的容器,一般情况下指的就是Docker容器。Docker 这个技术你肯定或多或少已经用过了,使用它们可以将应用代码和代码依赖的Runtime一起打包成一个Docker镜像。这个Docker镜像,可以在云上、自己的笔记本电脑、同事的电脑上无畅运行,并且完全不用担心环境依赖的问题,因为我们应用的依赖也打包在一起了。
|
||||
|
||||
到这里,我们又引入了Docker的概念,Docker出现之后,CaaS(Container as a Service)很快也就流行起来了。为了帮助你理解IaaS、PaaS、BaaS、CaaS这几者的关系,我画了张图,希望能从云服务发展的角度,帮你梳理下脉络。
|
||||
|
||||
<img src="https://static001.geekbang.org/resource/image/f0/3e/f0d74468ff49c43c9367fff9cc58833e.png" alt="" title="云服务发展进程">
|
||||
|
||||
图片很好理解,我就不解释了。不过这里要解决一个你可能会有的疑惑:为什么BaaS的出现,比Serverless FaaS还要早三年?那是因为早期出现的BaaS其实是mBaaS(移动后端即服务),这概念当时也曾经火过一段时间,现在已经被Google收购的FireBase其实就是做的这个生意。mBaaS设计之初是专门为移动端提供后端服务的,例如用户管理、角色服务、文件存储服务等等。除了服务对象是移动端之外,跟我们说的BaaS概念很像。移动端可以将BaaS的所需鉴权算法放到客户端中,并随着客户端的版本定期更新秘钥。但前端却做不到,因此mBaaS只局限在移动端,没有火起来。直到FaaS的出现,才诞生了BaaS的使用场景,mBaaS也开始转型,支持更广范围的前端场景了。
|
||||
|
||||
VM是一种虚拟化技术,这个我们都知道,其实Docker也是一种虚拟化技术,只不过它是利用新版Linux内核提供Namespace、Cgroups和Union File System特性,模拟操作系统的运行环境。它跟虚拟机VM最大的区别在于,Docker是进程模型的。怎么理解这句话呢?我们需要画一张图。
|
||||
|
||||
<img src="https://static001.geekbang.org/resource/image/2f/18/2f941369b366fff8883499d200cccb18.png" alt="" title="Docker进程模型">
|
||||
|
||||
从图中我们可以看出,虚拟机是在宿主机上启动一个虚拟机监视器HyperVisor管理控制虚拟机的。而虚拟机自身包含整个客户操作系统、函数库依赖和用户的应用,虚拟机操作系统之间相互都是隔离的。经典的HyperVisor就是VirtualBox[1],你感兴趣的话可以下载体验一下。你也可以试想一下,如果我们我们一台虚拟机部署一个微服务,其实资源利用率是很低的。
|
||||
|
||||
容器实例则只包含函数库依赖和用户的应用,操作系统是依赖宿主机的用户态模拟的,也就是说容器之间是共享宿主机内核的。所以Docker更加轻量,启动速度更快,代码执行速度也更快。
|
||||
|
||||
Docker的容器呢,因为只包含函数库依赖和用户的应用,可以部署到任意Docker引擎上,而Docker引擎呢,可以安装在你的个人电脑、公司机房或者云上。通常我们容器移动时,是移动容器的硬盘快照,内存中的状态我们比较难复制,这个硬盘快照就是Docker镜像。我们给Docker的镜像打上标签,标签就像是镜像的唯一标识符URI,打上后它就可以到处移动了。这个也是Docker的核心概念:build、ship、run。
|
||||
|
||||
其实你会不会觉得,Docker的这个层级结构有些眼熟?是的,这正是我们[[第 2 课]](https://time.geekbang.org/column/article/226574) 中讲的FaaS分层。我们回想一下,我当时所说的操作系统容器就是Docker模拟的,Runtime其实就是Bins/Libs层。云服务商的冷启动加速,就是利用Docker镜像缓存加速。我想你也应该猜到了,其实很多云服务商FaaS和PaaS的底层技术就是容器即服务CaaS。
|
||||
|
||||
那么接下来,我们就体验一下Docker的便利吧。我们的“待办任务”Web服务,只要添加一个文件,就可以让它变成Docker镜像了。
|
||||
|
||||
## Docker再探
|
||||
|
||||
这里我建议你,最好自己在电脑上安装体验一下Docker。在主流的操作系统数安装Docker都不难的,只需要下载安装包就可以了。
|
||||
|
||||
### build: Dockerfile
|
||||
|
||||
构建是Docker最重要的一环。Docker镜像是硬盘快照,那我们就看看标准的Docker硬盘快照如何制作吧。下面就是我们在代码根目录下增加的文件,供你参考:
|
||||
|
||||
```
|
||||
# FROM是指我们的镜像基于哪个镜像来构建,我们这里是基于jessie linux的Node.js镜像
|
||||
FROM registry.cn-shanghai.aliyuncs.com/jike-serverless/nodejs
|
||||
# ENV是设置环境变量
|
||||
ENV HOME /home/myhome/myapp
|
||||
# RUN是执行一段命令
|
||||
RUN mkdir -p /home/myhome/myapp/public
|
||||
# COPY是将当前目录下的内容拷贝到容器中
|
||||
COPY . /home/myhome/myapp
|
||||
COPY public /home/myhome/myapp/public/
|
||||
COPY node_modules /home/myhome/myapp/node_modules/
|
||||
# WORKDIR是设置进入容器后的工作目录
|
||||
WORKDIR /home/myhome/myapp/
|
||||
# ENTRYPOINT是容器启动后执行的脚本
|
||||
ENTRYPOINT [ "node","index.js" ]
|
||||
|
||||
```
|
||||
|
||||
通常我们使用Docker前,先去Docker Hub上,找合适的基础镜像。看看基础镜像上都安装了哪些函数库或二进制包,再对比一下要运行自己的应用还缺少哪些函数库和二进制包。在基础镜像的层级上,加上自己的依赖。这样我们就构建好适合自己的镜像了。以后我们就能FROM自己的基础镜像,构建自己的应用了。
|
||||
|
||||
然后你在项目下执行:`docker build` 命令,就可以帮你构建Docker镜像了。
|
||||
|
||||
```
|
||||
docker build --force-rm -t myapp -f Dockerfile .
|
||||
|
||||
```
|
||||
|
||||
构建好的镜像,我们可以通过 `docker run` 这个命令在本地调试。
|
||||
|
||||
```
|
||||
docker run -d -p 3001:3001 -e TEST=abc myapp:latest
|
||||
|
||||
```
|
||||
|
||||
然后我们通过浏览器访问[http://127.0.0.1:3001](http://127.0.0.1:3001),就可以看到我们刚刚构建的Docker内容了。你会发现,这不就是我们云上运行的版本吗?是的,既然FaaS是用CaaS技术实现的。我们当然也可以利用Docker实现我们的“待办任务”Web服务。
|
||||
|
||||
为了方便Docker例子的展示,这节课的项目代码index.js,包含了rule.js的逻辑。你会发现index.js中,我们启动的时候,不用关心初始化需要等待多少秒了,而是直到初始化完成后,才监听3001端口,而Node.js连接数据库的时间通常也只需要几十毫秒。
|
||||
|
||||
<img src="https://static001.geekbang.org/resource/image/9f/c4/9f7572a1ee36d167d30940f08d96ccc4.png" alt="" title="Docker版本的“待办任务”Web服务">
|
||||
|
||||
另外为了方面你观察和调试Docker容器实例,我这里给出一个登录Docker容器实例的命令。
|
||||
|
||||
```
|
||||
docker exec -it 容器ID bash
|
||||
|
||||
```
|
||||
|
||||
### ship: Docker Registry
|
||||
|
||||
本地调试完,我们再看看如何部署到云上。Docker官方的镜像仓库[2]速度太慢,现在的云服务都支持私有的容器镜像仓库[3],上面构建Dockerfile里面的Node.js镜像,就是用我自己的私有容器仓库搭建的。
|
||||
|
||||
你可以登录云服务的容器镜像服务,他们都会告诉你如何 `Docker login` 到私有Registry,如何打标签,以及如何上传。
|
||||
|
||||
用我们的例子来说,大致会有下面的命令。
|
||||
|
||||
未登录过的话,要先登录Registry。
|
||||
|
||||
```
|
||||
docker login --username=极客时间serverless registry.cn-shanghai.aliyuncs.com
|
||||
|
||||
```
|
||||
|
||||
根据容器镜像仓库的URI,打标签。镜像ID可以通过 `docker images` 查看。
|
||||
|
||||
```
|
||||
docker tag 你本地的镜像ID registry.cn-shanghai.aliyuncs.com/jike-serverless/todolist
|
||||
|
||||
```
|
||||
|
||||
上传镜像到私有的Registry仓库。
|
||||
|
||||
```
|
||||
docker push registry.cn-shanghai.aliyuncs.com/jike-serverless/todolist
|
||||
|
||||
```
|
||||
|
||||
### run: Docker Engine
|
||||
|
||||
运行Docker镜像就很简单了,云服务都支持容器服务CaaS。你只需要购买好容器服务,就可以从自己私有的容器镜像仓库Docker Registry中下载镜像,并运行了。你要是执行过上面的例子,那相信你也能体会到为什么FaaS的冷启动速度这么快了。不过需要提醒你一下,这里会产生费用。
|
||||
|
||||
<img src="https://static001.geekbang.org/resource/image/b0/32/b0f0839d924252943ab9cd67df923b32.png" alt="" title="运行Docker镜像示意图">
|
||||
|
||||
另外,我们的专栏是讲Serverless,但是为了让你理解Serverless底层的实现,所以我们也讲到了Docker容器的部分内容。对于我们专栏来说,你不用尝试部署云上容器了。
|
||||
|
||||
恭喜你,获得DevOps奖章一枚!
|
||||
|
||||
现在我们了解了容器,也知道了FaaS是构建在容器上的。我们的微服务和整个应用都可以部署在CaaS之上,容器对我们来说更加可控,因为我们可以通过Dockerfile安装我们需要的各种函数库依赖或者二进制文件。但这里还有一个问题,FaaS的扩缩容是怎么做到的呢?我们如果采用了容器,容器如何做到扩缩容呢?
|
||||
|
||||
## FaaS与Docker的扩缩容
|
||||
|
||||
FaaS中的扩缩容,我们可以通过云控制台看到在FaaS函数配置中的“单实例并发度”。FaaS的做法比较简单粗暴,需要我们告诉函数服务,我们的单个函数实例可以承载多少的并发量,如果事件触发并发量超出了这个并发度,则会自动扩容。扩容的数量N,就是这个事件触发量除以单机并发量取整。例如我们设定,我们rule-faas函数的单实例并发度为10,那么当用户并发量是11时就会扩容2个实例。如果用户并发量达到100,就会扩容出10个实例。
|
||||
|
||||
这种做法其实是比较机械式的,我们再看看FaaS的“函数指标”监控图。你有没有想到,我们其实可以利用实时监控,去控制扩缩容?例如当单个函数实例承受不了时,内存使用率会越来越高,它的执行时间也会越来越长。
|
||||
|
||||
<img src="https://static001.geekbang.org/resource/image/5e/2c/5e4d42ca0b4f27dbf04d91c31fa8f62c.png" alt="" title="FaaS的“函数指标”监控图">
|
||||
|
||||
是的,我们在使用Docker时,要考虑的就是:监控指标metrics以及扩容水位。
|
||||
|
||||
监控指标metrics就像FaaS“函数指标”监控图那样,是一系列需要我们关心的单个容器核心指标,包括CPU利用率、内存使用率、硬盘使用率、网络连接数等等。
|
||||
|
||||
我们将监控指标中的各项数值想象成蓄水,我们监控就像一个蓄水池,一旦某一项超过了蓄水池,水就会溢出。所以,我们要设定**水位**告警。我们在蓄水池里面画上刻度,当水位到这个刻度,我们就马上给这个蓄水池扩容。
|
||||
|
||||
<img src="https://static001.geekbang.org/resource/image/ab/ba/abf169adcc352cc0c37c6e66b710c6ba.png" alt="" title="蓄水池案例">
|
||||
|
||||
Docker容器本身并不提供扩缩容机制,但我们要让Docker自动化扩缩容,就可以用监控指标和水位去设计扩缩容机制。我们需要实时监控容器状态,当容器状态的某一项数值过高时,我们就需要给容器扩容。FaaS的弹性做法很简洁,而Docker的扩缩容机制弹性更高、更加可控。但是Docker容器,通常需要我们至少保持1个容器实例在线。相信你也知道了FaaS的预留实例是怎么做到的了吧?
|
||||
|
||||
讲到这里,不知道你发现没有,我们BaaS化的三讲,已经默默地实现了微服务的10要素。这也是为什么我一直说BaaS化和微服务高度重叠。
|
||||
|
||||
我们再来回顾一下微服务10要素。其中,API、服务调用、服务发现,我们可以通过RESTful HTTP接口实现;日志、链路追踪,我们没有展开,但我们可以依赖云服务商提供的日志采集解决;鉴权我们可以用跨域认证JWT解决;发布管道,需要我们搭建流水线发布管道;容灾性、监控、扩缩容,我们可以通过实时监控和扩缩容解决。到这儿呢,我们的高铁车厢也终于完成了。
|
||||
|
||||
这节课的内容挺多的,需要你动手消化吸收一下。下节课我们再看看,如何利用K8s调度我们的Docker容器。
|
||||
|
||||
## 总结
|
||||
|
||||
BaaS化的内容,到今天,我们用了三节课讲完了,现在我们一起回顾一下。
|
||||
|
||||
我们讲后端应用BaaS化的问题,转换为后端应用NoOps微服务。所以我们先了解了微服务的10要素,并且看到微服务中如何通过消息队列将Stateful的节点,变成Stateless的。在微服务的拆解过程中,我们学习了微服务的拆解思想DDD和动态网络演进,以及拆解后带来的信任问题,需要我们加密身份认证。合并时,除了代码里面编排HTTP请求结果,我们还学习了事件流触发获取结果。为了让微服务能够快速迭代,我们还需要自己搭建流水线发布管道。
|
||||
|
||||
这节课我们通过FaaS和BaaS的初始化问题,向你介绍了FaaS和BaaS依赖的底层实现容器Docker。Docker也是一种虚拟化技术,它的核心理念是:build、ship、run。通过将我们的应用代码和应用依赖的函数库、二进制包,打包在一起的方式,解决应用开发环境和运行环境的一致性问题。我们可以借助Docker容器维持我们的应用实例,来解决初始化慢的问题。
|
||||
|
||||
我们还了解了FaaS的扩缩容逻辑是通过用户配置的“函数初始化入口”,以及固定的“初始化超时时间”配合“单实例并发度”来实现的。而我们在容器Docker,其实可以采用实时监控配合扩容水位,来做到更加弹性的扩缩容策略。
|
||||
|
||||
接下来我会通过K8s实践我们目前所学到的内容,我们的“待办任务”Web服务,将在我们本地搭建的CaaS环境和Serverless环境中开发和调试。
|
||||
|
||||
## 作业
|
||||
|
||||
首先,拉取我们[lesson07](https://github.com/pusongyang/todolist-backend/tree/lesson07)的代码,为“待办任务”部署的rule-faas函数添加初始化入口。
|
||||
|
||||
这节课的作业呢,就是我们要在本地完全通过Docker容器,搭建起我们的“待办任务”Web服务。除了css和js静态资源是来自CDN,其它内容都将运行在Docker容器里。
|
||||
|
||||
相信你可以通过这个作业,体验到FaaS的底层CaaS的运行机制。
|
||||
|
||||
当然如果你有预算,也可以将Docker镜像上传到云服务商的Registry,在云上购买容器服务就可以部署你的Docker镜像,并在云上运行我们的“待办任务”Docker版本了。这样你就拥有了一个永不停机的Docker服务。
|
||||
|
||||
另外,我也希望你可以帮助我继续优化我们的课程作业代码。如果你有更好的建议,也可以通过Github的MergeRequest告知我。
|
||||
|
||||
接下来就期待你的作业和建议了。如果今天的内容让你有所收获,也欢迎你把文章分享给身边的朋友,邀请他加入学习。
|
||||
|
||||
## 参考资料
|
||||
|
||||
[1] [https://www.virtualbox.org/](https://www.virtualbox.org/)
|
||||
|
||||
[2] [https://docs.docker.com/get-docker/](https://docs.docker.com/get-docker/)
|
||||
|
||||
[3] [https://hub.docker.com/](https://hub.docker.com/)
|
||||
Reference in New Issue
Block a user