Serverless Custom (Container) Runtime

本文已参与好文召集令活动,点击查看:后端、大前端双赛道投稿,2万元奖池等你挑战! javascript

这是第 106 篇不掺水的原创,想获取更多原创好文,请搜索公众号关注咱们吧~ 本文首发于政采云前端博客:Serverless Custom (Container) Runtimephp

雪霁.png

背景

咱们知道 Serverless 能够理解为 Serverless = FaaS + BaaS 。Serverless 应用中,对于服务端业务逻辑代码,开发者是以函数的形式去实现的,即 FaaS(函数即服务)。( Serverless 相关文章能够看下团队结合阿里云 FC 谈谈我对 FaaS 的理解)html

对于云厂商的 FaaS 平台,虽然他们支持多种编程语言及版本的标准运行环境,但毕竟仍是有限的。因此,为了知足用户更多个性化开发语言及版本的函数实现需求,他们提供了 Custom Runtime 服务,即可定制化运行环境,支持用户用任何编程语言编写的函数。前端

以阿里云函数计算 FC 为例,这是它所支持的开发语言列表:java

支持语言 运行环境
Node.js Node.js 6.10(runtime=nodejs6)、
Node.js 8.9.0(runtime=nodejs8)、
Node.js 10.15.3(runtime=nodejs10)
Node.js 12.16.1(runtime=nodejs12)
Python Python 2.7(runtime = python2.7)
Python 3.6(runtime = python3)
PHP PHP 7.2.7(Runtime=php7.2)
Java Java OpenJDK 1.8.0(runtime=java8)
C# .NET Core 2.1(runtime=dotnetcore 2.1)
Go Go Custom Runtime
Ruby Ruby Custom Runtime
PowerShell PowerShell Custom Runtime
TypeScript TypeScript Custom Runtime
F# F# Custom Runtime
C++ C++ Custom Runtime
Lua Lua Custom Runtime
Dart Dart Custom Runtime
其余语言 Custom Runtime

能够看出,对于咱们前端工程师,若是想使用阿里云 FC 平台,并不能为所欲为的使用 Node.js 和 TypeScript 。由于 Node.js,只支持表格中的四种版本,而 TypeScript ,FC 平台自身彻底不支持。因此要想使用 Node.js 的其它版本和 TypeScript,就须要自定义运行时。node

那么什么是 Custom Runtime 呢?python

概念

运行时( Runtime )指函数代码在运行时所依赖的环境,包括任何库、代码包、框架或平台。Custom Runtime 就是彻底由用户自定义函数的运行环境。git

FaaS 平台经过开放实现自定义函数运行时,支持根据需求使用任意开发语言的任意版本来编写函数。web

做用

阿里云官方文档中说到,基于 Custom Runtime 咱们能够实现这两件事:docker

  • 定制个性化语言(例如 Go、Lua、Ruby )和各类语言的小版本(例如 Python 3.七、Node.js 14)的执行环境,打造属于您的运行环境。
  • 一键迁移现有的 Web 应用或基于传统开发的 Web 项目到函数计算平台,不用作任何改造。

实现 Custom Runtime

本文将以阿里云 FC 为例,实现一个 Custom Runtime。其它平台好比腾讯云 SCF 等,原理和过程也都大体相同。

工做原理

Custom Runtime 本质上是一个 HTTP Server,代码里面包含一个名为 bootstrap 的启动文件,以后这个 HTTP Server 接管了函数计算平台的全部请求,包括事件调用或者 HTTP 函数调用等。

现在 Typescript 在 Node 中的应用已经愈来愈普遍,因此笔者将实现一个能够运行 TS 代码的 TypeScript 运行时。

操做步骤

准备工做

为了更快更好地玩转 Serverless 应用,须要先安装阿里云的一个 Fun工具,它是一个用于支持 Serverless 应用部署的工具,能帮助咱们便捷地管理函数计算、API 网关、日志服务等资源。它经过一个资源配置文件(template.yml),协助咱们进行开发、构建、部署操做。

安装配置过程以下:

(1)安装:

// 安装命令
$ npm install @alicloud/fun -g

// 执行 fun --version 检查安装是否成功
$ fun --version

3.6.21
复制代码

(2)安装好后,使用fun config命令配置帐户信息(配置文档),按照提示依次配置 Account ID、AccessKey ID、AccessKey Secret、Default Region Name。

