Fission
Kubernetes中文社区对Fission的描述是:Fission是一款基于Kubernetes的FaaS框架。通过Fission可以轻而易举地将函数发布成HTTP服务。它通过读取用户的源代码,抽象出容器镜像并执行。同时它帮助开发者们减轻了Kubernetes的学习负担,开发者无需了解太多Kubernetes也可以搭建出实用的服务。Fission可以与HTTP路由、Kubernetes Events和其他的事件触发器结合,所有这些函数都只有在运行的时候才会消耗CPU和内存。Kubernetes提供了强大的弹性编排系统,并且拥有易于理解的后端API和不断发展壮大的社区。所以Fission将容器编排功能交给了Kubernetes,让自己专注于FaaS的特性。
在Gihub上面,是以这样的特性描述,对Fission进行描述:
- Performance: 100msec cold start: Fission maintains a pool of "warm" containers that each contain a small dynamic loader. When a function is first called, i.e. "cold-started", a running container is chosen and the function is loaded. This pool is what makes Fission fast: cold-start latencies are typically about 100msec.
- Kubernetes is the right place for Serverless: We're built on Kubernetes because we think any non-trivial app will use a combination of serverless functions and more conventional microservices, and Kubernetes is a great framework to bring these together seamlessly. Building on Kubernetes also means that anything you do for operations on your Kubernetes cluster — such as monitoring or log aggregation — also helps with ops on your Fission deployment.
可见,Fission在FaaS平台上面的两大特性或者说是优点:
- 针对冷启动进行优化;
- 基于Kubernetes进行部署,降低Kubernetes使用难度;
工作原理
Fission是一款基于Kubernetes的FaaS框架。通过Fission可以轻而易举地将函数发布成HTTP服务。它通过读取用户的源代码,抽象出容器镜像并执行。他整体结构也是依靠K8S来实现的:
在这个结构中,Fission主要包括了三个模块:
- Controller:提供了针对 Fission 资源的增删改查操作接口,包括 Functions、Triggers、Environments、Kubernetes Event Watches 等。它是 Fission CLI 的主要交互对象。
- Router:函数访问入口,同时也实现了 HTTP 触发器。它负责将用户请求以及各种事件源产生的事件转发至目标函数。
- Executor:fission 包含 PoolManager 和 NewDeploy 两类执行器,它们控制着 Fission 函数的生命周期。
功能与策略
Executor模块
PoolManager
其中fission 包含 的PoolManager 和 NewDeploy 两类执行器,是有不同的作用和价值的。Poolmgr使用了池化技术,它通过为每个 Environment 维持了一定数量的通用 Pod 并在函数被触发时将 Pod 特化,大大降低了函数的冷启动的时间。同时,Poolmgr 会自动清理一段时间内未被访问的函数,减少闲置成本。
PoolManager执行器的基本流程:
1、使用 Fission CLI 向 Controller 发送请求,创建函数运行时需要的特定语言环境。
2、Poolmgr 定期同步 Environment 资源列表,定期时间是2S
3、Poolmgr 遍历 Environment 列表,使用 Deployment 为每个 Environment 创建一个通用 Pod 池,
4、使用 Fission CLI 向 Controller 发送创建函数的请求。此时,Controller 只是将函数源码等信息持久化存储,并未真正构建好可执行函数。
5、Router 接收到触发函数执行的请求,加载目标函数相关信息。
6、Router 向 Executor 发送请求获取函数访问入口
7、Poolmgr 从函数指定环境对应的通用 Pod 池里随机选择一个 Pod 作为函数执行的载体.通过更改 Pod 的标签让其从 Deployment 中“独立”出来,Kubernetes发现 Deployment 所管理 Pod 的实际副本数少于目标副本数后会对 Pod 进行补充,这样便实现了保持通用 pod 池中的 Pod 个数的目的。
8、特化处理被挑选出来的 Pod,这里需要先准备数据,包括Pod信息和用户的function等信息,并将信息交给Fetcher来处理。通过POST方法传入代码资源,整个过程的流程:
Fetcher:下载用户函数并将其放置在共享 Volume 里。
Env:用户函数运行的载体。当它成功加载共享 Volume 里的用户函数后,便可接收用户请求。详细过程:
- 容器 Fetcher 接收到拉取用户函数的请求。
- Fetcher 从 KubernetesCRD 或 Storagesvc 处获取用户函数。
- Fetcher 将函数文件放置在共享的 Volume 里,如果文件被压缩还会负责解压。
- 容器 Env 接收到加载用户函数的命令。
- Env 从共享 Volume 中加载 Fetcher 为其准备好的用户函数。
- 特化流程结束,容器 EEnv 开始处理用户请求。
9、为特化后的 Pod 创建 ClusterIP 类型的 Service
10、将函数的 Service 信息返回给 Router,Router 会将 ServiceUrl 缓存以避免频繁向 Executor 发送请求。
11、Router 使用返回的 ServiceUrl 访问函数
12、请求最终被路由至运行函数的 Pod。
13、如果该函数一段时间内未被访问会被自动清理,包括该函数的 Pod 和 Service
NewDeploy
Poolmgr 很好地平衡了函数的冷启动时间和闲置成本,但无法让函数根据度量指标自动伸缩。NewDeploy 执行器实现了函数 Pod 的自动伸缩和负载均衡。
NewDeploy的基本流程:
1、使用 fission CLI 向 Controller 发送请求,创建函数运行时需要的特定语言环境
2、使用 fission CLI 向 Controller 发送创建函数的请求。
3、Newdeploy 会注册一个 FuncController 持续监听针对 Function 的 ADD、UPDATE、DELETE 事件
4、Newdeploy 监听到了函数的 ADD 事件后,会根据 minScale 的取值判断是否立即为该函数创建相关资源
- minScale > 0,则立即为该函数创建 Service、Deployment、HPA(Deployment 管理的 Pod 会特化)。
- minScale <= 0,延迟到函数被真正触发时创建
5、Router 接收到触发函数执行的请求,加载目标函数相关信息
6、Router 向 NewDeploy 发送请求获取函数访问入口。如果函数所需资源已被创建,则直接返回访问入口。否则,创建好相关资源后再返回。
7、Router 使用返回的 ServiceUrl 访问函数
8、如果该函数一段时间内未被访问,函数的目标副本数会被调整成 minScale,但不会删除 Service、Deployment、HPA 等资源
上述两种方法,可以在Fn Creat的时候通过参数控制:
--executortype poolmgr
--executortype newdeploy
关于性能官方的描述为:
The executors allow you as a user to decide between latency and a small idle cost trade-off. Depending on the need you can choose one of the combinations which is optimal for your use case. In future, a more intelligent dispatch mechanism will enable more complex combinations of executors.
关于NewDeploy和Poolmgr的性能对比:
EXECUTOR TYPE |
MIN SCALE |
LATENCY |
IDLE COST |
Newdeploy |
0 |
High |
Very low - pods get cleaned up after idlle time |
Newdeploy |
>0 |
Low |
Medium, Min Scale number of pods are always up |
Poolmgr |
0 |
Low |
Low, pool of pods are always up |
触发器
在触发器层面,Fission支持多样化的触发器,例如Synchronous Req/Rep类的有fission CLI、HTTP Trigger等,Job (Master/Worker)的有Time Trigger等,Async Message Queue类的有Message Queue Trigger(包括nats-streaming、 azure-storage-queue、 kafka等)和Kubernetes Watch Trigger。
弹性伸缩
在Fission官网,对自动扩缩容部分,有这样的描述:
The new deployment based executor provides autoscaling for functions based on CPU usage. In future custom metrics will be also supported for scaling the functions. You can set the initial and maximum CPU for a function and target CPU at which autoscaling will be triggered. Autoscaling is useful for workloads where you expect intermittant spikes in workloads. It also enables optimal the usage of resources to execute functions, by using a baseline capacity with minimum scale and ability to burst up to maximum scale based on spikes in demand.
可以看出,Fission的自动扩缩容部分,是通过CPU使用率来作为某一种标准,在上文分析过程中,可以明确Fission只有通过 NewDeploy 方式创建的函数才能利用 HPA 实现自动伸缩。
只能通过NewDeploy方法创建函数,且只有基于CPU使用率这一种指标(Kubeless支持CPU和QPS)过于局限。Fission的自动扩缩容部分使用的是Autoscaling/v1版本的 HPA API。以一个Demo为例,对其进行分析:
fission fn create --name hello --env python --code hello.py --executortype newdeploy --minmemory 64 --maxmemory 128 --minscale 1 --maxscale 6 --targetcpu 50
该命令将创建一个名为 hello 的函数,运行该函数的 pod 会关联一个 HPA,该 HPA 会将 pod 数量控制在 1 到 6 之间,并通过增加或减少 pod 个数使得所有 pod 的平均 cpu 使用率维持在 50%。
日志
使用 DaemonSet 在集群中的每个工作节点上部署一个 Fluentd 实例用于采集当前机器上的容器日志。这里,Fluentd 容器将包含容器日志的宿主机目录/var/log/和/var/lib/docker/containers挂载进来,方便直接采集。Fluentd 将采集到的日志存储至 Influxdb 中。用户使用 fission CLI 查看函数日志。例如,使用命令fission function logs --name hello可以查看到函数 hello 产生的日志。