无服务计算应用场景探讨及 FaaS 应用实战


作者 | 宋文龙(闻可) 阿里云全球技术服务部高级交付专家

什么是无服务计算

无服务器计算(Serverless Computing)在构建和运行应用时无需管理服务器等基础设施。它描述了一个细粒度的部署模型,在该模型中,应用被拆解为一个或多个细颗粒度的函数,在云端托管环境中被触发运行,然后根据需要执行、扩展容量并且计费。各大云厂商 Amazon、微软、Google、IBM、阿里云、腾讯云、华为云相继推出 Serverless 产品。

无服务计算本身是一个概念或者理论模型,落地到具体技术上主要有函数即服务(FaaS)以及后端即服务(BaaS)两种形式,阿里云提供函数即服务 FaaS 产品。

阿里云对于 FaaS 的定义如下:

函数计算是事件驱动的全托管计算服务。使用函数计算,您无需采购与管理服务器等基础设施,只需编写并上传代码。函数计算为您准备好计算资源,弹性地、可靠地运行任务,并提供日志查询、性能监控和报警等功能。

关于 FaaS 的详细介绍官方文档已经讲的很清楚,本文不再赘述。本文重点讨论无服务计算的应用场景以及应用实践。

无服务计算应用场景

1. 无服务计算的优势

无服务计算有很多优点,个人认为其中最主要的有三点:
  • 使用无服务计算,用户无需考虑基础设施,可以更加专注于业务逻辑;
  • 无服务计算支持弹性伸缩,按需使用,按量计费,非常适合流量不稳定、有明显波峰波谷的业务;
  • 无服务计算是云原生产品,可以无缝对接各种云上产品,作为黏合剂串联各种云原生产品。


下图说明了阿里云 FaaS 产品的核心优势:

1.png


2. 无服务计算应用场景

前面探讨了无服务计算的优势,那么无服务计算都有哪些典型的应用场景呢?

在此分享笔者之前在项目中构建无服务应用的两个案例:
  • 云端视频转码:
    • 技术方案:设备端捕获短视频,上传到云端存储(例如阿里云 OSS),云端存储监测到文件上传后触发无服务计算函数函数,函数中调用 FFmpeg 给视频加水印、加特效,将视频转码后再转存到云端存储上,同时通知业务服务更改视频状态。
    • - **方案优势**:设备端视频上传频率很不稳定,可能有一段时间所有设备空闲,也有可能某一段时间几千台设备同时上传,而视频转码是比较消耗资源的,如果通过普通服务器提供转码能力并要确保所有视频都能在预期时间内转码完成的话,必须按最高并发量配置服务器资源,这些服务器成本是相当可观的。即使云服务器采用 Auto Scalling 策略,也需常备部分服务器,无法做到不使用时零运行成本,而且云服务 Auto Scalling 触发及启动时间也是分钟级的,很难达到服务能力秒级快速伸缩。
  • 物联网设备监控:
    • 技术方案:设备端通过云原生 IoT 服务上报状态,云原生 IoT 服务收到消息后触发无服务计算函数,函数中对 IoT 原始消息做简单清洗后转发到后端服务做进一步业务处理,例如:发推送消息给设备管理 App,当然也可以通过函数将 IoT 消息转存到 NoSQL 数据库。
    • - **方案优势**:端侧设备活跃时间不固定,如果想通过其他后端服务监控设备状态, 该服务必须保持 7*24 小时在线,架构和应用都需要确保高可用。而且设备并发量也是不稳定的,要考虑服务伸缩架构。还有一点需要考虑的是,云端资源通过事件或者规则直接唤起无服务计算函数比价方便,只需要简单配置即可,而对接自定义服务则要复杂得多。


基于项目经历以及个人理解,笔者认为无服务计算主要有以下几种典型的应用场景:
  • 可分解成独立运行单元的 CPU 密集计算型任务;
  • 构建弹性伸缩 Web 后台应用;
  • 无缝粘合调度云上资源;
  • 快速低成本上线敏捷应用。


3. 无服务计算不适合做什么

前面列举了很多无服务计算的优点以及运用在一些场景中的好处,很多人肯定觉得无服务计算太好用了,太想用了,这个场景能否用无服务计算?那个业务能否用无服务计算代替?

这里笔者可能要先泼一瓢冷水:无服务计算很好用,但不一定处处好用。当然这瓢冷水以及接下来的一些总结只是基于个人理解, 欢迎拍砖。