配置完成后,先在本地建立一个 TypeScript 项目 custom-runtime-typescript,并安装相关依赖。

npm i typescript ts-node @types/node
复制代码

接下来,开始 Custom Runtime 的开发流程,一步一步打造属于本身的自定义运行环境。

1.搭建一个具备监听端口的 HTTP Server

  • 须要注意的是,这个服务必定要监听0.0.0.0:CAPort*:CAPort端口,默认是 9000。若是使用127.0.0.1:CAPort端口,会致使请求超时

用 TS 编写一个 HTTP Server 文件 server.ts 以下:

注意:在开发函数具体的逻辑以前,通常会确认开发的函数是事件函数仍是 HTTP 函数

import * as http from 'http';

// 建立一个 HTTP Server
const server = http.createServer(function (req: http.IncomingMessage, res: http.ServerResponse): void {
  var rid = req.headers["x-fc-request-id"];
  console.log(`FC Invoke Start RequestId: ${rid}`);
  
  var rawData = "";
  req.on('data', function (chunk) {
    rawData += chunk;
  });
  
  req.on('end', function () {
    // 处理业务逻辑 ……
    console.log(rawData);
    
    res.writeHead(200);
    res.end(rawData);
    console.log(`FC Invoke End RequestId: ${rid}`);
  });
});

server.timeout = 0; // never timeout
server.keepAliveTimeout = 0; // kee palive, never timeout

// 启动 HTTP 服务并监听 0.0.0.0:9000 端口
server.listen(9000, '0.0.0.0', function () {
  console.log('FunctionCompute typescript runtime inited.');
});


复制代码

编写完成后,能够先在本地测试该服务是否启动成功,经过安装在项目中的 ts-node 命令来运行上述代码:

# 启动 HTTP 服务

$ ./node_modules/.bin/ts-node server.ts
复制代码

启动后,在另外一个终端中使用 curl 命令测试:

$ curl 0.0.0.0:9000 -X POST -d "hello world" -H "x-fc-request-id:123" 

hello world
复制代码

若服务已正常启动,说明它能够在接收 HTTP 请求后处理业务逻辑,而后将处理结果再以 HTTP 响应的形式返回给 FaaS 平台。

2.建立一个启动目标 Server 的可执行文件 bootstrap

函数计算冷启动 Custom Runtime 时,会默认调用 bootstrap 文件启动自定义的 HTTP Server。而后这个 HTTP Server 接管了函数计算系统的全部请求。

  • bootstrap 是运行时入口引导程序文件,它会告诉 FaaS 如何启动你的自定义运行时。Custom Runtime 加载函数时会固定检索 bootstrap 同名文件,并执行该程序来启动 Custom Runtime 运行时。
  • bootstrap 需具有 777 或 755 可执行权限
  • 若是是 shell 脚本,必定要添加#!/bin/bash

建立 bootstrap 文件以下:

#!/bin/bash
./node_modules/.bin/ts-node server.ts
复制代码

3.编写资源配置文件 template.yaml

在当前目录下编写一份用于部署到函数计算的资源配置文件 template.yaml:

ROSTemplateFormatVersion: '2015-09-01'
Transform: 'Aliyun::Serverless-2018-04-03'
Resources:
  custom-runtime: # 服务名称
    Type: 'Aliyun::Serverless::Service' 
    Properties:
      Description: 'helloworld'
    custom-runtime-ts: # 函数名称
      Type: 'Aliyun::Serverless::Function' 
      Properties:
        Handler: index.handler # Handler 在此时没有实质意义,填写任意的一个知足函数计算 Handler 字符集约束的字符串便可, 例如 index.handler
        Runtime: custom # custom 表明自定义运行时
        MemorySize: 512
        CodeUri: './'
复制代码

4.部署、调用测试、完成

(1)使用fun deploy -y 命令将咱们的自定义运行时和业务逻辑代码全部资源部署到阿里云。

image-20210505220309647

(2)使用命令调用部署函数,验证

$ fun invoke -e "hello,my custom runtime"  
复制代码

image-20210505220520916

看到成功输出,就表明咱们的 custom runtime 大功告成了!它能够直接运行咱们写的 TS 代码了。

image-20210505223345696

实现 Custom Container Runtime

TS 的运行环境问题能够用 Custom Runtime 解决,可是 Node 某些版本平台不支持的问题,就不能用一样的办法了。由于 Node 是全局安装的,依赖系统环境。

