开篇
2020年11月份,阿里云智能开源了Serverless 社区的开发者工具Serverless Devs(后简称S) 弥补了国内在Serverless 开发者工具的一个空白。通过高度灵活的配置设定,实现了无厂商锁定的支持;直观易懂的可视化配套也带来了极致的开发者使用体验。
通过S你可以体验Serverless hello world 以及 构建生产级Serverless 应用 。你也可以尽情发挥自己的创意,在S 这个平台上为更多的开发者贡献自己的 应用和组件。本篇将会从整个技术架构视角为你揭开S的神秘面纱,希望通过本篇文章可以帮助你了解S的核心设计理念和实现的基本原理,在类似的可扩展应用设计中带来一些解题思路。
S外貌 - 功能结构全景展示
为了更方便的讲解,我们可以先看看S的全景图
如上图所示S 核心构成大致分成了5部分,他们分别是
1 - 核心启动器 ;
2 - GUI 图形界面 ;
3 - 核心组件库s/core和 实现fc部署的 s-framework ;
4 - 应用中心;
5 - 以FC 为基础的Serverless 服务接口。
其中 1,2,3是完全开源开放的,4,5则目前由阿里云云原生团队提供经费负责维护。
值的一提的是,S工具侧服务端应用以及S的社区接口服务都是依赖阿里云函数计算产品,稳定性,可靠性,安全,可观测等能力皆由其保障。
S整个服务侧的开发都专注在业务域,服务端的开发模式基本就是在线撸码,在线构建,在线发布,在线版本管理。 这样的模式为我们的快速迭代发布提供了有力的保障(从开始到第一个版本的研发结束时间至少缩短一周)
接下来的重点介绍会集中在1,2,3部分。在这之前,为了方便更好的理解S的执行过程,我们就先说一说S的灵魂-- 配置约定部分
S灵魂-配置约定
S的配置文件包括 应用/组件 , 秘钥信息,缓存信息。其中应用/组件 是最核心的部分,也是构成整个生态体系最主要的部分。
我们先看一个hexo博客应用的配置文件
HexoComponent: # 主体(唯一键)
Component: hexo # 组件名
Provider: alibaba # 供应商
Extends: # 扩展能力
deploy: # 针对deploy 的扩展
- Hook: npm install --production
Path: ./src
Pre: true
- Hook: npm run build
Path: ./src
Pre: true
Properties: # 属性配置
Region: cn-hangzhou
CodeUri: ./src
Detail:
Service:
Name: hanxiedemo
Access: hanxie #访问秘钥
S启动器可以解读这个配置然后把一个hexo 的博客系统部署到 函数计算上。这个配置本身没有任何处理逻辑,仅是资源和配置描述,他表达的是这样一件事:
主体HexoComponent ,利用 阿里云提供的 hexo 组件以及客户自身在阿里云的秘钥 解锁了部署本地博客应用FC产品的操作。此外,解锁这个部署操作需要的配套参数 Properties以及执行部署前的指令集也可以被描述进去完善部署操作本身。
在这样的范式定义下,配合真正执行逻辑的组件,即可完成各种复杂的操作。当然,软件应用的真实场景会比这个hexo应用要更复杂,可能会由多个具备独立业务逻辑的步骤而成,并且可能有前后的依赖,针对这种情况,我们来看一下如何配置:
Global: # 全局变量
Access: release
Region: cn-beijing
AccessKeyId: '${Env(AccessKeyId)}'
AccessKeySecret: '${Env(AccessKeySecret)}'
OSSConfBucketSourceName: serverlessbook-image-source
OSSConfBucketTargetName: serverlessbook-image-target
OSSConfObjectSignUrlTimeOut: 1200
SourceBucket: # 存储源主体
Component: oss
Provider: alibaba
Access: hanxie
Properties:
Region: '${Global.Region}'
Bucket: '${Global.OSSConfBucketSourceName}'
CodeUri: ./test
TargetBucket: # 存储目标主体
Component: oss
Provider: alibaba
Access: hanxie
Properties:
Region: '${Global.Region}'
Bucket: '${Global.OSSConfBucketTargetName}'
Acl: public-read
ServerlessBookImageDemo: # 执行转化逻辑主体
Component: fc # 执行转化逻辑详情
Provider: alibaba
Access: hanxie
Extends:
deploy:
- Hook: s ServerlessBookImageDemo install docker
Src: ./src
Pre: true
Properties:
Region: '${Global.Region}'
Service:
Name: ServerlessBook
Description: Serverless图书案例
Log:
Project: aliyun-fc-cn-beijing-9bd4a65b-c9d1-529d-9ce0-c91adfb823ab
LogStore: function-log
Function:
Name: serverless_image
Description: 图片压缩、水印
CodeUri: ./src
Handler: index.handler
MemorySize: 128
Runtime: python3
Timeout: 5
Environment:
- Key: AccessKeyId
Value: '${Global.AccessKeyId}'
- Key: AccessKeySecret
Value: '${Global.AccessKeySecret}'
- Key: OSSConfBucketSourceName
Value: '${SourceBucket.Output.Bucket}'
- Key: OSSConfBucketTargetName
Value: '${TargetBucket.Output.Bucket}'
- Key: OSSConfEndPoint
Value: '${SourceBucket.Output.Endpoint.Publish}'
- Key: OSSConfObjectSignUrlTimeOut
Value: '1200'
Triggers:
- Name: OSSTrigger
Type: OSS
Parameters:
Bucket: '${SourceBucket.Output.Bucket}'
Events:
- 'oss:ObjectCreated:*'
Filter:
Prefix: ''
Suffix: ''
这是一个图片Ai处理的应用,基本逻辑就是ServerlessBookImageDemo主体,从已有的OSS存储上拿到源图片文件,经过一系列步骤流程操作,转存到目标OSS。为了配合完成这些操作,提前设定好了各种授权配置。并把他们作为全局变量供给3个主体去调用,可以看到,这个复杂配置描述了独立的执行逻辑之间如何协同调用。配置文件通过 “${}”的语法糖描述动态变量,S可以自动解析装配,除此之外 S可以根据调用关系自动识别配置文件中主体的先后执行顺序 ,意味着你只需要关注每一个主体的输入和输出,而不用为他们各自的执行先后去做特殊编排。
讲到这里你或许还是很迷惑,因为配置看起来很复杂,这是因为入参部分也就是Properties 完全属于自定义范畴,所以可以延展的非常复杂(实际上我们有针对这类配置的可视化解决方案,稍后会介绍),但是除了这个,配置文件还是有自己的固定范式(划重点)
首先 最外层的Key (我称为主体),只有2类,一类Global,另一类就是唯一的键名,比如上面的SourceBucket,TargetBucket,ServerlessBookImageDemo;
其次非Global 主体 的范式也是固定的:
- Component - 核心执行逻辑组件;
- Provider - 服务商 ;
- Access-访问秘钥配置;
- Extends - 主体执行指令的扩展,它包括对应指令执行前后的要做什么操作;
- Properties - 输入参数,注意这个输入参数是给 Component 使用的。
所以你只要掌握上面划线部分的内容,这些配置文件再复杂也能快速识别出他的主体逻辑。
为了解决复杂配置的问题社区还提供了配套的可视化方案,简化开发者和使用者的配置使用
① 启动器
S 启动器是 serverless devs 最核心的部分,我们经常比喻说Serverless Devs 可以让开发者像使用手机一样玩转Serverless,这个手机的OS 就是说的S 启动器本器,他负责完成配置文件解析,组件加载,任务调度,日志输出,发布管控,国际化等能力。
灵活的指令集
我们说S启动器也是一个灵活的“瘦子”,是因为虽然他支持各种指令自定义,但实际上内置指令只有
init, config, platform, search, set, gui 这六个,通过将配置文件的主体和指令动态加载到内核实现这个骚操作。就举上面水印处理的例子,如果我们在项目外敲 S 这个指令,
输出的只有上面提到的6个指令
但是如果在水印项目的目录下则会有9个
如果再进一步的展开 s ServerlessBookImageDemo 则会继续把ServerlessBookImageDemo主体下的指令全部列举出来,这样的好处就是可以针对单个主体做功能调用,这里面列出来的指令实际上是主体下组件包含的真实方法。当然也有坏处,就是不利于做规范约束,那么针对目前的配置问题社区也正在讨论新的规范,欢迎大家一起加入
开发者可以通过开发自己的组件并将它的方法执行通过s 指令的方式触发。
动态的执行解析
当我们使用s deploy 指令的时候,启动器会先把 deploy 指令预加载到系统指令里,
接下来会自动触发 deploy的执行,可以简述为:
预先对配置文件进行解析,分离出变量元素,并进行对应的填充,如果遇到两个主体有先后依赖则进行顺序排序,最后生成待执行主体序列。
接下来按照序列遍历每一个主体 然后去检索主体下包含的组件,
执行donwload-》前置钩子检查执行-》具体组件方法执行的动作。
可以看到,S启动器的核心逻辑是解析相应的配置,组织执行顺序,调度执行任务, 真正的执行逻辑实际上是交给了具体的组件。执行逻辑部分下放到组件,使得S具备了支持不同云厂商的能力。
③ S framework
组件是核心的逻辑,我们就先来看看 组件的基类 核心库 s/core | s/framework
首先要说明的是 s/core 是 完全无厂商场依赖的基础库。
s/framework 继承了 s/core 的基础能力但却依赖 阿里云fc组件,它可以帮助开发者快速构建依托于阿里云fc 的应用场景,但是本身是锁定了云商的,这点需要大家知晓。
基本关系
怎么用
s/core
import { Component, Log } from '@serverless-devs/s-core';
class YourComponent extends Component {
async test(inputs ) {
const fc = await this.load('fc', 'Component'); // 通过load 方法加载已经存在于app store上的组件
const logger = new Log()
logger.log('test core logger ...');
}
}
s/framework
const FrameWork = require('@serverless-devs/s-framework');
class YourComponent extends FrameWork {
async deploy(inputs) {
const frameworkInputs = inputs;
const customerProperty = inputs.Properties.YourCustomerProperty;
if (customerProperty) {
// 做自己的逻辑处理,比如整合目录,发布等
// 可以重新修改入参值 frameworkInputs.Properties.CodeUri = new value;
}
return await super.deploy(frameworkInputs);
}
}
② S 可视化工具
如果说S 启动器 + S/framework 及所衍生的组件让S实现了支持无厂商锁定的能力,那么S Gui 则是帮助S在使用体验上更进一步。
应用/组件/插件
这三个概念在讲解s gui 之前想先简单说明一下,或许这边会有一些同学的困惑,通常你从app store 搜索项目的时候会发现一个hexo 的博客会出现好几个结果。有些是组件,有些是应用,那么组件和应用是什么关系呢?
应用本身不具备具体的执行逻辑细节,是一个项目的完整描述。它包含全局配置设置部分,项目约定配置,每一个项目又由组件,服务商,秘钥信息,组件入参组成。 应用的配置解析,执行步骤是由核心启动器来把控
组件才是执行逻辑的核心部分,hexo的部署逻辑,图片Ai的转换逻辑都是在组件里完成的,开发者通过将动态的参数剥离给使用的人,做到多场景的支持。而且大部分的应用业务场景都可以依赖我们提供的framework基础能力完成,比如前端框架托管,多语言的framework 托管等,当然如果你想构建更复杂的场景以及跟云商关联,那就需要花一些功夫打通云商服务的sdk了。
插件是更小的执行逻辑单元,通常会放到 应用配置的extends选项中,用来抽离多个不同场景组件的公共逻辑单元。这部分因重要性较低,暂不做过多介绍。
发布的可视化和解析的可视化
S 的组件从生产发布 到真正展现给社区的同学使用,主要涉及2个可视化配置部分
第一个是作为组件作者要把自己的组件文档,组件参数配置描述清楚
如上,组件作者需要将组件的入参说明一一配置成 类似下面Region,Detail的数据结构,一旦入参很多,并且是复合类型的,比如Detail,手写起来会非常麻烦,现在提供了gui 的支持,组件开发者就可以很轻松的构建复杂的配置项。
Region:
Description: 地域
Required: true
Example: ''
Default: ''
Type:
- String
Detail:
Description: 详细
Required: true
Example: ''
Default: ''
Type:
- Struct:
Service:
Description: 服务
Required: true
Example: ''
Default: ''
Type:
- Struct:
Name:
Description: 服务名
Required: true
Example: ''
Default: ''
Type:
- String
当然我们给使用者使用的时候也会简化成可视化的方案,比如基于上面的realworld-web-static 组件构建的静态托管应用最终呈现给使用者的配置
我们看到同样是 Region 和 Detail 这两个属性,在开发者描述的时候用的数据结构会比较麻烦,但是给用户这边用的时候就非常简单。
总结下来,S Gui 把简单留给了开发者,同时帮开发者把简单留给了使用者。
极简工程架构
S Gui 是完全基于开源项目构建, 桌面部分是用的electron 官方的demo,Browser 的页面则是 create react 初始化的标准 React项目,没有掺杂过多的依赖。所以如果你比较熟悉React 技术栈,并且希望构建一个结构清爽的桌面项目,也可以直接把S Gui 当成初始化模板使用
此外,我们提供了全套的桌面打包构建脚本,只需执行npm run build 就可以完成Browser 前端打包构建-> electron 多平台资源整合 -> 压缩桌面包 -> 发布OSS 一系列流程。 这大约帮助可以节省 20min 的构建发布时间。
开源开放极具美感的新一代UI库
Gui 的视觉部分使用的是 阿里云智能自研的 Ui库 b-design,最初是为我们的合作伙伴提供更具现代感的设计体验服务,现在则完全开放给Serverless 社区使用
@b-design/ui
import { Button } from "@b-design/ui";
import "@b-design/ui/dist/index.css";
写在最后
因为篇幅的问题,对S的介绍还是相对笼统,如有说明不清楚的还请见谅,另外因为S 本身也是新生的一款Serverless 开发者工具,本身还有很多的不足。我们希望能够有更多的小伙伴加入讨论共建,尤其最近我们决定
对S 的“灵魂”做进一步的“美化”(讨论链接),这里面可能涉及大量的核心逻辑改动,也需要拿到更多开发者的宝贵意见,以下是我们的学习群二维码,如果有兴趣的话欢迎你的加入。
如果您对我们的S工具感兴趣,就麻烦帮忙动动手指给我们的项目加个Star吧
S 命令行git地址 https://github.com/Serverless-Devs/Serverless-Devs
S Gui git 地址 https://github.com/Serverless-Devs/Serverless-Devs-App-Store