本文介绍当前serverless框架的函数概念,Serverless框架还允许用户监视代码存储库中的更改(例如代码触发器等),并在每次提交时自动构建函数镜像。函数代码、依赖项和/或二进制文件可以驻留在外部存储库(例如S3对象存储桶或Git存储库)中,或由用户直接提供。如果代码在外部存储库中,则用户需要指定路径和接入凭证。函数可能依赖于外部库或二进制文件,这些需要由用户提供,包括描述其构建过程的方式(例如,使用 Dockerfile、Zip等)。另外,可以通过一些二进制打包(例如OCI镜像)将函数提供给框架。
接下来让我们介绍一下serverless函数概念。
一、函数定义
函数代码、依赖项或二进制文件可以驻留在外部存储库(例如S3对象存储桶或Git存储库)中,或由用户直接提供。如果代码在外部存储库中,则用户需要指定路径和凭据。Serverless 框架还允许用户 watch 代码存储库中的更改(例如,使 Webhook),并在每次提交时自动构建函数镜像/二进制文件。函数可能依赖于外部库或二进制文件,这些需要由用户提供,包括描述其构建过程的方式(例如,使用 Dockerfile、Zip)。另外,可以通过一些二进制打包(例如OCI镜像)将函数提供给框架。
函数定义
Serverless 函数定义可能包含以下规范和元数据,该函数定义:
- 唯一ID
- 名称
- 说明
- Label(或tag)
- 版本ID/或版本别名
- 版本创建时间
- 上次修改时间(函数定义)
- 函数处理程序
- 运行时语言
- 代码 + 依赖关系或代码路径和凭据
- 环境变量
- 执行角色和密钥
- 资源(所需的 CPU、内存)
- 执行超时处理
- 日志记录失败(Dead Letter Queue,)
- 网络策略/VPC
- 数据绑定
- 网关接入等
Dead Letter Queue
中文译作“死信队列”,在消息队列中,死信队列是一种服务实现,用于存储满足以下一个或多个条件的消息: 发送到不存在的队列的消息。 超出队列长度限制。 超
出了邮件长度限制。 消息被另一个队列交换拒绝。 消息达到阈值读取计数器编号,因为它没有被消耗。有时这被称为“退出队列”。 存储这些消息的死信队列允许
开发人员查找常见模式和潜在的软件问题。
二、元数据
函数框架可能包括以下函数元数据:
- 版本:每个函数版本应具有唯一的标识符,此外,可以使用一个或多个别名(例如“latest”、“production”、“beta”)来标记版本。API 网关和事件源会将流
量/事件路由到特定的函数版本。 - 环境变量:用户可以指定在运行时将提供给函数的环境变量。环境变量也可以从机密和加密的内容派生,也可以从平台变量派生(例如,像Kubernetes EnvVar定
义)。环境变量使开发人员能够控制函数行为和参数,而无需修改代码和/或重建函数,从而获得更好的开发人员体验和函数重用。 - 执行角色:该函数应在特定的用户或角色身份下运行,以授予和审核其对平台资源的访问权限。
- 资源:定义所需或最大的硬件资源,例如函数使用的内存和CPU。
- 超时:指定函数调用在平台终止之前可以运行的最长时间。
- 故障日志(死信队列):队列或流的路径,它将存储具有适当详细信息的失败函数执行列表。
- 网络策略:分配给函数的网络域和策略(函数与外部服务/资源进行通信)。
- 执行语义:指定应如何执行函数(例如,每个事件至少执行一次,最多执行一次,恰好一次)。
三、数据绑定
某些 Serverless 框架允许用户指定函数使用的输入/输出数据资源,这使开发变得更简单,性能更高(在执行期间保留数据连接,可以预取数据等)以及更好的安全
性(数据资源凭据是上下文的一部分,而不是代码)。
绑定数据可以采用文件、对象、记录、消息等形式,函数说明可以包括一组数据绑定定义,每个定义都指定数据资源、其凭证和使用参数。数据绑定可以引用事件数据
(例如,DB 键是从事件 “username” 字段派生的)
四、函数输入
函数输入包括事件数据和元数据,并且可以包括上下文对象。
事件数据和元数据
事件详细信息应传递给函数处理程序,不同的事件可能具有不同的元数据,因此希望函数能够确定事件的类型并轻松解析公共和特定于事件的元数据。
可能需要将事件类与实现分离,例如:不管流存储是 Kafka 还是 Kinesis,处理消息流的函数都可以运行。在这两种情况下,它将接收消息正文和事件元数据,消
息可能在不同框架之间路由。
事件可以包括单个记录(例如,在请求/响应模型中),也可以接受多个记录或微批处理(例如,在流模式中)。
FaaS 解决方案使用的常见事件数据和元数据的示例:
- Event Class/Kind
- 版本
- 事件 ID
- Event Source/Origin
- 来源身份
- 内容类型
- 邮件正文
- 时间戳记
事件/记录特定元数据的示例
- HTTP:Path、Method、Header、查询参数
- 消息队列:Topic、Header
- 记录流(Record Stream):表、键、操作、修改时间、旧字段、新字段
事件源结构的示例:
- http://docs.aws.amazon.com/lambda/latest/dg/eventsources.html
- https://docs.microsoft.com/zh-cn/azure/azure-functions/functions-triggers-bindings
- https://cloud.google.com/functions/docs/concepts/events-triggers
一些实现将 JSON 视为将事件信息传递给函数的机制。对于高速函数(例如,流处理)或低能耗设备(IoT),这可能会增加大量的序列化/反序列化开销。在这些情况
下,可能值得考虑使用本地语言结构或其他序列化机制。
五、函数上下文
调用函数时,框架可能希望提供对跨多个函数调用的平台资源或常规属性的访问,而不是将所有静态数据放入事件中或强制该函数在每次调用时初始化平台服务。上下文(Context)可以是一组输入属性、环境变量或全局变量。有的实现将这三者结合使用。
上下文示例:
- 函数名称、版本、ARN
- 内存限制
- 请求 ID
- Cloud Region
- 环境变量
- 安全密钥/令牌
- 运行时/绑定路径
- 日志
- 数据绑定
有的实现初始化日志对象(例如, AWS 中的全局变量或 Azure 中的部分上下文),用户可以使用平台集成的工具查看日志来跟踪函数执行。除了传统的日志记录,未来的实现可能会将计数器/监控和跟踪活动抽象为平台上下文的一部分,以进一步提高函数的可用性。数据绑定是函数上下文的一部分,平台根据用户配置启动与外部数据资源的连接,并且这些连接可以在多个函数调用之间重用。
五、函数输出
当函数退出时,它可能:
- 将值返回给调用方(例如,在 HTTP 请求/响应示例中)
- 将结果传递到工作流程中的下一个执行阶段
- 将输出写入日志
应该有确定的方式通过返回的错误值或退出代码来知道函数是成功还是失败。
函数输出可以是结构化的(例如 HTTP 响应对象)或非结构化的(例如某些输出字符串)。