跳到主要内容
版本:4.0.0 🚧

部署到阿里云函数计算

阿里云 Serverless 是国内最早提供 Serverless 计算服务的团队之一, 依托于阿里云强大的云基础设施服务能力,不断实现技术突破。目前,淘宝、支付宝、钉钉、高德等已经将 Serverless 应用于生产业务,云上的 Serverless 产品在南瓜电影、网易云音乐、爱奇艺体育、莉莉丝等数万家企业成功落地。

阿里云 Serverless 包含许多产品,如函数计算 FC,轻量应用引擎 SAE 等,本文主要使用了其 函数计算 部分。

下面是常见的函数触发器的使用、测试和部署方法。

部署类型

阿里云的函数计算部署类型比较多,根据运行的不同容器有以下几种。

名称能力限制描述部署媒介
内置运行时不支持流式请求和响应;不支持太大的请求和响应入参只能部署函数接口,不需要自定义端口,构建出 zip 包给平台部署zip 包部署
自定义运行时(Custom Runtime)可以部署标准应用,启动 9000 端口,使用平台提供的系统镜像,构建出 zip 包给平台部署zip 包部署
自定义容器(Custom Container)可以部署标准应用,启动 9000 端口,自己控制所有环境依赖,构建出 Dockerfile 提供给平台部署Dockerfile 部署

在平台上分别对应创建函数时的三种方式。

纯函数开发(内置运行时)

下面我们将以使用 "内置运行时部署" 纯函数作为示例。

触发器代码

发布不包含触发器的函数,这是最简单的类型,可以直接通过 event 手动触发参数,也可以在平台绑定其他触发器。

通过直接在代码中的 @ServerlessTrigger 装饰器绑定事件触发器。

import { Provide, Inject, ServerlessTrigger, ServerlessTriggerType } from '@midwayjs/core';
import { Context } from '@midwayjs/faas';

@Provide()
export class HelloAliyunService {
@Inject()
ctx: Context;

@ServerlessTrigger(ServerlessTriggerType.EVENT)
async handleEvent(event: any) {
return event;
}
}
信息

触发器的更多配置由于和平台相关,将写在 s.yaml 中,如定时任务的时间间隔等,更多细节请查看下面的部署段落。

类型定义

FC 的定义将由适配器导出,为了让 ctx.originContext 的定义保持正确,需要将其添加到 src/interface.ts 中。

// src/interface.ts
import type {} from '@midwayjs/fc-starter';

此外,还提供了各种 Event 类型的定义。

// Event 类型
import type {
OSSEvent,
MNSEvent,
SLSEvent,
CDNEvent,
TimerEvent,
APIGatewayEvent,
TableStoreEvent,
} from '@midwayjs/fc-starter';
// InitializeContext 类型
import type { InitializeContext } from '@midwayjs/fc-starter';

本地开发

HTTP 触发器和 API Gateway 类型可以通过本地 npm run dev 和传统应用类似的开发方式进行本地开发,其他类型的触发器本地无法使用 dev 开发,只能通过运行 npm run test 进行测试执行。

本地测试

和传统应用测试类似,使用 createFunctionApp 方法创建函数 app, 使用 close 方法关闭。

import { Application, Context, Framework } from '@midwayjs/faas';
import { mockContext } from '@midwayjs/fc-starter';
import { createFunctionApp } from '@midwayjs/mock';

describe('test/hello_aliyun.test.ts', () => {

it('should get result from event trigger', async () => {

// create app
const app: Application = await createFunctionApp<Framework>(join(__dirname, '../'), {
initContext: mockContext(),
});

// ...

await close(app);
});
});

mockContext 方法用来模拟一个 FC Context 数据结构,可以自定传递一个类似的结构或者修改部分数据。

import { Application, Context, Framework } from '@midwayjs/faas';
import { mockContext } from '@midwayjs/fc-starter';
import { createFunctionApp } from '@midwayjs/mock';

describe('test/hello_aliyun.test.ts', () => {

it('should get result from event trigger', async () => {

// create app
const app: Application = await createFunctionApp<Framework>(join(__dirname, '../'), {
initContext: Object.assign(mockContext(), {
function: {
name: '***',
handler: '***'
}
}),
});

// ...

await close(app);
});
});

不同的触发器有着不同的测试方法,下面列出了一些常见的触发器。

通过 getServerlessInstance 获取类实例,直接调用实例方法,传入参数进行测试。

import { HelloAliyunService } from '../src/function/hello_aliyun';

describe('test/hello_aliyun.test.ts', () => {

it('should get result from event trigger', async () => {
// ...
const instance = await app.getServerlessInstance<HelloAliyunService>(HelloAliyunService);
expect(await instance.handleEvent('hello world')).toEqual('hello world');
// ...
});
});

纯函数部署(内置运行时)

以下将简述如何使用 Serverless Devs 部署到阿里云函数。

1、确认启动器

在项目根目录的 f.ymlprovider 段落处确保 starter 为 @midwayjs/fc-starter

provider:
name: aliyun
starter: '@midwayjs/fc-starter'

2、安装 Serverless Devs 工具

aliyun 使用 Serverless Devs 工具 进行函数部署。

你可以将其安装到全局。

$ npm install @serverless-devs/s -g

参考 密钥配置 文档进行配置。

3、编写一个 Serverless Devs 描述文件

在根目录创建一个 s.yaml ,添加以下内容。

edition: 1.0.0
name: "midwayApp" # 项目名称
access: "default" # 秘钥别名

vars:
service:
name: fc-build-demo
description: 'demo for fc-deploy component'
services:
project-0981cd9b07:
component: devsapp/fc
props:
region: cn-hangzhou
service: ${vars.service}
function:
name: hello # 函数名
handler: helloHttpService.handleHTTPEvent
codeUri: '.'
initializer: helloHttpService.initializer
customDomains:
- domainName: auto
protocol: HTTP
routeConfigs:
- path: /*
serviceName: ${vars.service.name}
functionName: helloHttpService-handleHTTPEvent
triggers:
- name: http
type: http
config:
methods:
- GET
authType: anonymous

每加一个函数都需要调整 s.yaml 文件,为此Midway 提供了一个 @midwayjs/serverless-yaml-generator 工具用来将装饰器函数信息写入 s.yaml

{
"scripts": {
+ "generate": "serverless-yaml-generator",
},
"devDependencies": {
+ "@midwayjs/serverless-yaml-generator": "^1.0.0",
},
}

通过执行下面的命令,可以将现有函数信息填充到 s.yaml 中,并生成入口文件,方便排查问题。

$ npm run generate

工具将以函数名作为 key 在 s.yaml 中查找配置。

  • 1、如果存在函数,则会覆盖特定字段,比如 handler,http 触发器的 methods
  • 2、如果不存在函数,则会添加一个新函数
  • 3、工具不会写入 http 的路由方法,为了简化后续更新,可以提供一个 /* 路由(如示例)

我们推荐用户只在装饰器定义基础函数名,函数 handler,以及基础触发器信息(比如 http 触发器的 path 和 method),其余都写在 yaml 中。

s.yaml 的完整配置较为复杂,具体请参考 描述文件规范

4、编写一个部署脚本

由于部署有构建,拷贝等多个步骤,我们可以编写部署脚本统一这个过程。

比如在项目根目录新建一个 deploy.sh 文件,内容如下。

#!/bin/bash

set -e

# 构建产物目录
export BUILD_DIST=$PWD/.serverless
# 构建开始时间,单位毫秒
export BUILD_START_TIME=$(date +%s%3N)

echo "Building Midway Serverless Application"

# 打印当前目录 cwd
echo "Current Working Directory: $PWD"
# 打印结果目录 BUILD_DIST
echo "Build Directory: $BUILD_DIST"

# 安装当前项目依赖
npm i

# 执行构建
./node_modules/.bin/tsc || return 1
# 生成入口文件
./node_modules/.bin/serverless-yaml-generator || return 1

# 如果 .serverless 文件夹存在,则删除后重新创建
if [ -d "$BUILD_DIST" ]; then
rm -rf $BUILD_DIST
fi

mkdir $BUILD_DIST

# 拷贝 dist、 *.json、*.yml 到 .serverless 目录
cp -r dist $BUILD_DIST
cp *.yaml $BUILD_DIST 2>/dev/null || :
cp *.json $BUILD_DIST 2>/dev/null || :
# 移动入口文件到 .serverless 目录
mv *.js $BUILD_DIST 2>/dev/null || :

# 进入 .serverless 目录
cd $BUILD_DIST
# 安装线上依赖
npm install --production

echo "Build success"

# 在 .serverless 目录进行部署
s deploy

可以将这个 deploy.sh 文件放到 package.jsondeploy 指令中,后续部署执行 npm run deploy 即可。

{
"scripts": {
"deploy": "sh deploy.sh"
}
}
提示
  • 1、 deploy.sh 只测试了 mac,其余平台可以自行调整
  • 2、脚本内容可以根据业务逻辑自行调整,比如拷贝的文件等

自定义运行时部署

1、创建项目

自定义运行时可以使用标准项目来部署,由于需要提供 9000 端口,需要创建 Midway koa/express/express 项目。

初始化项目请参考 创建第一个应用

2、调整端口

为了避免影响本地开发,我们仅在入口 bootstrap.js 处增加端口。

const { Bootstrap } = require('@midwayjs/bootstrap');

// 显式以组件方式引入用户代码
Bootstrap.configure({
globalConfig: {
koa: {
port: 9000,
}
}
}).run()

不同的框架修改端口请参考:

3、平台部署配置

  • 1、选择运行环境,比如 Node.js 18
  • 2、选择代码上传方式,比如可以本地打 zip 包上传
  • 3、启动命令指定 node bootstrap.js
  • 4、监听端口 9000

配置完成之后,上传压缩包即可部署完成。