Author | Jiang Yu (Alibaba Cloud Serverless Product Manager)​

foreword

Serverless Devs has been building with open source code and an open ecosystem, so there are two ways for community users to participate in the construction of Serverless Devs:

1. Participate in contributing code: The contribution of participating in the code has a clear and clear process, and it is also a common way to participate in open source projects. For the contributor documentation of Serverless Devs, you can refer to the code contribution documentation;

2. Participate in the contribution Package: You can develop applications or components and publish them to the Serverless Registry for more people to learn, reference or use; this part of the content can refer to this article;​​

Introduction to Serverless Devs Package

Before talking about Serverless Devs Packages, I need to talk about Serverless Registry, believe that many R&D students know that different languages/ecologies have their own package management platforms, such as Pypi in Python and NPM in Node.js.

The so-called package management platform, roughly speaking, is to manage "packages". The "packages" here often refer to certain functions or capabilities that have been encapsulated by others, and we can use them directly without reinventing the wheel.

Let’s talk about two more vivid examples. If we are engaged in artificial intelligence, it is not realistic for us to write various algorithms manually. We often use Sklearn, Tensorflow and other corresponding packages to quickly load some models, and then on this basis. Further development and improvement.

In the serverless field, we also hope to have a similar package management platform, which is the Serverless Registry:

|

Serverless ReigstryPython PypiNodejs NPM
store content Serverless packages , (including Components and Application) Python packagesNodejs packages
Is it an open standard is YesYes
official sourceregistry.devsapp.cn/simplepypi.python.orgregistry.npmjs.org
Examples of other sourcesGithub registryGitee registryTsinghua source, Douban sourcetnpm、cnpm
Whether to support privatization supports supportsupport
Supporting tools Serverless Devs Developer Python package management tool (pip)Node.js package management tool (npm)
Companion commandspipnpm
how to useDirect reference in s.yamlAfter installation, reference in the codeAfter installation, reference in the code

Different from Python's Pypi and Node.js's NPM, in Serverless Regsitry, Package is divided into two categories, one is Component and the other is Application.

Distinguish between Component and Application in general:

  • Component: refers to a component, similar to a script, through which you can do something. For example, deploy a function to a cloud platform, debug a function, view the log of a function, etc.;
  • Application: refers to Application, similar to a case. For example, through an Application, users can quickly create a Hello World application, create an audio and video processing application, etc.;
    In the specification of Serverless Devs, there is a difference diagram between the two:


The relationship between Component and Application is: Application is the definition of an application case, which needs to be deployed and launched through Component.

Perhaps the above expression is a little abstract, but in fact, it can be explained with a vivid case. For example:

  • You have made a face recognition application through Python's Tensorflow framework, then Tensorflow can be regarded as a Component, and the face recognition application can be regarded as an Application;
  • You have made a personal blog by relying on Express, Fs, Path and other dependencies of Node.js, then at this time, Express, Fs, Path and other dependencies can be considered as different components, and this blog can be considered as a an Application;
  • Serverless Registry Model
  • Serverless Package Model

Development Package

The process for developers to develop Serverless Packages is relatively simple. Because in Serverless Devs developer tools, relatively complete scaffolding capabilities have been provided.

Developers only need to execute s init and select Dev Template for Serverless Devs:

After the selection is complete, it is not difficult to find that we will continue to choose whether to develop Component or Application:

Develop Components

After selecting Component Scaffolding, you need to give the Component to be developed a name (such as deployfunction):


At this point, you can enter the project directory of Component according to the system prompt:

At this point, you can open the current project through IDE and install dependencies through npm_(Because Serverless Devs is a Typescript-based project, component development only supports Typescript and Node.js languages)_:


At this point, you can open the src/index.ts file in the project, and it is not difficult to find that there is already a case:

import logger from './common/logger';
import { InputProps } from './common/entity';

export default class ComponentDemo {
  /**
* demo instance
   * @param inputs
   * @returns
   */
  public async test(inputs: InputProps) {
    logger.debug(input: ${JSON.stringify(inputs.props)});
    logger.info('command test');
    return { hello: 'world' };
  }
}

In this file, it is not difficult to find that there is a test(inputs) method, which is a case of printing the inputs parameter and returning hello world, but through this simple case, we can learn a few things:

Public methods are commands that users can use

