本文发表于一年多前。旧文章可能包含过时内容。请检查页面中的信息自发布以来是否已变得不正确。

Fission:Kubernetes 的无服务器函数即服务

Fission 是一个基于 Kubernetes 构建的函数即服务 (FaaS) / 无服务器函数框架。

Fission 允许您轻松地在 Kubernetes 上从函数创建 HTTP 服务。它在源代码级别工作,并抽象了容器镜像(在大多数情况下)。它还简化了 Kubernetes 的学习曲线,使您无需深入了解 Kubernetes 即可创建有用的服务。

要使用 Fission,您只需创建函数并通过 CLI 添加它们。您可以将函数与 HTTP 路由、Kubernetes 事件或其他触发器关联起来。Fission 目前支持 NodeJS 和 Python。

函数在触发器触发时被调用,并且它们只在运行时消耗 CPU 和内存。空闲函数除了存储之外不消耗任何资源。

为什么要基于 Kubernetes 构建 FaaS 框架?

我们认为需要一个 FaaS 框架,它可以在各种基础设施上运行,包括公共云和本地基础设施。接下来,我们必须决定是从头开始构建它,还是在现有编排系统之上构建。很快就清楚,我们不应该从头开始构建——我们最终只会重新发明集群管理、调度、网络管理等等。

Kubernetes 提供了一个功能强大且灵活的编排系统,拥有全面的 API,并由一个强大且不断壮大的社区提供支持。在此基础上构建意味着 Fission 可以将容器编排功能留给 Kubernetes,并专注于 FaaS 功能。

我们不希望拥有独立 FaaS 集群的另一个原因是,FaaS 与其他基础设施结合使用效果最佳。例如,它可能适用于小型 REST API,但它需要与其他服务协同工作才能存储状态。FaaS 也是一种出色的机制,可用于事件处理程序处理来自存储、数据库以及 Kubernetes 本身的消息通知。Kubernetes 是所有这些服务协同工作的绝佳平台。

部署和使用 Fission

Fission 可以通过 `kubectl create` 命令安装:请参阅项目 README 获取说明。

以下是如何编写一个“hello world”HTTP 服务

$ cat \> hello.py

def main(context):

    print "Hello, world!"


$ fission function create --name hello --env python --code hello.py --route /hello


$ curl http://\<fission router\>/hello

Hello, world!

Fission 负责将函数加载到容器中,将请求路由到容器中等等。我们将在下一节中详细介绍。

Fission 如何在 Kubernetes 上实现

FaaS 框架的核心必须 (1) 将函数转换为服务,并且 (2) 管理这些服务的生命周期。

实现这些目标有多种方法,每种方法都有不同的权衡。框架应该在源代码级别操作,还是在 Docker 镜像级别操作(或者介于两者之间,例如“buildpacks”)?函数首次运行时可接受的开销是多少?这些选择会影响平台的灵活性、易用性、资源使用和成本,当然还有性能。

打包、源代码和镜像

我们的目标之一是让 Fission 对新用户来说非常易于使用。我们选择在源代码级别操作,这样用户就可以避免处理容器镜像构建、将镜像推送到注册表、管理注册表凭据、镜像版本控制等等。
然而,容器镜像是在源代码级别操作,这样用户就可以避免处理容器镜像构建、将镜像推送到注册表、管理注册表凭据、镜像版本控制等等。

然而,容器镜像才是打包应用程序最灵活的方式。一个纯粹的源代码级别接口将不允许用户打包二进制依赖项,例如。

因此,Fission 采用混合方法——包含函数动态加载器的容器镜像。这种方法允许大多数用户纯粹在源代码级别使用 Fission,但在需要时可以自定义容器镜像。

这些镜像在 Fission 中被称为“环境镜像”,它们包含语言运行时(如 NodeJS 或 Python)、一组常用依赖项和函数的动态加载器。如果这些依赖项足以满足用户正在编写的函数,则无需重新构建镜像。否则,可以修改依赖项列表并重新构建镜像。

这些环境镜像仅仅是Fission中特定于语言的部分。它们向框架的其余部分呈现统一的接口。这种设计使得Fission能够轻松扩展到更多的语言。

冷启动性能

