怎样快速的开发 Serverless Devs Package ?

作者阿里云代理 文章分类 分类:linux图文教程 阅读次数 已被围观 557


作者 | 江昱(阿里云 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 的规范中,有关于二者的一个差异图:


image


而关于 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即可:

image


选择结束,不难发现,此时会让我们继续选择是开发 Component 仍是开发 Application:


image


开发 Component


当选择Component Scaffolding之后,需求给即将开发的 Component 起一个名字(例如deployfunction):

image


此时,可以依据体统提示,进入到 Component 的项目目录:


image

此时,可以通过 IDE 翻开其时项目,并通过npm进行依托设备(由于 Serverless Devs 是根据 Typescript 的项目,所以组件的开发仅支撑 Typescript 和 Node.js 言语)

image


此时,可以翻开项目中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:

image

此时,我们可以找到 example 目录,进行 deploy 方法的测验,例如:

image

通过 example 下面的 s.yaml 文件,我们不难看出,这个 yaml 有两个 service(分别是 component-test 和 component-test2)。

并且这两个 service,都用了我们同一个组件。所以,在实行s deploy之后获得到预期的效果:即实行了 deploy 方法。

相同的,我们也可以实行 test 指令看一下作用:

image

要结束的逻辑可以在方法内安闲结束


换句话来说,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`
           }, {
               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;
   }

在该方法中首要存在几个工作:

  1. 对inputs参数解析,获取用户输入的参数内容;
  2. 假设用户输入的参数带有-h或许--help参数,则输出对应的协助信息;
  3. 假设用户输入的参数存在--token,则将--token对应的值存入到某个文件中;
  4. 假设用户没有带有--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 包:

在该东西包中,我们可以看到诸多的方法助力我们快速的运用:

image

例如,获取用户运用密钥,就可以直接引入 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中:

image

关于该文件的内容以及部分参数的取值,可以参看组件模型元数据。

当然,除了 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):

image

Serverless Package 中的 Application 开发相对更为简略,不论是任何编程言语,不论是任何项目,只需可以通过 Serverless Devs 开发者东西进行安置,就可以把它包装成为一个运用。

或许更为精确的来表述,只需你现在有一个可以通过 Serverless Devs 直接安置的项目,那么你就可以:

  1. s init 创建一个运用模板
  2. 把你的那个项目,脱敏后,直接放在src目录下
  3. 对运用进行描绘,例如修正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 中,方法很简略:

  1. 创建一个 Repo(代码仓库)
  2. 将整个运用或许组件推到该仓库
  3. 发布一个 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 的这个项目,本身也是其时文章的一个最佳实践。)

在结束组件或许运用的开发流程之后,需求:

  1. 注册并登录 Serverless Registry,实际上便是实行s cli registry login
  2. 进行组件的发布,实际上便是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,被更为广泛的运用。


本公司销售:阿里云新/老客户,只要购买阿里云,即可享受折上折优惠!>

我有话说: