如何快速开发 Serverless Devs Package ?
简介:目前,开发者开发 Serverless Package 的流程相对来说是比较简单的。因为在 Serverless Devs 开发者工具中,已经提供了相对完整的脚手架能力,一文了解详情~
作者 | 江昱(阿里云 Serverless 产品经理)
前言
Serverless Devs 一直在以开源代码、开放生态的模式进行建设,所以在社区用户参与 Serverless Devs 的建设过程中,就会有两条途径:
1、参与贡献代码:参与代码的贡献相对于来说是有着清晰明确的流程,并且也是参与开源项目常见的途径,Serverless Devs 的贡献者文档,可以参考代码贡献文档;
2、参与贡献 Package:可以开发应用或者组件,发布到 Serverless Registry,以供更多人学习、参考或者使用;这一部分内容可以参考本文;
Serverless Devs Package 介绍
在说什么是 Serverless Devs Packages 之前,需要先说一下 Serverless Registry,相信很多研发同学都是知道,不同语言/生态都有自己的包管理平台,例如 Python 语言的 Pypi,Node.js 的 NPM。
而所谓的包管理平台,粗暴来说就是管理 “包” 的,这里的 “包”,往往指的是别人已经封装了的某些功能或者能力,我们可以直接使用,而无需我们重复造轮子。
说两个比较形象的例子,如果是搞人工智能,我们不太现实要手动的来写各种算法,往往会通过 Sklearn,Tensorflow 等对应的包来快速的加载某些模型,然后在这个基础上再进步的开发和完善。
而在 Serverless 领域,我们也希望有一个类似的包管理平台,那就是 Serverless Registry:
Serverless Reigstry |
Python Pypi |
Nodejs NPM |
|
存储内容 |
Serverless packages,(包括 Components 和 Application) |
Python packages |
Nodejs packages |
是否开放标准 |
是 |
是 |
是 |
官方源 |
registry.devsapp.cn/simple |
pypi.python.org |
registry.npmjs.org |
其它源举例 |
Github registry 、 Gitee registry |
清华源 、豆瓣源 |
tnpm、cnpm |
是否支持私有化 |
支持 |
支持 |
支持 |
配套工具 |
Serverless Devs 开发者工具 |
Python包管理工具(pip) |
Node.js打包管理工具(npm) |
配套命令 |
s |
pip |
npm |
如何使用 |
在s.yaml中直接引用 |
安装之后,在代码中引用 |
安装之后,在代码中引用 |
与 Python 的 Pypi,Node.js 的 NPM 不同的是,在 Serverless Regsitry 中,Package 是分成两类的,一类是 Component,一类是 Application。
针对 Component 和 Application 的通俗来作区分:
- Component:指的是组件,类似于一个脚本,通过这个脚本可以做一些事情。例如部署一个函数到某个云平台,调试某个函数,查看某个函数的日志等;
- Application:指的是应用,类似于一个案例。例如通过某个 Application,可以让用户快速的创建一个 Hello World 的应用,创建一个音视频处理的应用等;
在 Serverless Devs 的规范中,有关于二者的一个区别图:
而关于 Component 和 Application 的关系是:Application 是一个应用案例的定义,需要通过 Component 进行部署上线。
或许上面的表示有些许的抽象,其实可以用一个形象的案例进行解释。例如:
- 你通过 Python 的 Tensorflow 框架,做了一个人脸识别的应用,那么此时 Tensorflow 就可以认为是一个 Component,而人脸识别的应用就可以认为是一个 Application;
- 你通过 Node.js 的 Express、Fs、Path 等依赖,做了一个个人博客,那么此时 Express、Fs、Path 等依赖,就可以认为是不同的 Component,而做出来的这个博客,就可以认为是一个 Application;
- Serverless Registry Model
- Serverless Package Model
开发 Package
开发者开发 Serverless Package 的流程相对来说是比较简单的。因为在 Serverless Devs 开发者工具中,已经提供了相对完整的脚手架能力。
开发者只需要执行s init,并且选择Dev Template for Serverless Devs即可:
选择完成,不难发现,此时会让我们继续选择是开发 Component 还是开发 Application:
开发 Component
当选择Component Scaffolding之后,需要给即将开发的 Component 起一个名字(例如deployfunction):
此时,可以根据体统提示,进入到 Component 的项目目录:
此时,可以通过 IDE 打开当前项目,并通过npm进行依赖安装(因为 Serverless Devs 是基于 Typescript 的项目,所以组件的开发仅支持 Typescript 和 Node.js 语言):
此时,可以打开项目中src/index.ts文件,不难发现已经存在一个案例:
import logger from './common/logger';
import { InputProps } from './common/entity';
export default class ComponentDemo {
/**
* demo 实例
* @param inputs
* @returns
*/
public async test(inputs: InputProps) {
logger.debug(`input: ${JSON.stringify(inputs.props)}`);
logger.info('command test');
return { hello: 'world' };
}
}
在该文件中,我们不难发现存在一个test(inputs)方法,该方法是一个打印inputs参数,并返回hello world的案例,但是通过这个简单的案例,我们可以了解到几个事情:
公共方法就是用户可以使用的命令
在项目中,我们可以写多个方法对外暴露,目前只有一个test,但是我们可以增加任何public方法,而这些方法将会成为该组件的命令。例如:
public async test(inputs: InputProps) {
logger.debug(`input: ${JSON.stringify(inputs.props)}`);
logger.info('command test for test');
return { hello: 'world' };
}
public async deploy(inputs: InputProps) {
logger.debug(`input: ${JSON.stringify(inputs.props)}`);
logger.info('command test for deploy');
return { hello: 'world' };
}
此时,我们在使用该组件时,组件就具备了两个命令:test 命令和 deploy 命令,为了验证我们的想法,我们可以对项目进行基础的开发态的编译:npm run watch:
此时,我们可以找到 example 目录,进行 deploy 方法的测试,例如:
通过 example 下面的 s.yaml 文件,我们不难看出,这个 yaml 有两个 service(分别是 component-test 和 component-test2)。
并且这两个 service,都用了我们同一个组件。所以,在执行s deploy之后获得到预期的结果:即执行了 deploy 方法。
同样的,我们也可以执行 test 命令看一下效果:
要实现的逻辑可以在方法内自由实现
换句话来说,Serverless Devs 工具在加载组件的时候,实际上就是将对应的参数,传递到指定的方法,并且执行该方法。所以,你要实现什么功能都可以写在对应的方法里。
以 Serverless Registry Component 项目为例,我在该项目中,存在一个 Login 的功能,所以我在 Login 中实现了以下内容:
/**
* demo 登陆
* @param inputs
* @returns
*/
public async login(inputs: InputProps) {
const apts = {
boolean: ['help'],
alias: {help: 'h'},
};
const comParse = commandParse({args: inputs.args}, apts);
if (comParse.data && comParse.data.help) {
help([{
header: 'Login',
content: `Log in to Serverless Registry`
}, {
header: 'Usage',
content: `$ s cli registry login <options>`
}, {
header: 'Options',
optionList: [
{
name: 'token',
description: '[Optional] If you already have a token, you can configure it directly',
type: String,
}
],
}, {
header: 'Examples without Yaml',
content: [
'$ s cli registry login',
'$ s cli registry login --token my-serverless-registry-token',
],
},]);
return;
}
const tempToken = comParse.data ? comParse.data.token : null
let st = 0
let user
if (tempToken) {
const fd = await fse.openSync(`${getRootHome()}/serverless-devs-platform.dat`, 'w+')
await fse.writeSync(fd, tempToken)
await fse.closeSync(fd)
st = 1
} else {
const token = random({length: 20})
const loginUrl = `https://github.com/login/oauth/authorize?client_id=beae900546180c7bbdd6&redirect_uri=http://registry.devsapp.cn/user/login/github?token=${token}`
// 输出提醒
logger.warn("Serverless registry no longer provides independent registration function, but will uniformly adopt GitHub authorized login scheme.")
logger.info("The system will attempt to automatically open the browser for authorization......")
try {
await sleep(2000)
opn(loginUrl)
} catch (e) {
logger.info("Failed to open the default address. Please try to open the following URL manually for authorization: ")
logger.info(loginUrl)
}
await logger.task('Getting', [
{
title: 'Getting login token ...',
id: 'get token',
task: async () => {
for (let i = 0; i < 100; i++) {
await sleep(2000)
const tempResult = await request('http://registry.devsapp.cn/user/information/github', {
params: {
token: token,
},
})
if (!tempResult.Error && tempResult.safety_code) {
// 或得到结果, 存储状态
const fd = await fse.openSync(`${getRootHome()}/serverless-devs-platform.dat`, 'w+')
await fse.writeSync(fd, tempResult.safety_code)
await fse.closeSync(fd)
st = 1
user = tempResult.login
break
}
}
},
}
])
}
if (st == 1) {
logger.log(`${user ? user + ': ' : ''}Welcome to Serverless Devs Registry.`, "green");
} else {
logger.error("Login failed. Please log in to GitHub account on the pop-up page and authorize it, or try again later.")
}
return null;
}
在该方法中主要存在几个事情:
- 对inputs参数解析,获取用户输入的参数内容;
- 如果用户输入的参数带有-h或者--help参数,则输出对应的帮助信息;
- 如果用户输入的参数存在--token,则将--token对应的值存入到某个文件中;
- 如果用户没有带有--token输入,则直接打开浏览器,访问 Serverless Registry 的登陆地址,并进行相关登陆token的获取;
那么同样的方法,如果是一个部署函数的方法或者命令,我们是不是可以在这个里面实现打包压缩代码,然后调用相关创建函数,更新函数的接口进行函数的创建呢?再比如,想要做一个删除函数的方法,是不是可以在里面调用删除函数的接口呢?
所以可以认为,无论想要实现什么功能,都可以在对应的方法中实现。
关于开发过程中的一些规范
在上面我们说到,Serverless Devs 会带着某些参数调用该方法,那么参数是什么样子的?格式如何,我们该如何解析呢?
再比如,项目最后的 return 有什么用处?如何在项目中获取用户的密钥信息?用户在 Yaml 中写的各种参数如何获取?用户在执行命令时候传递的参数如何获取?
其实这些都可以参考:Serverless Devs Package 的开发规范文档的组件模型代码规范,在这里我们不难发现:
入参 inputs 的结构为:
{
"command": "",
"project": {
"projectName": "",
"component": "",
"provider": "",
"access": ""
},
"credentials": {},
"prop": {},
"args": "",
"argsObj": []
}
其中,这些参数的含义:
目录 |
含义 |
command |
用户所执行的命令 |
project |
用户的项目基本信息 |
credentials |
用户的密钥信息 |
prop |
用户配置的属性/参数 |
args |
用户传递的参数(字符串形式) |
argsObj |
用户传递的参数(解析后的,以数组形式传递) |
一个更为具体的例子是,在上面的案例代码中,有一个 test 方法,该方法就是功能实现的方法。此时当用户使用 test 命令时,系统就会携带参数调用该方法。以一个真实案例作为举例说明:
该组件名为hexo,组件核心代码如上所示,具备一个 test 方法,此时用户侧的 Yaml 为:
edition: 1.0.0 # 命令行YAML规范版本,遵循语义化版本(Semantic Versioning)规范
name: FullStack # 项目名称
access: xxx-account1 # 秘钥别名
services:
HexoComponent:
component: hexo
props:
region: 'cn-hangzhou'
codeUri: './src'
当用户执行s test mytest -a -b abc,此时,组件代码中的test方法,收到的inputs参数实际上是:
{
"command": "test",
"project": {
"projectName": "HexoComponent",
"component": "hexo",
"provider": "alibaba",
"access": "release"
},
"credentials": {
"AccountID": "********",
"AccessKeyID": "********",
"AccessKeySecret": "********"
},
"prop": {
"Region": "cn-hangzhou",
"CodeUri": "./src"
},
"args": "mytest -a -b abc",
"argsObj": [
"mytest", "-a", "-b", "abc"
]
}
此时 test 方法会打印日志信息等,并返回最终的结果给命令行工具:{ "hello": "world" }
而关于如何返回帮助文件,如何获取密钥信息,如何解析用户的输入内容,则可以参考 Serverless Devs 提供的 core 包:
在该工具包中,我们可以看到诸多的方法助力我们快速的使用:
例如,获取用户使用密钥,就可以直接引入 core 包,使用对应的getCredential方法即可:
- 使用方法 1 :不传任何参数的时候,会获取 default 密钥信息
const { getCredential } = require('@serverless-devs/core');
async function get() {
const c = await getCredential();
console.log('c', c);
}
- 使用方法 2 :传参数,获取指定的密钥信息
const { getCredential } = require('@serverless-devs/core');
async function get() {
// 组件接收的inputs
const inputs = {};
const c = await getCredential(inputs, 'custom', 'AccountIdByCustom', 'SecretIDByCustom');
console.log('c', c);
}
组件的描述
在完成我们的组件功能编写之后,就可以进行组件的描述,所谓的组件的描述,就是要告诉 Serverless Registry,这是一个什么组件,有哪些功能。描述内容在publish.yaml中:
关于该文件的内容以及部分参数的取值,可以参考组件模型元数据。
当然,除了 publish.yaml 之外,在 Serverless Package 的规范中,目录哪还有其他的文件:
|- src # 目录名字可以变更
| └── 代码目录
|- package.json: 需要定义好main
|- publish.yaml: 项目的资源描述
|- readme.md: 项目简介
|- version.md: 版本更新内容
其中:
目录 |
必须 |
含义 |
src |
推荐存在 |
统一放置功能实现,当然也可以换成其他的名称,或者平铺到项目下,但是推荐通过src来做统一的存放 |
package.json |
必须存在 |
Node.js的package.json,需要描述清楚组件的入口文件位置 |
publish.yaml |
必须存在 |
Serverless Devs Package的开发识别文档 |
readme.md |
必须存在 |
对该组件的描述,或帮助文档信息 |
version.md |
推荐存在 |
版本的描述,例如当前版本的更新内容等 |
最新版本的规范 (0.0.2 版本),将会在近期上线,和 0.0.1 版本不同的是,新版本的 Properties 参数将遵循 JSON Scheme 规范,目前可参考 pr#386)
开发 Application
当选择Application Scaffolding之后,需要给即将开发的 Application 起一个名字(例如helloworld):
Serverless Package 中的 Application 开发相对更为简单,无论是任何编程语言,无论是任何项目,只要可以通过 Serverless Devs 开发者工具进行部署,就可以把它包装成为一个应用。
或者更为准确的来表述,只要你现在有一个可以通过 Serverless Devs 直接部署的项目,那么你就可以:
- s init 创建一个应用模板
- 把你的那个项目,脱敏后,直接放在src目录下
- 对应用进行描述,例如编辑publish.yaml,编辑version.md,编辑readme.md等
这一部分具体情况,可以参考应用模型文档:
特殊格式:在应用模型中,需要存在src/s.yaml文件,作为Serverless Devs识别和使用的资源、行为描述文件,在该文件中,可能涉及到部分内容是需要用户进行填写的,例如用户的密钥名字,用户部署业务的地域等。此时可以参考:
- "{{ access }}" :直接提醒用户需要输入access这样的一个参数,作为Yaml中所必须的参数;
- '{{ bucket | alibaba oss bucket }}' ::直接提醒用户需要输入bucket这样的一个参数,作为Yaml中所必须的参数,并以|之后的内容"alibaba oss bucket"作为解释这个参数的含义;
例如,在某应用的s.yaml中表现为:edition: 1.0.0
access: "{{ access }}"
services:
website-starter:
component: devsapp/website
actions:
pre-deploy:
- run: npm install
path: ./
- run: npm run build
path: ./
props:
bucket: '{{ bucket | alibaba oss bucket }}'
src:
codeUri: ./
publishDir: ./build
index: index.html
region: cn-hangzhou
hosts:
- host: auto
发布 Package
在完成 Serverless Package 的开发之后,为了给更多人使用,还可以将发布到 Registry 中
发布到 Github/Gitee
关于发布到 Github 或者 Gitee 中,方法很简单:
- 创建一个 Repo(代码仓库)
- 将整个应用或者组件推到该仓库
- 发布一个 Release:此时,在客户端,用户就可以通过s set registry进行registry的切换,来使用相对应的功能了。例如,我在 Github 的账户为 anycodes,我就可以创建一个仓库,名字为 demo,此时,我将我的组件/应用上传到这个仓库中,然后发布一个 Release。此时,我在客户端,将 registry 切换到 Github,然后就可以:
- 在使用组件的时候,指定组件名为仓库,例如 anycodes/demo
- 在初始化应用的时候,也可以直接指定应用,例如 anycodes/application
发布到 Serverless Registry
若想把 Package 发布到 Serverless Registry,可以考虑使用 Serverless Registry Component。
(Serverless Registry 的客户端工具,也是一个组件,所以可以认为 Serverless Registry Component 的这个项目,本身也是当前文章的一个最佳实践。)
在完成组件或者应用的开发流程之后,需要:
- 注册并登录 Serverless Registry,实际上就是执行s cli registry login
- 进行组件的发布,实际上就是s cli registry publish
当然,Serverless Registry Component 这个项目,除了登陆和发布之外,还有诸多其他的功能,例如:
- 查看当前登陆账号发布过的 Package
- 查看某个 Package 的版本信息
- 查看某个 Package 指定版本信息
- 删除某个指定版本的 Package
- 对登陆 token 进行更新
总结
众所周知,一个完整的技术架构的发展,离不开生态社区对他的赋能,无论是 Docker 的 Dockerhub 还是 Python 的 Pypi,再或者是 Node.js 的 NPM,生态的活跃度和我们开发者的幸福感是正相关的。
我们也非常希望 Serverless Devs 可以通过 Serverless Regsitry 这样一个开放的生态,和更多的人一同玩转 Serverless 架构,也期待更多优秀的 Package,被更为广泛的应用。
本文为阿里云原创内容,未经允许不得转载。
如何快速开发 Serverless Devs Package ?的更多相关文章
- 重磅 | 阿里开源首个 Serverless 开发者平台 Serverless Devs
Serverless 从概念提出到应用,已经走过了 8 个年头,开发者对 Serverless 的使用热情不断高涨.为帮助开发者实现一键体验多云产品,极速部署 Serverless 项目,10 月 2 ...
- 暑期 2021 | Serverless Devs 最全项目申请攻略来啦!
Serverless 是近年来云计算领域热门话题,凭借极致弹性.按量付费.降本提效等众多优势受到很多人的追捧,各云厂商也在不断地布局 Serverless 领域.但是随着时间的发展,Serverles ...
- Serverless 初体验:快速开发与部署一个Hello World(Java版)
昨天被阿里云的这个酷炫大屏吸引了! 我等85后开发者居然这么少!挺好奇到底什么鬼东西都是90.95后在玩?就深入看了一下. 这是一个关于Serverless的体验活动,Serverless在国内一直都 ...
- 快速开发Grunt插件----压缩js模板
前言 Grunt是一款前端构建工具,帮助我们自动化搭建前端工程.它可以实现自动对js.css.html文件的合并.压缩等一些列操作.Grunt有很多插件,每一款插件实现某个功能,你可以通过npm命名去 ...
- Android快速开发系列 10个常用工具类
转载请标明出处:http://blog.csdn.net/lmj623565791/article/details/38965311,本文出自[张鸿洋的博客] 打开大家手上的项目,基本都会有一大批的辅 ...
- Sublime插件库新成员基于APICloud快速开发跨平台App
互联网时代强调用户体验,那什么是HTML5跨平台App开发者的编程体验?“不剥夺.不替换开发者喜欢的开发工具,就是人性化的用户体验”,APICloud给出了这样的答案! 重磅发布“多开发工具支持策略” ...
- 【转】 Android快速开发系列 10个常用工具类 -- 不错
原文网址:http://blog.csdn.net/lmj623565791/article/details/38965311 转载请标明出处:http://blog.csdn.net/lmj6235 ...
- Android 快速开发系列 打造万能的ListView GridView 适配器
转载请标明出处:http://blog.csdn.net/lmj623565791/article/details/38902805 ,本文出自[张鸿洋的博客] 1.概述 相信做Android开发的写 ...
- GAE+bottle+jinja2+beaker快速开发demo - Python,GAE - language - ITeye论坛
GAE+bottle+jinja2+beaker快速开发demo - Python,GAE - language - ITeye论坛 :GAE+bottle+jinja2+beaker快速开发 ...
- 使用 CodeIgniter 框架快速开发 PHP 应用(七)
原文:使用 CodeIgniter 框架快速开发 PHP 应用(七) CodeIgniter 和对象这是玩家章节.它讲述的是 CodeIgniter 的工作原理,也就是揭开CI头上'神秘的面纱'.如果 ...
随机推荐
- FFmpeg命令行之ffprobe
一.简述 ffprobe是ffmpeg命令行工具中相对简单的,此命令是用来查看媒体文件格式的工具. 二.命令格式 在命令行中输入如下格式的命令: ffprobe [文件名] 三.使用ffprobe查看 ...
- vim的使用进步
vim的使用进步 1.如果遇到命令行中无法退出的 狂按esc按键 或者也可以使用v模式下切换一下,之后按esc 保存退出 保存退出--:wq 保存:w 不保存退出:q! i--插入模式 v- 可视化模 ...
- vim下删除swp文件
vim下删除swp文件 几个选项的内涵 [O]pen Read-Only 只读的方式打开 (E)dit anyway 编辑模式打开,但是不会载入存盘的内容 (R)ecover 编辑模式打开并且加载暂存 ...
- 作用域&变量提升&闭包题目及内容解答
这里给大家分享我在网上总结出来的一些知识,希望对大家有所帮助 1. 代码输出结果 (function(){ var x = y = 1; })(); var z; console.log(y); // ...
- .NET分布式Orleans - 2 - Grain的通信原理与定义
Grain 是 Orleans 框架中的基本单元,代表了应用程序中的一个实体或者一个计算单元. 每个Silo都是一个独立的进程,Silo负责加载.管理和执行Grain实例,并处理来自客户端的请求以及与 ...
- mybatis调用Oracle存储过程 带游标
存储过程 CREATE OR REPLACE PROCEDURE proc_test2(p_id IN NUMBER, v_cur OUT SYS_REFCURSOR, p_result_code O ...
- wire和reg型变量的组合使用
模型功能 实现寄存器之间的连线 实现寄存器的声明 建构时钟的时序系统 模型框图 `timescale 1ns / 1ps /* */ // ****************************** ...
- KingbaseES 分区表与 Oracle 分区表对于空值的处理差异
一.对于null 值处理 1.Oracle 分区字段允许为空,只要存在maxvalue 分区,值就可以插入. SQL> create table t1(id number,data varcha ...
- OPC报文详解
OPC (OLE for Process Control) 是一种工业通讯协议的标准,用于实现不同制造商的设备和系统之间的数据交换.它主要用于工业自动化系统中.OPC标准有几个不同的规范,包括OPC ...
- Modbus报文详解
Modbus是一种串行通信协议,最初由Modicon公司(现为施耐德电气的一部分)在1979年为使用其PLC(可编程逻辑控制器)而开发.Modbus已成为工业领域内广泛使用的一种通信协议,特别是对于监 ...