Serverless构架不仅仅在工业界有诸多厂商不断为之努力,在开源领域也是有诸多优秀的开源Serverless项目。在《CNCF Cloud Native Interactive Landscape》的Serverless标签中,我们可以看到包括OpenWhisk、Fission、Knative以及Kubeless等在内的众多优秀开源FaaS平台。
在说开源的FaaS平台之前,不得不意味深长的说一句:Kubernetes 赢了。这不是夸大其词,事实就是如此。越来越多的人开始基于容器部署,而 Kubernetes 已经成为容器编排的事实标准。但是,众所周知,Kubernetes是一个容器平台而不是代码平台。它可以作为一个运行和管理容器的很好的平台,但是这些容器是如何构建、运行、扩展和路由很大程度上是由用户自己决定的。而如何来补充这些缺失的部分?无论是Knative、Fission,还是Kubeless这些所谓的开源FaaS平台,还是一些工业级的FaaS平台,真多时候就是在做这件事情,其目的就是让Kubernetes更好用,好用到用户都不知道自己在用Kubernetes,反正就是可以用的很爽。目前,在主流的FaaS开源项目中,绝大多数的项目都使用了Kubernetes相关技术,或者将其作为运行平台。
Knative |
OpenWhisk |
FnProject |
Fission |
Kubeless |
OpenFaaS |
|
开发语言 |
Go |
Scala |
Go |
Go |
Go |
Go |
创建时间 |
2018 |
2016 |
2012 |
2016 |
2016 |
2016 |
支持厂商 |
Google等 |
Apache/IBM |
Oracle |
Platform9 |
Bitnami |
Alex Ellis |
运行平台 |
K8S |
Docker/K8S |
Docker |
K8S |
K8S |
Docker/K8S |
Knative
在上文中,介绍工业界Serverless平台Google Cloud Functions的时候,我们提到过Knative,Knative 是 Google 在 2018 的 Google Cloud Next 大会上发布的一款基于 Kubernetes 的 Serverless 框架。
授权协议 |
Apache |
开发厂商 |
|
开发语言 |
Go |
发布时间 |
2018年(Google Cloud Next) |
操作平台 |
跨平台 |
Github Star/Fork |
3600/758 |
提交次数 |
6227次 |
Github地址 |
/knative/serving |
Knative 一个很重要的目标就是制定云原生、跨平台的 Serverless 编排标准。Knative 是通过整合容器构建(或者函数)、工作负载管理(和动态扩缩)以及事件模型这三者来实现的这一 Serverless 标准。Knative 社区的主要贡献者有 Google、Pivotal、IBM、Red Hat。可见其阵容强大, CloudFoundry、OpenShift 这些 PAAS 提供商都在积极的参与 Knative 的建设。
工作原理
knative 是建立在 kubernetes 和 istio 平台之上的,使用 kubernetes 提供的容器管理能力(deployment、replicaset、和 pods等),以及 istio 提供的网络管理功能(ingress、LB、dynamic route等)。在Knative中,有两个重点组件,分别是为其提供流量的Serving(服务),以及确保应用程序能够轻松地生产和消费Event(事件)。其中Serving基于负载自动伸缩,包括在没有负载时缩减到零,允许使用者为多个修订版本(revision)应用创建流量策略,从而能够通过 URL 轻松路由到目标应用程序;而Eventing是使得生产和消费事件变得容易,抽象出事件源,并允许操作人员使用自己选择的消息传递层。除了Serving和Event之外,Build也是Kantive的组件之一,其提供显式“运行至完成”功能,这对创建 CI/CD 工作流程很有用, 通过灵活的插件化的构建系统将用户源代码构建成容器。目前已经支持多个构建系统,比如 Google 的 Kaniko,它无需运行 Docker daemon 就可以在 Kubernetes 集群上构建容器镜像。。Serving 使用它将源存储库转换为包含应用程序的容器镜像。在诸多Serverless的开源项目中,Knative的优势也是较为明显和清晰的,一方面Knative 以 Kubernetes 作为其底层框架,与 kubernetes 生态结合更紧密,无论是线上还是线下,任何 Kubernetes 集群,无论是云上 Kubernetes 服务还是自建 Kubernetes 集群,都能通过安装 knative 插件快速的搭建 serverless 平台;另一方面Knative 联合 CNCF,把所有事件标准化,统一为 CloudEvent,提供事件的跨平台,同时让函数和具体的调用方能够解耦;在弹性层面,Knative可以监控应用的请求,并自动扩缩容, 借助于istio(ambassador,gloo等)天生支持蓝绿发布、回滚功能,方便应用发布流程,同时Knative支持日志的收集、查找和分析,并支持 VAmetrics 数据展示、调用关系 tracing等。
功能与策略
Serving(服务)
Serving 模块定义一组特定的对象以控制所有功能:Revision(修订版本)、Configuration (配置)、Route(路由)和 Service(服务)。Knative 使用 Kubernetes CRD(自定义资源)的方式实现这些 Kubernetes 对象。所有 Serving 组件对象模型间的关系:
Knative Serving 始于 Configuration。使用者在 Configuration 中为部署定义所需的状态。最小化 Configuration 至少包括一个配置名称和一个要部署容器镜像的引用。在 Knative 中,定义的引用为 Revision。Revision 代表一个不变的,某一时刻的代码和 Configuration 的快照。每个 Revision 引用一个特定的容器镜像和运行它所需要的任何特定对象(例如环境变量和卷)。然而,使用者不必显式创建 Revision。由于 Revision 是不变的,它们从不会被改变和删除,相反,当使用者修改 Configuration 的时候,Knative 会创建一个 Revision。这允许一个 Configuration 既反映工作负载的当前状态,同时也维护一个它自己的历史 Revision 列表。
Knative 中的 Route 提供了一种将流量路由到正在运行的代码的机制。它将一个命名的,HTTP 可寻址端点映射到一个或者多个 Revision。Configuration 本身并不定义 Route。
弹性伸缩
Serverless 的一个关键原则是可以按需扩容以满足需要和缩容以节省资源。Serverless 负载应当可以一直缩容至零。这意味着如果没有请求进入,则不会运行容器实例。Knative 使用两个关键组件以实现该功能。它将 Autoscaler 和 Activator 实现为集群中的 Pod。您可以看到它们伴随其他 Serving 组件一起运行在 knative-serving
命名空间中。Autoscaler 收集打到 Revision 并发请求数量的有关信息。为了做到这一点,它在 Revision Pod 内运行一个称之为 queue-proxy
的容器,该 Pod 中也运行用户提供的 (user-provided) 镜像。
queue-proxy
检测该 Revision 上观察到的并发量,然后它每隔一秒将此数据发送到 Autoscaler。Autoscaler 每两秒对这些指标进行评估。基于评估的结果,它增加或者减少 Revision 部署的规模。默认情况下,Autoscaler 尝试维持每 Pod 每秒平均 100 个并发请求。这些并发目标和平均并发窗口均可以变化。Autoscaler 也能够被配置为利用 Kubernets HPA(Horizontal Pod Autoscaler)来替代该默认配置。这将基于 CPU 使用率来自动伸缩但不支持缩容至零。这些设定都能够通过 Revision 元数据注解(annotations)定制。
Autoscaler 采用的伸缩算法针对两个独立的时间间隔计算所有数据点的平均值。它维护两个时间窗,分别是 60 秒和 6 秒。Autoscaler 使用这些数据以两种模式运作:Stable Mode(稳定模式)和 Panic Mode(恐慌模式)。在 Stable 模式下,它使用 60 秒时间窗平均值决定如何伸缩部署以满足期望的并发量。
如果 6 秒窗口的平均并发量两次到达期望目标,Autoscaler 转换为 Panic Mode 并使用 6 秒时间窗。这让它更加快捷的响应瞬间流量的增长。它也仅仅在 Panic Mode 期间扩容以防止 Pod 数量快速波动。如果超过 60 秒没有扩容发生,Autoscaler 会转换回 Stable Mode。
Build(构建)
Knative 的 Serving(服务)组件是解决如何从容器到 URL 的,而 Build 组件是解决如何从源代码到容器的。Build resource 允许您定义如何编译代码和构建容器,而不是指向预构建的容器镜像。这确保了在将代码发送到容器镜像库之前以一种一致的方式编译和打包代码。本章中将会向您介绍一些新的组件:
- Build:驱动构建过程的自定义 Kubernetes 资源。在定义构建时,您需要定义如何获取源代码以及如何创建容器镜像来运行代码。
- Build Template: 封装可重复构建步骤集合以及允许对构建进行参数化的模板。
- Service Account: 允许对私有资源(如 Git 仓库或容器镜像库)进行身份验证。
Event(事件)
到目前为止,向应用程序发送基本的 HTTP 请求是一种有效使用 Knative 函数的方式。然而,无服务器的松耦合特性同时也适用于事件驱动架构。也就是说,可能在文件上传到 FTP 服务器时我们需要调用一个函数;又或者,任何时间我们在进行一笔物品销售时需要调用一个函数来处理支付和库存更新的操作。与其让我们的应用程序或函数考虑监听事件的逻辑,不如当那些被关注的事件发生时,让 Knative 去处理并通知我们。
自己实现这些功能则需要做很多工作并要编写实现特定功能的代码。幸运的是,Knative 提供了一个抽象层使消费事件变得更容易。Knative 直接提供了一个“事件”,而不需要你写特定的代码来选择消息代理。当事件发生时应用程序根本无需关心它来自哪里或发到哪去,就只需要知道事件发生了这么简单。为实现这一目标,Knative 引入了三个新的概念:Source(源)、Channel(通道)和 Subscription(订阅)。
Sources(源): Source 是事件的来源,它是我们定义事件在何处生成以及如何将事件传递给关注对象的方式。
Channel(通道): 通道处理缓冲和持久性,即使该服务已被关闭时也确保将事件传递到其预期的服务。另外,通道是我们代码和底层消息传递解决方案之间的一个抽象层。这意味着可以像 Kafka 和 RabbitMQ 一样在某些服务之间进行消息交换,但在这两种情况下我们都不需要编写特定的实现代码。
Subscriptions(订阅): 我们将事件源发送到通道,并准备好开始处理它们的服务,但目前我们没有办法获取从通道发送到服务的事件。Knative 允许我们给这种情况定义订阅功能。订阅是通道和服务之间的纽带,指示 Knative 如何在整个系统中管理我们的事件。
Knative 中的服务不了解或不关心事件和请求是如何获取的。它可以是来自入口网关的 HTTP 请求,也可以是从通道发送来的事件。无论何种方式,服务仅接收 HTTP 请求。这是 Knative 中一个重要的解耦方式,它确保我们将代码编写到架构中,而不是在底层。我们创建订阅,通道向服务发送事件