个人认为无服务计算其实只适合应用在一些逻辑相对简单、外部依赖相对较少、不需要复杂编排治理、没有极致性能要求的业务,主要原因如下:
  • 无服务计算在服务编排以及服务治理方面是比较弱的,虽然主流云厂商都提供无服务计算的函数编排产品(例如阿里云的 Serverless 工作流,AWS 的 Step Functions 等),但想要实现函数的限流降级、调用链追踪、自动注册发现等比较复杂的编排治理以及可探测性还是需要做很多额外的工作,弥补这些不足所要做的工作远大于无服务计算本身节省的工作量。这点也是和无服务计算无需运维、配置简单、开箱即用的思想是背道而驰的。
  • 无服务计算应用运行所依托的底层服务器以及运行环境对用户是透明的,用户无法选择,也无法优化。例如在笔者之前的视频转码方案中,有些特效处理如果运行在 GPU 服务器上是可以大幅加速的,但是在无服务计算中只能依靠 CPU 计算。
  • 无服务计算程序运行的环境是高度标准化的,有些依赖于特定运行环境、特定服务器版本、甚至特定硬件资源的依赖是很难确保兼容性的。如果有些应用尤其是原有技术资产受到运行环境制约的话,尽量还是回归到传统的云服务器部署方案。
  • 无服务计算的弹性伸缩并非没有上限,和普通应用一样,无服务应用在实际生产系统中也需要考虑并发达到上限的情况。虽然所有厂商的无服务计算产品都能根据并发量弹性伸缩,但这个弹性本身还是靠底层硬件资源支撑的,一般来讲各厂商的无服务计算产品都会有默认的并发上限设置,虽然通过工单申请可以调整,但也不能调整为无限量。如果账号下有多个无服务应用,需要对不同应用做的流量分配或者限制,并且考虑某一应用访问流量达到并发上限的情况。


4. 无服务能否替代微服务

笔者认为这两者应该不是一个维度的概念,微服务更多的是一种服务构建的架构思想,而无服务更偏重于一种服务部署和运行的技术方案,例如:微服务架构中某一个接口是可以通过无服务方式实现的。

当然,涉及到具体技术层面,通过 SpringCloud 或者 Dubbo 对外提供 RESTful 接口服务,还是将无服务函数挂载到 API 网关下对外提供 RESTful 服务,这两种技术方案确实是可以放到一起比较的。至于选择哪种技术实现,个人认为主要取决于运用场景和业务复杂度,前面总结了无服务计算的优势和不一定那么好用的场景。下面是个人的建议:
  • 如果应用中需要复杂的服务编排、服务治理、调用链追踪、统一的配置管理、自动注册发现等能力,建议还是采用“传统的”微服务技术方案;
  • 相反,如果只需要构建一个“短平快”的应用,采用无服务计算方案会让你好体验到极大的便利。正如笔者接下来将要分享的案例,就是采用无服务技术构建一个简单的 Web 后台应用。


阿里云 FaaS 实战

前面谈了比较多抽象的概念和看法, 下面结合笔者经历的一个实际案例展示如何使用阿里云 FaaS 产品构建一个无服务应用,尤其是实现 FaaS 应用的自动构建、自动部署。

1. 项目背景介绍

某外企客户需要将一个为用户提供产品咨询的服务从某友商无服务平台迁移到阿里云 FaaS。这个应用原部署于友商海外平台,基于 Nodejs 开发,通过 API Gateway 暴露 RESTful 接口,数据存储采用友商云原生 NoSQL 数据库。

迁移改造主要涉及以下三部分工作:
  • 阿里云产品适配选型
  • 代码逻辑改造
  • 应用部署工具选择及脚本编写


其中第三部分构建、部署脚本改造是此次迁移工作的重点,接下来逐一展开说明。

2. 阿里云产品适配选型

友商的无服务计算及网关在阿里云上均有对应产品,在此不展开比较。

友商云原生 NoSQL 数据库为该平台独有产品,经过和客户讨论认为,完全可以用其他 NoSQL 产品代替,加之现有开发人员更熟悉 MongoDB 技术栈,最后数据存储选型为阿里云云原生 MongoDB。

以下为整体迁移方案示意图:

2.png


3. 代码逻辑改造

用户编写无服务应用代码时,原则上只需要关注代码逻辑即可,主体代码逻辑部分大体上差异不大,比较明显的差异主要体现在事件处理入口函数写法上。参考FaaS 代码示例改造针对 API 网关事件的处理函数:
module.exports.handler = function(event, context, callback) {    
var response = {};
callback(null, response);
}; 

4. 应用部署工具选择及脚本编写

  • 应用构建及部署工具选择