FC 平台已经为咱们想好了此类问题的解决办法,为咱们提供了 Custom Container Runtime (自定义容器运行环境)的能力。FaaS 平台有这种能力,是由于它的底层实现原理是 Docker 容器,因此它经过运用容器技术,把咱们的应用代码和运行环境打包为 Docker 镜像,保持环境一致性。实现一次构建,处处运行。

工做原理

Custom Container Runtime 工做原理与Custom Runtime 基本相同:

  • 函数计算系统初始化执行环境实例前会扮演该函数的服务角色,得到临时用户名和密码并拉取镜像
  • 拉取成功后根据指定的启动命令 Command、参数 Args 及 CAPort 端口(默认 9000 )启动自定义的 HTTP Server。
  • 而后这个 HTTP Server 接管了函数计算系统的全部请求,包括来自事件函数调用及 HTTP 函数调用。

下面咱们自定义一个 Node v16.1.0 版本的容器运行环境。

操做步骤

1.自定义 HTTP Server

这一步和 Custom Runtime 相同,使用 Node.js Express 自定义一个 Http 服务 server.js,GET 和 POST 方法分别路由至不一样的 Handler:

// server.js 文件
'use strict';

const express = require('express');

// Constants
const PORT = 9000;
const HOST = '0.0.0.0';

// HTTP 函数调用
const app = express();
app.get('/*', (req, res) => {
  res.send(`Hello FunctionCompute, http function, runtime is : Node ${process.version}\n`);
});

// 事件函数调用
app.post('/invoke', (req, res) => {
  res.send(`Hello FunctionCompute, event function,runtime is : Node ${process.version}\n`);
});

// 启动 HTTP 服务并监听 9000 端口
var server = app.listen(PORT, HOST);
console.log(`Running on http://${HOST}:${PORT}`);

server.timeout = 0; // never timeout
server.keepAliveTimeout = 0; // keepalive, never timeout

复制代码

启动服务,本地测试一下:

# 启动 HTTP 服务
$ node server.js
复制代码
# 新开一个终端,经过 curl 命令测试
$ curl http://0.0.0.0:9000
Hello FunctionCompute, http GET, this runtime is : Node v11.5.0     # 这是我本地的 Node 版本,后面在自定义容器中会输出 v16.1.0 
复制代码

验证经过。

2.构建镜像并上传

一样的,须要先作两个准备工做:

接下来,先编写 Dockerfile,再构建包含咱们 Node 指定版本运行环境和应用代码的镜像,最后上传到本身的镜像仓库。

