本文编译自 Kubernetes 的官方博文,原文为 Platform9 的软件工程师 Soam Vasani 所写,讲解了一个基于 Kubernetes 的 Serverless 函数(FaaS)框架——Fission。
简单的来讲,Fission 是一个构建在 Kubernetes 之上的 FaaS/Serverless 函数框架。
注:Faas 即 Function as a Service 的缩写
Fission 允许你通过函数轻松的在 Kubernetes 上创建 HTTP 服务。它工作在源码级别函数和抽象的容器镜像之上(大多时候),同时简化了 Kubernetes 的学习曲线,让你不用深入了解 Kubernetes 就能创建出有用的服务。
通过 Fission CLI,你可以简单地创建和添加函数,还可以将这些函数关联到 HTTP 路由、Kubernetes 事件、或者其他触发器。函数在调度器触发后被执行,而且函数运行时只消耗 CPU 和内存(空闲的函数除了存储不会消耗任何资源)。Fission 当前支持多种语言,包括 NodeJS 和 Python。
为什么要在 Kubernetes 上创建一个 FaaS 框架?
基于 Fission 的特性,我们认为有必要让一个 Faas 框架运行在不同的基础设施上,包括公有云和专属云(on-premise)。但我们不清楚是否应该从无到有来构建,还是可以基于现有的编排系统。后来证明我们不应该从零开始去重复创造诸如集群管理、调度、网络管理等一系列功能。
借助强大和快速生长的社区,Kubernetes 提供了一个强大和灵活的编排系统,并有完善的 API 支持。在它上面构建意味着可以将容器编排功能留给 Kubernetes,而 Fission 就专注于 FaaS 特性。
另外,我们不想分离 FaaS 集群的原因是 FaaS 可以和其它基础设施很好的结合。比如,FaaS 可能适合做一个小的 REST API,但它需要和其它设施结合来存储状态。同时 FaaS 还有一个很好的工作机制,那就是作为事件处理者,用于处理来自存储、数据库,甚至 Kubernetes 本身的通知。Kubernetes 是一个让所有这些设施相互合作的平台。
部署和使用 Fission
Fission 可以用 kubectl create 命令来安装:详情可查看项目 README(https://github.com/fission/fission#get-and-run-fission-minikube-or-local-cluster) 。
如下是一个如何写 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:///hello Hello, world!
Fission 会关心如何将函数加载到容器当中,如何将请求路由过去等等。接下来我们将探讨更多的技术细节。
Fission 是怎样在 Kubernetes 上实现的
最核心的,一个 FaaS 框架必须做好:
- 将函数转化为服务
- 管理这些服务的生命周期
有很多种方法可以达到以上目标,每种方法都需要权衡。框架计算是基于源码级别,还是 Docker 镜像级别,又或者是两者之间(类似buildpacks)?一个函数首次运行时间上限是多少?这些选择都会影响平台的灵活性、易用性、资源的利用率和消耗,当然还有性能。
打包,源码和镜像
我们的目标是想让 Fission 对新手友好。我们选择基于源码级别来计算,所以用户能避免处理容器镜像打包、推送镜像到镜像中心、管理镜像中心证书、镜像版本等问题。
然而,容器镜像还是最灵活的打包应用的方式,比如,一个纯粹的代码级别接口是不允许用户打包二进制依赖的。
所以,Fission 选择了一种混合的方法—-容器镜像包含函数所需的动态装载器。这种方法允许大多数用户纯粹从源码级别来使用 Fission,但需要时也允许他们自定义容器镜像。
在 Fission 中,这些镜像被称作“环境镜像”,包含了编程语言(比如说 NodeJS 或者 Python) 所需的运行环境,一系列通用的依赖和为函数准备动态加载器。如果这些依赖满足用户所写的函数,镜像无需重新构建,否则,依赖列表需要修改,镜像需要重新构建。
这些环境镜像只是和 Fission 的特定语言相关,它们给框架提供了一个统一的接口。这样的设计可以让 Fission 相对容易地扩展到其他语言。
冷启动性能
Serverless 函数其中一个目标就是函数只在运行时使用 CPU/内存资源。这样优化了函数的资源消耗,但也带来了从空闲到启动的性能损耗(冷启动损耗)。
冷启动损耗在很多场景下非常重要。特别是交互式应用的场景——就像用户等待一个 web 或移动应用响应——几秒的冷启动延迟是不能接收的。
为了优化冷启动延迟,Fission 为每个环境保持着一个运行容器池。当一个函数请求进来之后, Fission 不需要部署一个新容器——它只需要选择一个已经在运行的容器,将函数拷贝到容器当中,将函数动态加载起来,并将请求路由到这个实例即可。对 NodeJS 和 Python 函数来说,这个过程的损耗差不多是 100 毫秒。
Fissin 是怎样在 Kuberntes 上工作的
如上图所示,Fission 被设计为一系列的微服务。Controller 负责追踪函数、HTTP 路由、事件触发器和环境镜像。poolmgr 负责管理空闲的环境容器池、将函数加载到这些容器当中、以及杀死空闲的函数实例。Router 接收 HTTP 请求,并将他们路由到函数实例上,如果需要会向 poolmgr 请求新的实例。
Controller 提供 Fission API,所有其他组件通过关注 controller 来更新。Router 被暴露为 Kubernetes 的服务,可能是 LoadBalancer 类型或 NodePort 类型,依赖于 Kubernetes 集群在哪里托管。
当 router 获得请求,它会查找缓存,看是否有一个路由过去的服务。如果没有,它会查找匹配请求的函数,并且向 poolmgr 请求一个新的实例。poolmgr 有一个空闲 pods 池,它会选择一个 pod,将函数加载进去(将请求发送到 pod 的容器中),并将 pod 地址返回给 router。Router 将请求代理到这个pod。这个 pod 也会被缓存给随后的请求,如果空闲了好几分钟,那就会被杀掉。
注:目前,Fission 映射一个函数到一个容器;自动扩容到多实例还在计划当中;还计划加入重用函数 pods 用以托管多函数,特别是对于那种无需隔离的场景。
Fission 的用法
Bots,Webhooks,REST APIs
Fission 是一个不错的框架,易于构建小的 REST APIs,实现 webhooks,以及为 Slack 等服务编写聊天机器人(Chatbots)。
以一个简单的 REST API 为例,我们制作了一个小的留言板(guestbook)应用,它用函数来完成读写,结合 redis 来跟踪状态。你可以在 Fission GitHub 仓库中找到该应用。
这个应用包含两个访问终端——其中一个是 GET 终端, 它从 redis 缓存获取留言板实体,并将它们转化为 HTML 输出;另一个是 POST 终端,它向 redis 缓存中添加留言板列表想的新实体。函数所做的就这些而已——不需要管理 Dockerfile,更新 app 只需要简单地更新 Fission 函数即可。
处理 Kubernetes 事件
Fission 同样支持基于 Kubernetes watches 来触发函数。举个例子,你可以构造一个函数来观察在特定 namespace 中, 匹配特定 label 的 pod。函数获取序列化对象,监听事件,并基于上下文做相应操作(增加/删除/修改)。
这些事件处理函数可以用来做简单的监控,比如,无论何时当一个新的服务被加入到集群当中,你就可以发送一个 slack 消息。还有其它更复杂的应用场景,比如写一个自定义的 controller,来监听 Kubernetes 的第三方资源。
状态和路线图
Fission 当前还是早期的 alpha 版。还没做好生产使用的准备。我们正在寻找早期的使用者和反馈。
Fission 接下来要做什么呢?我们在努力使基于 Kubernetes 的 FaaS 更便捷,更容易使用和集成。在接下来的几个月我们会做这些支持:单元测试、Git 集成、函数监控和日志聚合。我们同样在考虑集成其它的事件源。
创建更多的语言环境也在工作当中,当前已经支持 NodeJS、Python、PHP7、.Net、Golang、Ruby。
你也可以在我们的 GitHub issues 和 projects 中查找当前的路线图。
附录:
原文链接:http://blog.kubernetes.io/2017/01/fission-serverless-functions-as-service-for-kubernetes.html
原文作者:Soam Vasani, Software Engineer, Platform9 Systems
编译: 黄庆兵,网易云系统开发工程师