客户原来采用第三方工具 Serverless (https://www.serverless.com/) 实现无服务计算应用的构建和部署。Serverless 框架虽然宣称支持几乎所有主流云厂商的无服务产品,但经过调研后发现其对某友商支持是最全面的,有很多代码示例或者工程脚手架,几乎是开箱即用。但该工具对阿里云的支持比较有限,示例也相对较少。经过评估,我们决定采用阿里云自己的工具 Funcraft。

Funcraft 如何安装配置请参考产品文档,此处不展开讨论。接下来主要展示如何通过 Funcraft 脚本实现服务、函数、网关、日志、鉴权等相关资源的部署及配置。
  • 编写脚本


编写脚本前,先看一下目标系统的部署架构以及 Funcraft 要实现的功能蓝图:

3.png


Funcraft 脚本及配置说明详见以下代码:
ROSTemplateFormatVersion: '2015-09-01'
Transform: 'Aliyun::Serverless-2018-04-03'
Resources:
demoService: #资源名称, 根据需要命名
Type: 'Aliyun::Serverless::Service'
Properties: #属性设置
  Description: 'This is a faas demo ' #资源描述
  Policies:  #安全策略,系统自动根据策略为函数创建角色
    - AliyunOSSFullAccess
    - AliyunRAMFullAccess
    - AliyunLogFullAccess
    - AliyunApiGatewayFullAccess
    - AliyunFCFullAccess
    - AliyunMongoDBFullAccess
    - AliyunVPCFullAccess
    - AliyunECSNetworkInterfaceManagementAccess #特别说明,配置此策略才能创建弹性网卡进而打通函数以及VPC内连接
  VpcConfig: #允许函数访问的VPC配置
    VpcId: 'vpc-xxxxxx'
    VSwitchIds: ['vsw-xxxxxx']
    SecurityGroupId: 'sg-xxxxxx'
  LogConfig: #日志服务配置
    Project: sls-demo
    Logstore: logstore-demo
demoFunction: #函数名称
  Type: 'Aliyun::Serverless::Function' #资源类型为服务,服务内可以挂载多个函数
  Properties:
    Handler: index.handler #事件处理入口
    Runtime: nodejs10   #程序运行环境
    CodeUri: './src'  #程序代码相对于当前脚本的路径
    EnvironmentVariables:   #环境变量设置,
      MONGO_URL: mongodb://userx:pwdx@dds-xxx.mongodb.rds.aliyuncs.com:3717/demoDb #Mongo内网连接地址
      RESULT_TABLE_NAME: demo_table
demoGroup: # Api Group
Type: 'Aliyun::Serverless::Api' #资源类型为API,每个API分组下可以挂载多个API接口
Properties:
  StageName: RELEASE  #发布环境
  DefinitionBody:
    '/v1/recommendervera/[resultId]': # request path
      get: # http method
        x-aliyun-apigateway-api-name: demo_api # api name
        x-aliyun-apigateway-fc: # 当请求该 api 时,要触发的函数,
          arn: acs:fc:cn-shanghai:xxx:services/demoService.LATEST/functions/demoFunction
          timeout: 3000
        x-aliyun-apigateway-request-parameters: #设置参数类型
            - apiParameterName: 'resultId'
              location: 'Path'  #传参方式,此处为在URI请求路径中传餐
              parameterType: 'String'
              required: 'REQUIRED'  #设置为必选参数
    '/v1/recommendervera/': # request path
      post: # http method
        x-aliyun-apigateway-api-name: demo_api_post # api name
        x-aliyun-apigateway-fc: # 当请求该 api 时,要触发的函数,
          arn: acs:fc:cn-shanghai:xxxx:services/demoService.LATEST/functions/demoFunction
          timeout: 3000
        x-aliyun-apigateway-auth-type: APP  #设置鉴权类型,此处设置为简单的APP code类型鉴权
        x-aliyun-apigateway-app-code-auth-type: HEADER #鉴权加密方式,此处设置为通过Header传递授权后的app

  • 资源准备


服务、函数、API 网关都可以通过以上Funcraft 脚本一站式创建完成,日志、MongoDB、鉴权所需应用等外部资源需要提前创建或者配置好。

这里特别要说明的是:函数通过内网链接 MongoDB 时,需要通过配置允许函数访问 MongoDB 所在 VPC 及交换机,并为函数访问创建一个独立的安全组用于配置弹性网卡,并且将此安全组加入到 MongoDB 的白名单中,进而打通函数计算与 VPC 之间的网络联通。

前面提到的 VPC、交换机以及安全组已经提前创建好并配置在 Funcraft 脚本中了,下面看看在实际的 MongoDB 资源中如何配置白名单安全组:

4.png


5. 构建和部署

以上脚本资源都准备好后,就可以执行 fun build 命令进行构建了。构建时,工具会读取指定代码目录下的 package.json 文件加载相关插件依赖及配置。

构建执行命令示例:
$ fun build
using template: template.yml
start building function dependencies without docker
building demoService/demoFunction
running task: flow NpmTaskFlow
running task: CopySource
running task: NpmInstall
Build Success
Built artifacts: .fun/build/artifacts
Built template: .fun/build/artifacts/template.yml
Tips for next step
======================
* Invoke Event Function: fun local invoke
* Invoke Http Function: fun local start
* Deploy Resources: fun deploy

构建完成后, 执行 fun deploy 命令部署到云端。示例如下:
$ fun deploy
using template: .fun/build/artifacts/template.yml
using region: cn-shanghai
using accountId: ***********3452
using accessKeyId: ***********1fap
using timeout: 60
Collecting your services information, in order to caculate devlopment changes...
Resources Changes(Beta version! Only FC resources changes will be displayed):
┌──────────────┬──────────────────────────────┬────────┬──────────────────────┐
│ Resource     │ ResourceType                 │ Action │ Property             │
├──────────────┼──────────────────────────────┼────────┼──────────────────────┤
│              │                              │        │ Description          │
│              │                              │        ├──────────────────────┤
│              │                              │        │ Policies             │
│ demoService  │ Aliyun::Serverless::Service  │ Add    ├──────────────────────┤
│              │                              │        │ VpcConfig            │
│              │                              │        ├──────────────────────┤
│              │                              │        │ LogConfig            │
├──────────────┼──────────────────────────────┼────────┼──────────────────────┤
│              │                              │        │ Handler              │
│              │                              │        ├──────────────────────┤
│              │                              │        │ Runtime              │
│ demoFunction │ Aliyun::Serverless::Function │ Add    ├──────────────────────┤
│              │                              │        │ CodeUri              │
│              │                              │        ├──────────────────────┤
│              │                              │        │ EnvironmentVariables │
└──────────────┴──────────────────────────────┴────────┴──────────────────────┘
? Please confirm to continue. Yes
Waiting for service demoService to be deployed...
make sure role 'aliyunfcgeneratedrole-cn-shanghai-demoService' is exist
role 'aliyunfcgeneratedrole-cn-shanghai-demoService' is already exist
attaching policies ["AliyunOSSFullAccess","AliyunRAMFullAccess","AliyunLogFullAccess","AliyunApiGatewayFullAccess","AliyunFCFullAccess","AliyunMongoDBFullAccess","AliyunVPCFullAccess","AliyunECSNetworkInterfaceManagementAccess"] to role: aliyunfcgeneratedrole-cn-shanghai-demoService
attached policies ["AliyunOSSFullAccess","AliyunRAMFullAccess","AliyunLogFullAccess","AliyunApiGatewayFullAccess","AliyunFCFullAccess","AliyunMongoDBFullAccess","AliyunVPCFullAccess","AliyunECSNetworkInterfaceManagementAccess"] to role: aliyunfcgeneratedrole-cn-shanghai-demoService
attaching police 'AliyunECSNetworkInterfaceManagementAccess' to role: aliyunfcgeneratedrole-cn-shanghai-demoService
attached police 'AliyunECSNetworkInterfaceManagementAccess' to role: aliyunfcgeneratedrole-cn-shanghai-demoService
Waiting for function demoFunction to be deployed...
Waiting for packaging function demoFunction code...
The function demoFunction has been packaged. A total of 1675 files were compressed and the final size was 2.1 MB
function demoFunction deploy success
service demoService deploy success
Waiting for api gateway demoGroup to be deployed...
URL: GET http://xxx-cn-shanghai.alicloudapi.com/v1/recommender/[resultId]
  stage: RELEASE, deployed, version: 20200715144450426
  stage: PRE, undeployed
  stage: TEST, undeployed
URL: POST http://xxx-cn-shanghai.alicloudapi.com/v1/recommender/
  stage: RELEASE, deployed, version: 20200715144453967
  stage: PRE, undeployed
  stage: TEST, undeployed
api gateway demoGroup deploy success

脚本正常执行后,可以在云端看到相关函数及 API 已经部署完成并且可以正常运行了。

5.png


6.png


接下来可以将以上 Funcraft 命令及脚本可以加入到 Jenkins、Gitlab 等工具流水线中,结合代码版本管理工具实现函数应用的持续集成、持续发布。

总结

随着无服务计算产品和技术的进一步成熟,会有越来越多的用户选择无服务计算技术;随着阿里云的进一步发展壮大,也会有越来越多用户采用阿里云 FaaS 构建无服务应用。

本文探讨了无服务计算的应用场景,并且结合实际应用案例,讲解了如何用 FaaS 构建无服务 Web 后台应用,并实现自动构建、自动部署,希望以上探讨和经验能够帮到大家。

作者简介

宋文龙,花名闻可,阿里云全球技术服务部高级交付专家,有多年云原生应用开发架构经验以及多年高性能服务器研发经验。目前专注于运用阿里云中间件技术、结合阿里巴巴中台架构经验为客户构建符合行业需要的业务中台解决方案并完成交付。

Serverless 公众号,发布 Serverless 技术最新资讯,汇集 Serverless 技术最全内容,关注 Serverless 趋势,更关注你落地实践中的遇到的困惑和问题。

0 个评论

要回复文章请先登录注册