In the project, we can write multiple methods that are exposed to the outside world, currently there is only one test, but we can add any public methods, and these methods will become the commands of the component. For example:

 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' };
  }​

At this point, when we use the component, the component has two commands: the test command and the deploy command. In order to verify our ideas, we can compile the project in a basic development state: npm run watch:


At this point, we can find the example directory and test the deploy method, for example:


Through the s.yaml file below the example, we can easily see that this yaml has two service_ (component-test and component-test2 respectively). _

And these two services use our same component. Therefore, after executing s deploy, the expected result is obtained: that is, the deploy method is executed.

Similarly, we can also execute the test command to see the effect:


The logic to be implemented can be freely implemented within the method

In other words, when the Serverless Devs tool loads a component, it actually passes the corresponding parameters to the specified method and executes the method. So, whatever function you want to achieve can be written in the corresponding method.

Taking the Serverless Registry Component project as an example, I have a Login function in this project, so I implemented the following in Login:

 /**
* demo login
     * @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}

// output reminder
            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) {
// or get the result, store the state
                                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;
    }

There are mainly a few things in this method:

  1. Parse the inputs parameter to obtain the parameter content input by the user;
  2. If the parameter input by the user has the -h or --help parameter, the corresponding help information is output;
  3. If the parameter entered by the user has --token, the value corresponding to --token is stored in a file;
  4. If the user does not have the --token input, open the browser directly, access the login address of the Serverless Registry, and obtain the relevant login token;

Then the same method, if it is a method or command to deploy a function, can we implement the packaging and compression code in this, and then call the relevant creation function, update the interface of the function to create the function? For another example, if you want to make a method to delete a function, can you call the interface of the delete function in it?

So it can be considered that no matter what function you want to achieve, it can be implemented in the corresponding method.

About some specifications in the development process

We said above that Serverless Devs will call this method with certain parameters, so what do the parameters look like? What is the format and how do we parse it?

For another example, what is the use of the final return of the project? How to get user's key information in project? How to obtain various parameters written by users in Yaml? How to obtain the parameters passed by the user when executing the command?

In fact, these can be referred to: the component model code specification of the development specification document of the Serverless Devs Package, here we can easily find:

The structure of the input parameters is:

 {
    "command": "", 
    "project": {
        "projectName": "", 
        "component": "",
        "provider": "",
        "access": ""
    },
    "credentials": {},
    "prop": {},
    "args": "",
    "argsObj": []
}

Among them, the meaning of these parameters:

contentmeaning
commandthe command executed by the user
projectUser's project basic information
credentialsUser's key information
propUser-configured properties/parameters
argsParameters passed by the user (in string form)
argsObjParameters passed by the user (parsed, passed as an array)

A more specific example is that in the above case code, there is a test method, which is the method of function implementation. At this point, when the user uses the test command, the system will call the method with parameters. Take a real case as an example:

The component name is hexo, the core code of the component is shown above, and it has a test method. At this time, the Yaml on the user side is:

edition: 1.0.0 # Command-line YAML specification version, following the Semantic Versioning specification
name: FullStack # Project name
access: xxx-account1 # key alias

services:
  HexoComponent:
    component: hexo
    props:
      region: 'cn-hangzhou'
      codeUri: './src'​

When the user executes s test mytest -a -b abc, at this time, the test method in the component code, the received inputs parameter is actually:

{
    "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"
    ]
}

At this point, the test method will print log information, etc., and return the final result to the command line tool: { "hello": "world" }

For how to return to the help file, how to obtain key information, and how to parse user input, you can refer to the core package provided by Serverless Devs:

In this toolkit, we can see many methods to help us use it quickly:


For example, to get the user key, you can directly import the core package and use the corresponding getCredential method:

  • Method 1: When no parameters are passed, the default key information will be obtained
    const { getCredential } = require('@serverless-devs/core');
    async function get() {
      const c = await getCredential();
      console.log('c', c);
    }

  • Use method 2: pass parameters to get the specified key information
    const { getCredential } = require('@serverless-devs/core');
    async function get() {
// inputs received by the component
      const inputs = {};
      const c = await getCredential(inputs, 'custom', 'AccountIdByCustom', 'SecretIDByCustom');
      console.log('c', c);
    }

description of the component

After completing the writing of our component functions, we can describe the components. The so-called component description is to tell the Serverless Registry what component this is and what functions it has. The description content is in publish.yaml:


For the content of this file and the values ​​of some parameters, you can refer to the component model metadata.

Of course, in addition to publish.yaml, in the specification of Serverless Package, there are other files in the directory:

|- src # directory name can be changed
| └── code directory
|- package.json: need to define main
|- publish.yaml: the resource description of the project
|- readme.md: Project Introduction
|- version.md: Version update content

in:

contentmustmeaning
srcrecommended presenceThe unified placement function can be realized, of course, it can also be replaced with other names, or tiled under the project, but it is recommended to use src for unified storage
package.jsonmust existThe package.json of Node.js needs to clearly describe the location of the entry file of the component
publish.yamlmust existDevelopment identification documentation for Serverless Devs Package
readme.mdmust existA description of the component, or help documentation
version.mdrecommended presenceA description of the version, such as the updated content of the current version, etc.

The latest version of the specification (version 0.0.2) will be launched in the near future. Different from version 0.0.1, the new version of the Properties parameter will follow the JSON Scheme specification, currently refer to pr#386)

Develop Application

After selecting Application Scaffolding, you need to give the application to be developed a name (eg helloworld):


Application development in Serverless Package is relatively simpler. No matter it is any programming language or any project, as long as it can be deployed through the Serverless Devs developer tool, it can be packaged into an application.

Or to put it more precisely, as long as you now have a project that can be deployed directly through Serverless Devs, then you can:

  1. s init creates an application template
  2. Put your project, after desensitization, directly in the src directory
  3. Describe the application, such as editing publish.yaml, editing version.md, editing readme.md, etc.

For details of this part, please refer to the application model documentation:

Special format: In the application model, the src/s.yaml file needs to exist as a resource and behavior description file identified and used by Serverless Devs. In this file, there may be some content that needs to be filled in by the user, such as the user's The name of the key, the region where the user deploys the service, etc. You can refer to:

  • "{{ access }}" : Directly remind the user that a parameter such as access needs to be entered as a necessary parameter in Yaml;
  • '{{ bucket | alibaba oss bucket }}' : Directly remind the user that a parameter such as bucket needs to be input as a necessary parameter in Yaml, and the content "alibaba oss bucket" after | is used to explain the meaning of this parameter;
    For example, in the s.yaml of an application, it is expressed as:

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

Publish Package

After completing the development of the Serverless Package, it can also be published to the Registry in order to be used by more people.

Publish to Github/Gitee

Regarding publishing to Github or Gitee, the method is very simple:

  1. Create a Repo (code repository)
  2. Push the entire application or component to this repository
  3. Publish a Release: At this point, on the client side, the user can switch the registry through the s set registry to use the corresponding function. For example, if my Github account is anycodes, I can create a repository named demo. At this point, I upload my components/applications to this repository, and then publish a Release. At this point, I'm on the client side, switch the registry to Github, and then I can:
  • When using a component, specify the component name as a repository, such as anycodes/demo
  • When initializing the application, you can also specify the application directly, such as anycodes/application

    Publish to Serverless Registry

If you want to publish Package to Serverless Registry, you can consider using Serverless Registry Component.

(The client tool of Serverless Registry is also a component, so it can be considered that this project of Serverless Registry Component is also a best practice in the current article.)

After completing the component or application development process, you need to:

  1. Registering and logging in to the Serverless Registry is actually executing s cli registry login
  2. Publishing components is actually s cli registry publish

Of course, the Serverless Registry Component project has many other functions besides logging in and publishing, such as:

  • View the Packages published by the current login account
  • View the version information of a package
  • View specific version information of a Package
  • Delete a package of a specified version
  • Update the login token

Summarize

As we all know, the development of a complete technical architecture is inseparable from the empowerment of the ecological community, whether it is Docker's Dockerhub, Python's Pypi, or Node.js' NPM, the ecological activity and the happiness of our developers. feeling is positively correlated.

We also very much hope that Serverless Devs can play the Serverless architecture with more people through an open ecosystem such as the Serverless Regsitry, and expect more excellent packages to be more widely used.

( END)

For more content, pay attention to the Serverless WeChat official account (ID: serverlessdevs), which brings together the most comprehensive content of serverless technology, regularly holds serverless events, live broadcasts, and user best practices.


Serverless
69 声望265 粉丝