(有须要的同窗能够先看下这篇文章如何把一个 Node.js web 应用程序给 Docker 化

(1) 编写 Dockerfile:

# 基于基础镜像 node:16.1.0-alpine3.11 构建咱们本身的镜像
FROM node:16.1.0-alpine3.11

# 设置容器工做目录
WORKDIR /usr/src/app

# 将 package.json 和 package-lock.json 都拷贝到工做目录
COPY package*.json ./

# 安装依赖
RUN npm install

# 将当前目录下的全部文件拷贝到容器工做目录中
COPY . .

# 暴露容器 8080 端口
EXPOSE 8080

# 在容器中启动应用程序
ENTRYPOINT [ "node", "server.js" ]

复制代码

(2)安装启动 Docker,登陆阿里云镜像服务,构建并上传:

# 登陆
$ sudo docker login --username=xxx registry.cn-hangzhou.aliyuncs.com
复制代码

登陆成功后,先构建 Docker 镜像:

# 指定ACR镜像地址:其中 my_serverless 为你本身的容器命名空间;nodejs 为你本身的镜像仓库名称;v16.1.0 为镜像版本号
$ export IMAGE_NAME="registry.cnhangzhou.aliyuncs.com/my_serverless/nodejs:v16.1.0"
复制代码
# 构建镜像
# -t 给镜像取名字打标签,一般 name:tag 或者 name 格式
$ docker build -t $IMAGE_NAME .
复制代码

再启动容器,本地打开浏览器 http://localhost:9000/ 看是否能够正常响应,来验证咱们的自定义镜像是否能够运行成功:

# 启动容器: 将容器的 9000 端口映射到主机的 9000 端口
$ docker run -p 9000:9000 -d $IMAGE_NAME
复制代码
image-20210514150709489

验证经过后,最后上传镜像:

# 上传镜像
$ docker push $IMAGE_NAME  
复制代码

上传成功后,能够在阿里云镜像服务中看到咱们的镜像。后面就可使用它啦!

image-20210509153919228

3.定义 template.yaml

建立一个 template.yaml文件以下:

ROSTemplateFormatVersion: '2015-09-01'
Transform: 'Aliyun::Serverless-2018-04-03'
Resources:
  CustomContainerRuntime: # 服务名称
    Type: 'Aliyun::Serverless::Service'
    Properties:
      Policies:
        - AliyunContainerRegistryReadOnlyAccess
      InternetAccess: true
    nodejs-express-http: # 函数名称
      Type: 'Aliyun::Serverless::Function'
      Properties:
        Description: 'HTTP function powered by nodejs express'
        Runtime: custom-container # 表示自定义容器
        Timeout: 60
        CAPort: 9000 # 注意!这里Custom Container Runtime使用的监听端口必定要和HTTP Server监听的端口保持一致,不然会出现错误
        Handler: not-used
        MemorySize: 1024
        CodeUri: ./   # Root directory for the function or the Dockerfile path
        CustomContainerConfig: # 容器镜像配置
          # Sample image value: registry-vpc.cn-shenzhen.aliyuncs.com/fc-demo/nodejs-express:v0.1 使用同地域的VPC镜像地址加速
          Image: 'registry.cn-hangzhou.aliyuncs.com/my_serverless/nodejs:v16.1.0'
          Command: '[ "node"]'
          Args: '["server.js"]'
      Events:
        http-trigger-test:
          Type: HTTP
          Properties:
              AuthType: ANONYMOUS
              Methods: ['GET', 'POST', 'PUT']
复制代码
4.部署测试
# 使用命令部署到 FC 
$ fun deploy -y
复制代码
image-20210509163842880

部署成功后,咱们去 FC 平台上进行测试。

由于咱们在template.yaml 中配置的触发器是 http 触发器,因此咱们点击“执行”按钮进行调试,发现正常运行,返回结果为 runtime is : Node v16.1.0,说明咱们的自定义容器运行环境也成功实现了!

image-20210509163945647

小结

Custom Runtime 为咱们打破了 FaaS 平台对语言的限制;Custom Container Runtime 让开发者能够将应用代码和运行环境打包成容器镜像做为函数的交付物,优化开发者体验、提高开发和交付效率。

自定义(容器)运行时让咱们开发者使用 Serverless 的自由度更高,经过它们可让咱们无需代码改造,一键迁移咱们的 Web 应用。

参考资料

what-is-runtime

为阿里云 serverless 打造 Deno 运行时

Custom Runtime 说明

fun 工具

函数计算支持容器镜像-加速应用 Serverless 进程

Custom Runtime - 打破云函数语言限制

推荐阅读

Vite 特性和部分源码解析

我在工做中是如何使用 git 的

15 分钟学会 Immutable

开源做品

  • 政采云前端小报

开源地址 www.zoo.team/openweekly/ (小报官网首页有微信交流群)

招贤纳士

政采云前端团队(ZooTeam),一个年轻富有激情和创造力的前端团队,隶属于政采云产品研发部,Base 在风景如画的杭州。团队现有 40 余个前端小伙伴,平均年龄 27 岁,近 3 成是全栈工程师,妥妥的青年风暴团。成员构成既有来自于阿里、网易的“老”兵,也有浙大、中科大、杭电等校的应届新人。团队在平常的业务对接以外,还在物料体系、工程平台、搭建平台、性能体验、云端应用、数据分析及可视化等方向进行技术探索和实战,推进并落地了一系列的内部技术产品,持续探索前端技术体系的新边界。

若是你想改变一直被事折腾,但愿开始能折腾事;若是你想改变一直被告诫须要多些想法,却无从破局;若是你想改变你有能力去作成那个结果,却不须要你;若是你想改变你想作成的事须要一个团队去支撑,但没你带人的位置;若是你想改变既定的节奏,将会是“5 年工做时间 3 年工做经验”;若是你想改变原本悟性不错,但老是有那一层窗户纸的模糊… 若是你相信相信的力量,相信平凡人能成就非凡事,相信能遇到更好的本身。若是你但愿参与到随着业务腾飞的过程,亲手推进一个有着深刻的业务理解、完善的技术体系、技术创造价值、影响力外溢的前端团队的成长历程,我以为咱们该聊聊。任什么时候间,等着你写点什么,发给 ZooTeam@cai-inc.com

相关文章
相关标签/搜索