• 动手搭建ServerLess服务


    一、前言

    ​ 通常我们在做ServerLess的时候会想到用各种云的Faas服务,比如腾讯云,AWS,阿里云等等。但我们很少去研究自己怎么搭建一个ServerLess服务。本篇文章重点会讲解如何自己在服务器上搭建ServerLess服务,并如何使用它。

    二、什么是ServerLess?

    ​ Serverless,又叫无服务器。Serverless 强调的是一种架构思想和服务模型,让开发者无需关心基础设施(服务器等),而是专注到应用程序业务逻辑上。Serverless 也是下一代计算引擎。

    ​ Serverless 与 FaaS(函数即服务)通常被视为可以互换的术语,但这并不准确。Serverless 是一种抽象层次更高的架构模式,而**“FaaS + BaaS”只是 Serverless 这种架构模式的一种实现**。

    ​ 其中,FaaS 是一种特定类型的服务,例如 AWS Lambda,Google Cloud Functions,Azure Functions,阿里云函数计算和腾讯云云函数等等;而 BaaS(后端即服务)可以理解为其他类型的托管服务,例如数据库服务,对象存储服务和日志服务等等。

    ServerLess具有以下特征:

    • 免运维:不需要管理服务器主机或者服务器进程。

    • 弹性伸缩:根据负载进行自动规模伸缩与自动配置。伸缩范围零到无穷大。

    • 按需付费:根据使用情况决定实际成本。

    • 高可用:具备隐含的高可用性。

    三、ServerLess技术选型

    ​ 两种方案,一种基于现有的Faas云服务,这样不用关心搭建和维护,只需要购买就行了(这种方式我就不讲了,大家可以直接去看腾讯云官网文档)。另一种是自己搭建SeverLess服务端。在这里我们不讲第一种方式,我们讲第二种方式。

    ​ 搭建服务有很多开源的方案,首先最火的是的OpenFaas库,它是基于kubernetes服务的,环境搭建比较复杂。出于快速上手的目的,选用一块轻量级的SeverLess服务的库。这里我们选用fnproject。它入手简单,只需要有docker环境,即可运行。可以实现快速部署。

    fnproject 是一个原生容器无服务器项目,它几乎支持任何编程语言,并且几乎可以在任何地方运行。Fn 是用 Go 语言编写的,因此性能较好且十分轻量。Fnproject 支持 AWS Lambda 风格(AWS Lambda是亚马逊提供的serverless服务),因此你可以轻松导入你的 Lambda 函数并通过 Fnproject 启动它。

    四、ServerLess基础服务搭建

    ​ 首先我们需要安装FnProject,它只支持Linux / Mac 两种系统, 对于Window用户,我建议安装一个ubuntu的操作系统。基于WSL2的话,还是比较简单的。

    ​ FnProject的服务端和客户端都是在一个程序中,所以我们只需要安装一个fn-cli就可以了。下面我讲一下详细的安装步骤。

    Mac安装示例:

    ​ 使用Homebrew安装 (这里有个坑,大家要换国内的镜像源,不然会很慢,清华镜像

    brew update && brew install fn
    复制代码

    ​ 或者使用FnProject的脚本安装

    curl -LSs https://raw.githubusercontent.com/fnproject/cli/master/install | sh
    复制代码

    Windows安装说明:

    ​ 首先升级你的系统支持WSL2协议, 安装Ubuntu系统,然后用上面的脚本安装即可。

    最后启动ServerLess服务

    fn start
    #默认会走8080端口
    fn start -p 9080 
    #可以指定一个端口启动
    #这里注意,如果修改端口后,还需要修改环境变量
    export FN_API_URL=http://127.0.0.1:9080 
    复制代码

    五、生成ServerLess应用

    fnproject 提供一下五种语言的支持,分别都有对应的教程,在这里我们主要讲的是nodejs语言,其他语言大家可以自行去看。

    fn init --runtime node nodeTest #初始化node项目的结构
    复制代码

    然后我们来看一下nodeTest的目录结构

    img

    理解func.yaml文件作用

    schema_version: 20180708
    name: nodetest
    version: 0.0.1
    runtime: node
    build_image: fnproject/node:14-dev
    run_image: fnproject/node:14
    entrypoint: node func.js
    复制代码
    • schema_version 服务的唯一标识 ,它决定哪些字段是可以用的

    • name 服务的名称,也是所在的目录名称

    • Version 当前的函数的版本

    • runtime 运行的语言信息

    • build_image 打包的docker镜像

    • run_image 运行的docker镜像

    • entrypoint 函数是由docker启动的,这个是最后docker执行的命令,node func.js,代表启动这个node服务

    func.js是我们的代码文件,比较简单,输出hello world,内容如下

    const fdk=require('@fnproject/fdk');
    
    fdk.handle(function(input){
      let name = 'World';
      if (input.name) {
        name = input.name;
      }
      console.log('\nInside Node Hello World function')
      return {'message': 'Hello ' + name}
    })
    复制代码

    六、ServerLess部署

    首先我们需要create app appname 一个node应用,对于现有的函数应用进行管理的app,也相当于给你的函数应用一个命名空间。这个app name 也是部署需要用到的。在同一个app下的函数,可以一起部署。

    #在当前函数代码目录下执行
    fn create app nodeApp
    复制代码

    创建完后,我们就可以部署我们的函数应用了。 这里fnproject为我们提供deploy命令

    fn --verbose deploy --app nodeApp --local
    复制代码

    这里说明一些参数

    • --verbose 这能将控制台中命令执行的细节和过程打印出来。
    • --app 指定app name
    • --local 如果你的ServerLess服务在本地机器的这里需要指定,如果在远程机器上,这里就不需要它

    img

    执行过程中,fnproject会自动构建docker镜像,并自动执行安装npm install , 执行完后,我们会看到上面的结果。

    验证并访问函数应用

    我们如何访问这个Severless服务呢?FnProject提供了两种调用方式。

    第一种是采用CLI命令行的方式去调用

    fn invoke nodeApp nodetest  #invoke是fn提供给我们可以直接调用funcApp的命令
    {"message":"Hello World"}  #这个是控制台的打印输出
    复制代码

    上面的函数例子,我们发现有一个input参数, 那么我们怎么将参数传递给func app呢?

    这里我们可以通过shell的管道命令传递过去。

    echo -n '{"name":"Felix"}' | fn invoke nodeApp nodetest --content-type application/json
    {"message":"Hello Felix"}  #这个是控制台的打印输出
    复制代码

    第二种就是采用API接口方式去调用。

    首先我们需要获取函数的Api地址,通过下面命令可以获取到Api地址。

    fn inspect function nodeApp nodetest
    {
            "annotations": {
                    "fnproject.io/fn/invokeEndpoint": "http://localhost:8080/invoke/01FG6BBGV9NG8G00GZJ0000002"
            },
            "app_id": "01FG681T38NG8G00GZJ0000001",
            "created_at": "2021-09-22T08:53:31.113Z",
            "id": "01FG6BBGV9NG8G00GZJ0000002",
            "idle_timeout": 30,
            "image": "nodetest:0.0.2",
            "memory": 128,
            "name": "nodetest",
            "timeout": 30,
            "updated_at": "2021-09-22T08:53:31.113Z"
    }
    复制代码

    http://localhost:8080/invoke/01FG6BBGV9NG8G00GZJ0000002 这个地址就是我们nodetest 服务的请求地址。我们可以通过Postman调用,可以在代码里面通过ajax调用。

    这里我用curl去演示一下。我就一步到了,用POST请求直接传递数据调用接口

    curl -X "POST" -H "Content-Type: application/json" -d '{"name":"Felix"}' http://localhost:8080/invoke/01FG6BBGV9NG8G00GZJ0000002
    {"message":"Hello Felix"}  #这个是控制台的打印输出
    复制代码

    到这里,我们关于一些基本的能力都讲完了,那我们讲一点高级的应用。比如如何访问数据库,自定义DockerFile文件,如何debug等。

    七、ServerLess进阶演示

    首先我们先来看一看,如何访问Mysql, 首先我们先用docker装一下Mysql

    #先拉取镜像
    docker pull mysql:5.7 
    #启动mysql镜像
    docker run -p 3306:3306 --name mysql -e MYSQL_ROOT_PASSWORD=123456 -d mysql:5.7
    复制代码

    环境变量处理,对于Mysql连接的话,我们需要考虑对db_host单独配置。避免写死在代码中。

    FnProject提供了丰富的环境变量,供我们使用

    • DB_HOST_URL 数据库的地址链接.

    • DB_USER 数据库的用户名.

    • DB_PASSWORD 数据库的密码.

    #配置数据库基本信息 
    fn cf a nodeApp DB_HOST_URL 172.29.149.191  #这里需要指定容器宿主Ip
    fn cf a nodeApp DB_HOST_PORT 3306
    fn cf a nodeApp DB_NAME db_test
    fn cf a nodeApp DB_USER root
    fn cf a nodeApp DB_PASSWORD 123456
    
    #通过此命令可以查看环境变量
    fn ls cf a nodeApp
    复制代码

    目前是一个空的数据库,我们需要创建简单数据库和一张表结构

    简单起见,我们通过命令行创建数据库和表吧。方便后面的演示。

     #进入mysql容器内
     docker exec -it mysql bash
     #CLI登录mysql
     mysql -uroot -p123456
     #创建数据库
     create database db_test;
     use db_test;
     #创建表
    CREATE TABLE IF NOT EXISTS `article`(
            `article_id` INT UNSIGNED AUTO_INCREMENT, 
            `article_title` VARCHAR(100) NOT NULL,
            `article_author` VARCHAR(40) NOT NULL,
            PRIMARY KEY ( `article_id` )
    )ENGINE=InnoDB DEFAULT CHARSET=utf8;
    #插入一条测试数据
    INSERT INTO article (article_title,article_author) VALUES("Attention is all you need","Bengio")
    复制代码

    然后给我们的nodeTest 函数应用赋能。 安装连接mysql的npm包.

    传统mysql连接拼接sql语句,我一直不提倡用这个方式。 我选一个支持sql builder的npm工具。

    这里我用一款国外比较流行的库knex(我就不介绍这款工具的使用了,大家自行学习)

    然后下面我们修改func.js的代码

    const fdk=require('@fnproject/fdk');
    
    
    fdk.handle(async function(input,ctx){
      let name = 'World';
      if (input.name) {
        name = input.name;
      }
      const knex = require('knex')({
        client: 'mysql',
        connection: {
          host : ctx.config['DB_HOST_URL'], //获取数据库配置环境变量
          port : ctx.config['DB_HOST_PORT'],
          user : ctx.config['DB_USER'],
          password : ctx.config['DB_PASSWORD'],
          database : ctx.config['DB_NAME']
        }
      });
      // 根据传入的 name查询数据表
      const rows = await knex('article').where('article_title','like',`%${name}%`)
      return rows
    })
    #下面我们将代码重新部署一下
    fn --verbose deploy --app nodeApp --local
    # 然后执行接口
    curl -X "POST" -H "Content-Type: application/json" -d '{"name":"you"}' http://localhost:8080/invoke/01FG6BBGV9NG8G00GZJ0000002
    # 下面直接获得接口返回的JSON
    [{"article_id":1,"article_title":"Attention is all you need","article_author":"Bengio"}]
    复制代码

    到此我们完成对数据库的访问。 举一反三同样,我们同样也可以访问redis,mongodb等外部服务。 但是这里有一个问题,我们缺少连接池,每次都需要重新创建一个新连接,这种方式不太高效。但对一些小的应用,用完就关闭,也可以满足了。 但做好也有办法可以解决,一个是将连接操作放外面。或者对这种连接操作,再单独做函数应用,类似微服务化的方式。在这里我就不展开讲了。 这里只是让大家通过自己动手搭建,来逐步理解ServerLess是怎么工作的。

    八、ServerLess最后总结

    在此,我只是简单的动手搭建了一个最基本的ServerLess服务,真正用于生产,还需要考虑更多。服务注册发现、健康检查、配置、容错,监控,流量,接入Kubernetes等 都是需要考虑的事情。 但ServerLess也有一定的弊端,开发、调试有点不太方便,你需要自己写单元测试提前在本地运行好。不然就只能看控制台日志。本地做的话,也需要注意,把业务拆细一点。代码太长也是不利于调试的。 对前端来说,可以用ServerLess快速实现一些想法,不依赖服务端的介入。还是很不错的。


    作者:FelixCoder
    链接:https://juejin.cn/post/7011383405799538719
    来源:稀土掘金
    著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
  • 相关阅读:
    一起谈.NET技术,用Dojo实现Ajax请求:XHR、跨域、及其他 狼人:
    一起谈.NET技术,微软 Share Point 2010 企业应用解决方案 狼人:
    一起谈.NET技术,MVC2.0本地化(另类解决方案)<上> 狼人:
    一起谈.NET技术,WCF的问题和Using语句块 狼人:
    一起谈.NET技术,微软缘何认为VB与C#需要异步语法 狼人:
    一起谈.NET技术,SharePoint 2010 BI:Chart WebPart 狼人:
    一起谈.NET技术,微博是个大金矿,使用VS2010编译QOAuth支持微博通用认证OAuth实现SINA微博登陆 狼人:
    一起谈.NET技术,不要在using语句中调用WCF服务 狼人:
    一起谈.NET技术,使用Dijit实现界面组件化开发 狼人:
    一起谈.NET技术,通过自定义配置实现插件式设计 狼人:
  • 原文地址:https://www.cnblogs.com/cangqinglang/p/16507892.html
Copyright © 2020-2023  润新知