Serverless
第1讲 Serverless介绍
1.1 历史
分为四个时代: 物理机时代、虚拟机时代、容器时代、Serverless时代
1.物理机时代
简单理解: 手动部署服务器、网站的过程;
缺点:
1.断电、断网容易导致服务中断;
2.出现硬件烧毁, 无法保证新环境与原来一致, 网站的持续可用,各种问题接连产生…;
3.承担服务器购买、场地、电力、网络、运维开销;
2.虚拟机时代
为解决物理机时代问题, 经历了虚拟机时代(不再关心硬件底层, 关心运行环境)。
简单理解: 在云平台购买虚拟机/数据库;
优点:
1.不用担心断电断网, 硬件故障;
2.成本更低, 无需运维;
3.云数据库做了性能优化, 承载千万级别写入;(且有异地容灾);(计算和存储分离,提升数据安全性);
4.硬盘可无限扩展;缺点:
1.服务器型号一直迭代更新,下一次购买可能已经没上次的型号,只有新型号;
2.新的服务器都要初始化环境和配置;名词解释:
laaS(基础设施即服务) - 卖云服务器
PaaS(平台即服务) - 云服务厂商卖的一些通用平台,如: 中间件、数据库
3.容器时代
2013年Docker的发布, 代表容器技术替代了虚拟化技术
,云计算进入容器时代
。
简单理解: 容器就是讲代码和运行环境一起打包, 这样代码就可以在任何地方运行。有了容器技术,
在服务器部署的不再是应用了,而是容器。但容器多了,如何管理成了问题,于是出现了容器编排技术,
比如2014年Google开源的Kubernetes。
自此,你不仅使用了容器,你还使用了 Kubernetes 来做管理容器集群。 基于 Kubernetes 和云厂商提供的弹性能力,
你可以实现网站的自动弹性伸缩。这样在流量洪峰到来时,就可以自动弹出更多的资源;当流量低谷时,自动释放多余的资源
优点:
1.新的服务器不在需要初始化环境和配置;
2.可以使用主流K8S服务提供商的服务, 方便管理硬件(如 EKS (Amazon Elastic Kubernetes Service) 和 ACK(阿里云容器服务));
缺点:
1.时间一久,需要去规划节点和 Pod 的 CPU、内存、 磁盘等资源,需要编写复杂的 YAML 去部署 Pod、
服务,需要经常排查 Pod 出现的异常,渐渐地, 你好像变成了 Kubernetes 运维工程师;
2.如果前期,服务器资源准备不足,如双十一来了, 瞬时流量太大,集群虽然感知到了需要弹出更多资源,
但由于服务器弹出需要一定时间,会没来得及反应这种瞬时流量。
对此的解决方案:
一、秒级别的弹性, Serverless;
二、定时扩容: 公交类公司,可以指定一个定时伸缩策略, 每天早晨 5 点将集群规模扩大 x 倍,xx 业务规模扩大 y 倍, 11 点再缩容至原规模,下午 4 点再进行扩容,以此类推。
3.k8s提供分钟级的弹性, 而不是秒级
名词解释:
Pod - 一个或多个容器的组合。 这些容器共享存储、网络和命名空间,以及如何运行的规范。可以理解为一个小主机.;
基于容器, 部署网站:
1.搭建 Kubernetes 集群;
2.构建容器镜像;
3.部署镜像;
4.Serverless
发展阶段,局限还是很大。
此前三者属于Serverful架构,Serverless指构建运行不需要服务器的架构概念, 但这并不代表应用运行不需要服务器,而是开发者不关心服务器。
简单理解: FaaS(函数即服务) + BaaS (后端即服务)。
FaaS能够运行开发者写的函数代码(处理业务逻辑, 调用BaaS), 每次会生成一个新的空间运行函数,运行完销毁。
BaaS就是正常些后台, 读写数据库、文件存储等;优点:
1.FaaS平台能实现业务的秒级弹性,理论上没有并发上限。
2.开发者只关心业务开发, 不关心服务器。
3.节省成本,按函数的运行次数和消耗CPU、内存等资源收费。
4.FaaS和BaaS是一种计算和存储分离的架构。缺点:
1.过度依赖第三方厂商服务(向切换云厂商要做好适配);
2.代码关注底层硬件的话, 会受影响;
3.函数通信效率低(只能通过HTTP通信/现在也有事件总线通信), 不像应用那样共享内存变量;
4.调试困难, 各个厂商各玩各的也不统一;
5.总结
- 物理机时代:2000 年之前,我们需要通过物理机部署网站。
- 虚拟机时代:2000 年之后,虚拟化技术发展成熟,不再关注物理机, 云服务提供稳定性。
- 容器时代:2013 年云计算进入容器时代,不再关心运行环境。
- Serverless 时代:云计算进入 Serverless 时代,不再关心服务器和运维工作,应用也天然具有弹性。专注业务开发。
问题:
1.Serverless依赖厂商?
答: 我们使用 Serverless 有两个途径,一是使用公有云 Serverless 产品,二是自己私有化部署 Serverless 平台。
即可以在自己服务器集群(支持秒级弹性)上部署Serverless平台,但这样需要自己维护底层机器,运维成本和经济成本都很高。
2.为什么需要一个网管承接用户流量?
答: 基于网关,就可以实现你说的负载均衡以及流量控制、白黑名单、权限校验等复杂功能了。
3.Serverless会取代k8s时代, 成为下一代主流?
答: 肯定的, 因为目前使用 k8s 的成本还比较高,还需要开发者自己去维护集群,编写繁多的 YAML 来部署应用。
K8s是按资源付费的,而不是实际使用。成本和效率一直是开发者和企业关注的问题。
4.SaaS没有提及?
答: 好问题。因为是按照应用部署架构演进的时间线来介绍的,而 SaaS 的话,是厂商直接提供软件服务,用户花钱购买使用,也就不用自己设计网站架构了,所以没有拿出来介绍,刚好借此问题简单补充一下。使用 SaaS 的优点就是方便简单,缺点也很明显,定制差。SaaS 在云计算早起就出现了,第一家通过互联网提供应用程序的公司是 Salesforce.com,它在 1999 年就成了。
5.Faas弹性扩容针对业务层,但实际web应用开发中,性能瓶颈在Backend ,如何弹性扩容?
答: 我理解 Backend 应该指的是云厂商提供的 BaaS?BaaS 性能瓶颈,的确是现在基于 FaaS 和 BaaS 这种 Serverless 实现面临的一个问题。基于 FaaS 我们可以实现计算资源的自动弹性伸缩,而 BaaS 多种多样,存储的弹性伸缩面临的挑战也更大。使用 BaaS,我们就需要依赖 BaaS 服务的能力,有些 BaaS 比如 DynamoDB、表格存储等 NoSQL 都支持自动弹性扩容,而 RDS 则不具备弹性能力,这时就需要人工根据业务流量进行容量规划了。
6.开源的serverless解决方案?
现在开源的 Serverless 解决方案有很多,比如 OpenWhisk、Fission、Kubeless、OpenFaaS、Fn 等。
OpenWhisk 起源于2016年2月,是由 IMB 开发的,IBM 的 Cloud Functions 就是基于OpenWhisk 实现的。后来 IMB 将 OpenWhisk 捐赠给了 Apache 基金会。OpenWhisk可以运行在物理机、虚拟机和 kubernetes 等多种不同的基础架构上。 OpenWhisk 是用 Scale 编写的。
Fisson 起源于 2016年 11 月,是 Platform9 公司的 Soam Vasani 及少数几位工程师开源的 Serverless 解决方案。Fisson 运行在 kubernetes 集群上。通常基于 Docker 的 FaaS 冷启动要 200 毫秒左右,而 Fisson 通过预热容器、容器重用等技术,实现了低于 100毫秒的冷启动。Fisson 是用 Go 编写的。
Kubeless 起源于 2016 年 8 月,是纯开源的软件。和 Fisson 一样,kubeless 也是运行在运行 kubernetes 的,kubernetes 使用了很多 Kubernetes原生的组件,如 Service、 Ingress、 HPA( Horizontal Pod Autoscaler)等。Kubeless 是用Go 编写的。
OpenFaaS 起源于 2016 年 12 月,最初是由一个 Alex Ellis 编写的,主要特点是简单易用,并且支持 Kubernetes 和 Docker Swarm。OpenFaaS 也是用Go 编写的。
7.多云问题怎么解决?
答: 通过适配器模式抹平云厂商API差异, 将业务代码抽离。
8.什么历史原因导致Serverless的开始?
答: 后台接口太过纯粹, 导致一个页面请求多个接口,前端需要通过 BFF 聚合成一个接口处理业务,
但无论是部署 BFF
或者 服务端渲染应用
, 都要处理 购买机器、 安装环境,甚至考虑负载均衡、分布式缓存、流量控制等
复杂后端问题,而 Serverless
把这些能力封装成服务,让你开箱即用,解决你不会服务器运维的困难。
2.学习路径图
3.Serverless应用
3.1选择FaaS平台
从表格中,你可以总结出这样几点信息。
FaaS 平台都支持 Node.js、Python 、Java 等编程语言;
FaaS 平台都支持 HTTP 和定时触发器(这两个触发器最常用)。此外各厂商的 FaaS 支持与自己云产品相关的触发器,函数计算支持阿里云表格存储等触发器;
FaaS 的计费都差不多,且每个月都提供一定的免费额度。其中 GB-s 是指函数每秒消耗的内存大小,比如1G-s 的含义就是函数以 1G 内存执行 1 秒钟。超出免费额度后,费用基本都是 0.0133元/万次,0.00003167元/GB-s。所以,用 FaaS 整体费用非常便宜,对一个小应用来说,几乎是免费的。
总的来说,国外开发者经常用 Lambda,相关的第三方产品和社区更完善,国内经常用函数计算,因为函数计算使用方式更符合国内开发者的习惯。
3.2阿里云demo
2.添加函数(使用HTTP触发器)
3.测试
curl https://1853774509549073.cn-hangzhou.fc.aliyuncs.com/2016-08-15/proxy/myserverless.LATEST/test01/?name=Serverless
// OUTPUT: Hello Serverless!
4.如果想在页面请求,需添加域名
添加域名
5.基础知识
- index.handler是入口函数,所以一般会将业务逻辑拆到入口函数外
// logic.js
exports.sayHello = function (name) {
return `Hello, ${name}!`;
}
// index.js
const logic = require('./logic');
exports.handler = (request, response, context) => {
// 从 request 中获取
const { name } = request.queries;
// 处理业务逻辑
const message = logic.sayHello(name)
// 设置 HTTP 响应
response.setStatusCode(200);
response.setHeader("Content-Type", "application/json");
response.send(JSON.stringify({ message }));
}
- node代码的具体使用可以看文档(比如上面handler函数的参数)
- 触发器
- HTTP触发器
在众多 FaaS 平台中,函数计算直接提供了 HTTP 触发器,HTTP 触发器通过发送 HTTP 请求来触发函数执行,一般都会支持 POST、GET、PUT、HEAD 等方法。所以你可以用 HTTP 触发器来构建 Restful 接口或 Web 系统。
- API网关触发器
API 网关触发器与 HTTP 触发器类似,它主要用于构建 Web 系统。本质是利用 API 网关接收 HTTP 请求,然后再产生事件,将事件传递给 FaaS。FaaS 将函数执行完毕后将函数返回值传递给 API 网关,API 网关再将返回值包装为 HTTP 响应返回给用户。
- 定时触发器
定时触发器就是定时执行函数,它经常用来做一些周期任务,比如每天定时查询天气并给自己发送通知、每小时定时处理分析日志等等。
4.Serverless是如何运行的
4.1 函数调用链路
可以看到有同步和异步的调用, 在执行异步操作的时候,
是通过排队列的方式, 所以当实例太多(同时在线实例最多100个),
部分实例运行可能延迟。
可通过SKD的方式在Node代码控制同步/异步。
4.2 生命周期
整个函数的运行过程可以分为四个阶段。
- 下载代码: FaaS 平台本身不会存储代码,而是将代码放在对象存储中,需要执行函数的时候,再从对象存储中将函数代码下载下来并解压,因此 FaaS 平台一般都会对代码包的大小进行限制,通常代码包不能超过 50MB。
- 启动容器: 代码下载完成后,FaaS 会根据函数的配置,启动对应容器,FaaS 使用容器进行资源隔离。
- 初始化运行环境: 分析代码依赖、执行用户初始化逻辑、初始化入口函数之外的代码等。
- 运行代码: 调用入口函数执行代码。
一般冷启动后会存活几分钟, 如果继续调用会沿用该存活实例,
也就是上面的热启动
,热启动
的耗时就完全是启动函数的耗时了,
只会执行启动函数,其他文件函数会沿用之前的!!
4.3 特性
- 实例的默认超时时间为60s
5.如何提高应用开发调试和部署效率?
选择合理的开发框架进行调试和部署, 主流的开发框架: Serverless Framework
和 函数计算 Fun
。
5.1 Serverless Framework
1.安装
$ npm install -g serverless
$ serverless --version
Framework Core: 2.15.0
Plugin: 4.2.0
SDK: 2.3.2
Components: 3.4.3
2.账号设置
serverless config credentials --provider aws --key key --secret secret
- provider 具体的 Serverless 平台
- key AWS 账号的 aws_access_key_id
- secret AWS 账号的 aws_secret_access_key
3.应用配置
初始化项目
$ serverless create --template aws-nodejs
Serverless: Generating boilerplate...
_______ __
| _ .-----.----.--.--.-----.----| .-----.-----.-----.
| |___| -__| _| | | -__| _| | -__|__ --|__ --|
|____ |_____|__| \___/|_____|__| |__|_____|_____|_____|
| | | The Serverless Application Framework
| | serverless.com, v2.15.0
-------'
Serverless: Successfully generated boilerplate for template: "aws-nodejs"
Serverless: NOTE: Please update the "service" property in serverless.yml with your service name
$ ls
handler.js serverless.yml
Serverless Framework 也是通过 YAML 配置文件来定义应用和函数的,其 YAML 格式如下:
service: myservice
frameworkVersion: '2'
provider:
name: aws
runtime: nodejs12.x
functions:
hello:
handler: handler.hello
events:
- http:
path: users/create
method: get
- websocket: $connect
- s3: ${env:BUCKET}
resources:
Resources:
NewResource:
Type: AWS::S3::Bucket
Properties:
BucketName: my-new-bucket
Outputs:
NewOutput:
Description: "Description for the output"
Value: "Some output value"
resources 的作用就是帮你创建或更新资源(如果资源不存在则创建,如果资源已存在则更新)。
4.应用调试
# 远程调试
$ serverless invoke --function hello
{
"statusCode": 200,
"body": "{\n \"message\": \"Go Serverless v1.0! Your function executed successfully!\",\n \"input\": \"\"\n}"
}
# 本地调试
$ serverless invoke local --function hello
{
"statusCode": 200,
"body": "{\n \"message\": \"Go Serverless v1.0! Your function executed successfully!\",\n \"input\": \"\"\n}"
}
5.应用部署
$ serverless deploy
Serverless: Packaging service...
Serverless: Excluding development dependencies...
Serverless: Service files not changed. Skipping deployment...
Service Information
service: myservice
stage: dev
region: us-east-1
stack: myservice-dev
resources: 6
functions:
hello: myservice-dev-hello
layers:
None
$ tree
|-- .serverless
|-- myservice.zip
|-- handler.js
|-- serverless.yaml
在将函数部署到 Lambda 之前,serverless 会先在本地将代码打包,最终代码是一个压缩包,路径为 .serverles/[serviceName].zip 。你也可以通过 serverless deploy function -f functionName 来单独部署某个函数。
5.2 函数计算 Fun(阿里云)
1.安装
$ npm install @alicloud/fun -g
$ fun --version
3.6.20
2.设置账号
$ fun config
? Aliyun Account ID *******
? Aliyun Access Key ID ******
? Aliyun Access Key Secret ******
? Default region name cn-hangzhou
$ cat ~/.fcli/config.yaml
endpoint: 'https://******.cn-hangzhou.fc.aliyuncs.com'
api_version: '2016-08-15'
access_key_id: ******
access_key_secret: ******
security_token: ''
debug: false
timeout: 10
retries: 3
sls_endpoint: cn-hangzhou.log.aliyuncs.com
report: true
enable_custom_endpoint: false
配置完成后,fun 会将账号信息存储在.fcli/config.yaml 文件中。除此之外,你也可以在项目根目录中使用 .env文件来配置账号信息,其优先级更高。
# 项目根目录 .env
ACCOUNT_ID=*****
REGION=cn-hangzhou
ACCESS_KEY_ID=******
ACCESS_KEY_SECRET=******
TIMEOUT=10
RETRIES=3
FC_ENDPOINT=******
ENABLE_CUSTOM_ENDPOINT=false
3.初始化项目
$ fun init event-nodejs12
$ ls -l
index.js template.yaml
template.yaml定义如下
ROSTemplateFormatVersion: '2015-09-01'
Transform: 'Aliyun::Serverless-2018-04-03'
Resources:
myservice:
Type: 'Aliyun::Serverless::Service'
Properties:
Description: 'helloworld'
hello:
Type: 'Aliyun::Serverless::Function'
Properties:
Handler: index.handler
Runtime: nodejs12
CodeUri: './'
其中 Resources 是表示资源的对象,对象的属性就是具体资源,对象属性中的Type 用来表示资源的类型。比如 Aliyun::Serverless::Service就表示是“服务”,Aliyun::Serverless::Function 表示函数。因此 template.yaml 中你可以定义多个服务,一个服务中也可以定义多个函数
4.调试
远程调试
$ fun invoke hello
using template: template.yml
========= FC invoke Logs begin =========
FC Invoke Start RequestId: c6abf471-949a-4bbc-9edf-35e4c312a974
load code for handler:index.handler
2020-12-06T14:19:11.353Z c6abf471-949a-4bbc-9edf-35e4c312a974 [verbose] hello world
FC Invoke End RequestId: c6abf471-949a-4bbc-9edf-35e4c312a974
Duration: 13.99 ms, Billed Duration: 100 ms, Memory Size: 128 MB, Max Memory Used: 17.30 MB
========= FC invoke Logs end =========
FC Invoke Result:
hello world
本地调试
fun local invoke function
// 本地调试。但本地调试前,你必须先安装 Docker,因为 fun 的本地调试原理是通过 Docker 在本地启动一个代码运行环境来执行代码,而不是直接模拟函数参数。这样的好处是,更接近 FaaS 平台的运行环境。
5.部署
$ fun fun deploy
using template: template.yml
using region: cn-hangzhou
using accountId: ***********4698
using accessKeyId: ***********QliF
using timeout: 10
Waiting for service myservice to be deployed...
Waiting for function hello to be deployed...
Waiting for packaging function hello code...
The function hello has been packaged. A total of 1 file were compressed and the final size was 318 B
function hello deploy success
service myservice deploy success
5.3 总结
- Serverless Framework 特点是功能完善、支持平台丰富。但由于 Serverless 平台尚且没有统一标准,所以支持多平台难度极大,也导致其对国内产品支持不够友好;
- Fun 的特点是只为函数计算服务,因此有很多针对函数计算的特定功能。
6.用nodejs开发如何安装依赖?(python、java的暂不讨论)
6.1 为什么安装依赖困难?
serverless运行环境由云厂商提供且预制,导致开发者只能进行有限的定制,
不好安装依赖。
6.2 如何解决?
解决:将全局依赖和项目依赖都安装到项目node_modules内, 一起打包
打包后可能出现新的问题↓
问题2: 代码体积太大,导致函数无法部署(函数计算最大100MB)
解决: 对代码进行treeShaking,去掉依赖里不必要的代码
FaaS中使用Node.js C++扩展等问题遇到再解决
7.FaaS通过自定义运行时运行其它语言
上图可知使用了Node.js12运行时来运行Node程序。这是官方内置的,我们也可以自己上传运行时。
不做展开, 具体示例可参考github
8.单元测试
将业务代码和FaaS代码分离,然后做node测试就好。node项目测试的可以看博客另一篇文章koa的
9.性能优化: 如何提升Serverless的应用性能?
Serverless在冷启动的时候耗时会比较大, 能否应对 低延迟
高并发
的场景?
9.1 函数的启动过程示意图
可以看到我们优化的空间主要是冷启动
9.2 链路追踪查看冷启动耗时
通过云平台的链路追踪,可以看到冷启动的耗时:
图中的主要信息有五点。
- InvokeFunction:函数执行总时间。
- ClodStart:是函数冷启动时间(如果函数是热启动的,不会有这个阶段)。
- PrepareCode:是函数冷启动过程中,下载代码或下载自定义镜像的时间。
- RuntimeInitialize:是执行环境启动的时间,包括启动容器和函数运行环境。
- Invocation:是执行函数的时间。
从调用链路中可以发现,本次执行函数是冷启动的,热启动的函数则没有ClodStart、 PrepareCode 和 RuntimeInitialize 这三个阶段。整个函数执行耗时 553ms,冷启动耗时 458ms,可见冷启动对函数性能影响还是很大的。
9.3 串行和并行请求使用的冷启动
串行,1个并发有顺序的请求100次,第一次冷启动之后都是热启动
并行,10个并发有顺序的请求100次,前10次冷启动之后都是热启动, 可以理解为并发的时候都没初始化完实例。
9.4 优化
- 避免函数冷启动
- 预热: 在高峰前预热API(提前初始化实例),但需要手动去释放创建函数的资源。
- 减小代码提及
- 不要加载不必要代码,压缩代码
- 提升函数吞吐量
- 为函数设置并发: 一个实例处理多个请求,单函数多并发
- 使用函数并发前
- 使用函数并发后
- 为函数设置并发: 一个实例处理多个请求,单函数多并发
- 选择合适编程语言
- 语言测试: Nodejs/python/PhP冷启动耗时小于JAVA, 且内存越大耗时越短
- 语言测试: Nodejs/python/PhP冷启动耗时小于JAVA, 且内存越大耗时越短
总结:
10.云服务访问权限控制
在大型团队开发中, 要限制不同成员能够访问的云资源, 可以通过云厂商的软件来控制, 如: 阿里云的RAM: Resource Access Manager
10.1 分权与授权
10.2 权限的工作原理
10.3 通过SDK授权的例子
10.4 授权的步骤
完整流程
通过 SDK 写代码实现访问权限控制: https://github.com/nodejh/serverless-class/blob/master/10/index.js
11. Serverless安全问题
11.1 开发者和厂商各自负责的安全问题
11.2 面临的挑战
11.3 主要的风险
由于事件多了, 导致攻击面变广
11.4 总结
12. 提高Serverless安全性
可以归纳为: 多关注云厂商提供的安全功能, 结合业务数据的加密
13. Serverless节省成本的方案
13.1 分析
阿里云1G服务器一个月大概53元, 如果部署两台大概106元, 这样比相对少了?
个人理解: 假设一个网站, 用户100个, 实际中用户经常会关闭再打开页面, 打开一次需要100个请求, 一天很容易就超10万了….,
感觉用来做混合APP全部接口/页面全部接口不实际…, 用来做中间件也纯粹多了一笔开销,
虽然服务器管理运维等需要人力成本, 但该部分人力成本并不单一, 他们在公司可能兼并测试、开发等职责。
可能更适合高并发场景(减少大量服务器资源)/少用户的场景(几乎免费)
13.2 成本优化
13.3 总结
14. Serverless实践经验 (工作)
14.1 怎么提升开发质量
14.2 怎么提升性能
14.3 怎么增强稳定性
14.4 怎么保障安全
14.5 怎么优化成本
14.6 总结
15. 实战: 登录注册功能
个人感觉: 和node写后台差不多, 只是数据库不同。mysql数据库需要维护连接池和重复连接, 所以不适用serverless,
所以选择使用表格存储
作为数据库。
使用jwt实现, Github: https://github.com/nodejh/serverless-class/tree/master/15/auth-app
16. 实践: 构建Retful API
17. 实践: SSR服务端渲染
18. 实践: 音视频
19. 未来展望
19.1 过去发展
让前端工程师回到软件工程师的职责
让后台工程师转向更底层的开发, 不再是CRUD
19.2 优势
19.2 2021年框架越来越多
19.3 国外使用增多
19.4 K8S新的方向
20. 传统项目迁移到Serverless
暂时用不到, snip...