无服务器函数的目标之一是函数仅在运行时才使用 CPU/内存资源。这优化了函数的资源成本,但代价是当从空闲状态启动时会产生一些性能开销(“冷启动”开销)。

冷启动开销在许多用例中都很重要。特别是,对于用于交互式用例的函数——例如Web或移动应用程序,用户正在等待操作完成——几秒钟的冷启动延迟是不可接受的。

为了优化冷启动开销,Fission 为每个环境都保留了一个正在运行的容器池。当一个函数的请求到来时,Fission 无需部署一个新的容器——它只需选择一个已经运行的容器,将函数复制到容器中,动态加载它,并将请求路由到该实例。此过程的开销对于 NodeJS 和 Python 函数大约为 100 毫秒。

Fission 在 Kubernetes 上的工作原理

fission-arch.png Fission 被设计为一组微服务。一个控制器负责跟踪函数、HTTP
路由、事件触发器和环境镜像。一个池管理器管理空闲环境容器池、将函数加载到这些容器中以及在函数实例空闲时终止它们。一个路由器接收 HTTP 请求并将其路由到函数实例,并在必要时从池管理器请求一个实例。

控制器提供 Fission API。所有其他组件都会监视控制器以获取更新。路由器作为 LoadBalancer 或 NodePort 类型的 Kubernetes Service 公开,具体取决于 Kubernetes 集群的托管位置。

当路由器收到请求时,它会查找缓存以查看该请求是否已有一个服务应路由到。如果不是,它会查找要映射请求的函数,并向池管理器请求一个实例。池管理器有一个空闲的 Pod 池;它选择一个,将函数加载到其中(通过向 Pod 中的一个 sidecar 容器发送请求),并将 Pod 的地址返回给路由器。路由器将请求代理到该 Pod。该 Pod 会被缓存以供后续请求使用,如果它空闲了几分钟,它就会被终止。

(目前,Fission 将一个函数映射到一个容器;计划在稍后版本中实现自动扩缩到多个实例。对于不需要隔离的情况,也计划复用函数 Pod 来托管多个函数。)

Fission 的使用场景

机器人、Webhook、REST API
Fission 是一个很好的框架,可以用来创建小型 REST API、实现 webhook 以及为 Slack 或其他服务编写聊天机器人。

作为简单 REST API 的一个例子,我们制作了一个小型留言簿应用程序,它使用函数来读写留言簿,并与 Redis 实例协同工作以跟踪状态。您可以在 Fission GitHub 仓库中找到该应用程序。

该应用程序包含两个端点——GET 端点列出 Redis 中的留言簿条目并将其渲染为 HTML。POST 端点将新条目添加到 Redis 中的留言簿列表。仅此而已——无需管理 Dockerfile,更新应用程序就像调用 `fission function update` 一样简单。

处理 Kubernetes 事件
Fission 还支持根据 Kubernetes 观察来触发函数。例如,您可以设置一个函数来观察特定命名空间中匹配特定标签的所有 Pod。该函数在其上下文中获取序列化对象和观察事件类型(添加/删除/更新)。

这些事件处理函数可用于简单监控——例如,每当集群中添加新服务时,您都可以发送 Slack 消息。还有更复杂的用例,例如通过观察 Kubernetes 的第三方资源来编写自定义控制器。

状态和路线图

Fission 目前仍处于早期 Alpha 阶段(2017 年 1 月)。它尚未准备好投入生产使用。我们正在寻找早期采用者和反馈。

Fission 的未来如何?我们正在努力使 Kubernetes 上的 FaaS 更加方便、易于使用,并且更容易集成。在接下来的几个月里,我们正在努力增加对单元测试、与 Git 集成、函数监控和日志聚合的支持。我们还在努力与其他事件源集成。

创建更多语言环境也在进行中。目前支持 NodeJS 和 Python。Klavs Madsen 已贡献了 C# .NET 的初步支持。

您可以在我们的 GitHub issuesprojects 中找到我们当前的路线图。

参与其中

Fission 是由 Platform9 Systems 公开开发的开源项目。请在 GitHub 上查看我们,如果您想与我们聊天,请加入我们的 Slack 频道。我们也在 Twitter 上,账号是 @fissionio