“功能”包-为无服务器应用程序打包设置标准

功能包

转载自原文,原文是发表在CNCF博客上

函数里有什么

创建无服务器应用程序是一个多步骤过程。此过程中的关键步骤之一是将想要部署到所选FaaS(功能即服务)平台中的无服务器功能打包。

在部署函数之前,需要两种类型的依赖关系:直接函数依赖关系和运行时依赖关系。让我们研究一下这两种类型。

直接函数依赖关系-这些对象是函数过程本身的一部分,包括:

  • 该函数的源代码或二进制
  • 函数使用的第三方二进制文件和库
  • 函数直接需要的应用程序数据文件

运行时函数依赖关系-这是与函数的运行时方面相关的数据。它不是函数直接需要的,而是配置或引用函数将运行的外部环境。例如:

  • 事件消息结构和路由设置
  • 环境变量
  • 运行时二进制文件,例如操作系统级库
  • 外部服务,如数据库等。

要运行函数服务,所有依赖项(直接依赖项和运行时依赖项)都需要打包并上传到无服务器平台。然而,目前还没有包装功能的通用标准。无服务器包格式是特定于供应商的,并且高度依赖于函数将要在其中运行的环境类型。这意味着,在大多数情况下,您的无服务器应用程序被锁定到单个提供者,即使您的函数代码本身抽象了特定于提供者的细节。

功能包

本文探讨了为开放和可扩展的“函数包”创建标准的机会,该标准支持跨不同的FaaS供应商部署无服务器函数二进制以及一些额外的元数据。

函数运行时环境

在研究当今常见的运行时FaaS提供程序时,我们看到了两种主流的运行函数方法:容器函数和自定义函数运行时。让我们仔细看看每一种类型。

容器函数运行时

该方法使用基于容器的运行时(如Kubernetes),是on-prem FaaS解决方案的常用运行时方法。顾名思义,它通常在某个级别向用户公开容器运行时语义。下面是它的工作原理。

容器入口点与事件层一起用作函数,以使用正确的参数集调用特定的容器。函数是用码头工人建造或任何其他图像生成器来创建OCI容器图像作为最终的包格式。

基于容器的无服务器运行时的例子包括:谷歌KnativeBitnami这样的Kubeless, IguazioNuclio和Apache的OpenWhisk

容器函数的主要问题是开发一个函数通常需要理解运行时环境;开发人员需要将他们的功能编织到容器中。有时候这个过程被框架隐藏了,但是开发人员仍然需要编写Dockerfile来更好地控制操作系统级的服务和映像结构。这导致了函数与运行时环境的高度耦合。

自定义函数运行时

自定义函数运行时通常由云提供商提供。它们提供了一种“干净”的模型,在这种模型中,只需用您最喜欢的编程语言编写处理程序回调就可以创建函数。与基于容器的函数相比,运行时细节完全留给云提供商(即使在幕后,运行时是基于容器的情况下)。

无服务器自定义函数运行时的例子有:AWSλAzure的功能而且谷歌云函数

自定义运行时环境为函数使用以语言为中心的模型。软件语言已经有了健康的打包实践(如Java jar, npm包或Go模块)。但是,它们的等效功能还没有通用的打包格式。这就造成了对云供应商的锁定。已经做了一些努力来定义一个通用的部署模型,(例如AWS无服务器应用程序模型(SAM)和开源无服务器框架),但是这些模型假设一个自定义二进制包已经存在,或者它们可能包括按照每个云提供商标准构建它的过程。

需要一个功能包

为了能够在生产中使用函数,我们需要使用对它们的稳定引用,以实现部署、升级和回滚到特定函数版本的可重复性。这可以通过一个不可变的、有版本控制的、密封的包来实现,该包包含函数及其所有直接依赖项。

容器映像可以满足这些要求,因为它们提供了通用的包格式。2022年世界杯预选赛赛程表但是,由于将函数与容器运行时的详细信息紧密耦合,会产生“污染”函数的副作用。自定义运行时还表现出与其函数包的耦合。虽然它们提供干净的函数,但它们使用专有的包格式,将函数依赖关系与运行时依赖关系混合在一起。

我们需要的是一个清晰的分离:一个干净的函数及其在本地“函数包”中的依赖关系,与运行时特定依赖关系的外部定义分开。这种分离将允许我们在不同的无服务器平台上重用一个函数,只需要根据需要添加外部配置。

功能包

让我们重新检查一下构建和运行基于容器的函数所需的步骤。我们可以将其视为四个步骤:

  1. 包装:用(直接+运行时)函数依赖关系和入口点定义构建容器映像。
  2. 坚持:将图像推至a容器注册表这样我们就有了一个稳定的参考。
  3. 安装:将映像拉到运行时,并根据特定于运行时的依赖项对其进行配置。
  4. 运行:在定义的入口点接受函数事件。

这个过程在容器运行时非常有效。我们可以试着把它表达成一个更广义的观点:

  1. 包装:构建一个包含直接依赖项(或对它们的引用)和入口点定义的函数包
  2. 坚持:将包上传到包注册表
  3. 安装:将包(及其直接依赖项)下载到运行时并配置特定于运行时的依赖项
  4. 运行:在定义的入口点接受函数事件。

创建功能包

这个通用流程可以应用于许多无服务器提供商!开发人员只需要担心创建一个干净的函数包并保持它的持久性。运行时配置可以在安装时提供,并且可以是特定于供应商的。

一个函数包将包含:

  • 函数文件-以源代码或二进制格式
  • 直接依赖-库和数据文件
  • 入口点定义

开发人员的体验是简单的,并且以编程为中心,因此我们可以创建映射到特定语言类型的标准“配置文件”。例如:

Java配置文件 Golang概要 码头工人档案
函数文件 JAR文件 Go模块源文件 构建文件引用到由入口点运行的通用文件
直接依赖 为jar生成pom文件,其中包含依赖项+数据文件的完整平面列表 go.modfile containing the dependencies + data files 构建对基本映像+其他服务文件和数据的文件引用
入口点定义 类引用 “主”包参考 构建图像入口点的文件引用

依赖性呢?

如果在安装之前运行库可以可靠地提供函数包,则函数包不一定要物理地嵌入二进制依赖项。相反,只能声明对依赖项的引用。例如,在POM文件中声明的外部JAR坐标,或者在go.mod文件。这些依赖项将在安装期间由运行时提取——类似于容器映像的提取方式来自docker注册表通过函数运行时。

通过使用稳定的引用,我们保证了可重复性和重用性。我们还创建了更轻的包,允许通过来自更接近运行时的注册表的依赖项更快、更经济地安装函数,而不必每次都重新加载它们。

功能包

总结

为公共运行时语言创建概要文件允许功能轻松地跨不同的无服务器FaaS提供商移动,添加特定于供应商的有关运行时配置的信息,并且只在安装阶段执行事件。对于应用程序开发人员来说,这意味着他们可以避免厂商锁定,专注于编写应用程序的业务逻辑,而不必熟悉操作方面。通过创建可共享的函数包作为CNCF无服务器标准,可以更快地实现这一目标。