嘉宾 | 霍秉杰 整理 | 王新
出品 | CSDN云原生
2022年5月10日,在CSDN云原生系列在线峰会第4期“ApacheSkyWalking峰会”上,青云科技资深架构师霍秉杰分享了SkyWalkingv9如何帮助OpenFunction实现函数可观测。
戳观看霍秉杰分享视频
利用SkyWalking监测函数性能,实现可观测性
随着技术的发展,人们越来越少关注底层技术栈的一些细节,越来越多关注自己应用的商业逻辑。CNCF发布的Serverless白皮书印证了这一点。
如上图所示,从下往上,最底层是CaaS(Container as a Service),它向下抽象和屏蔽了基础设施的差异,向上提供了一个应用运行的平台,用户对基础设施和应用有相对全面和灵活的掌控。
中间层是PaaS(Platform as a Service),主要分为两类,一类是传统的PaaS平台,例如Cloud Foundry、Heroku; 另一类则是容器平台,例如OpenShift、KubeSphere,它们可以提供Kubernetes没有的一些能力,比如用户的鉴权、认证与管理,可观测性包括监控、告警、日志、事件与审计,服务网格,还有CI/CD等。这使得用户可以更加关注于开发和部署应用,其他可以依赖平台提供的能力。
最上层是 FaaS(Function as a Service),包括云厂商的函数计算服务和开源的函数计算平台。在FaaS这层,用户更多关注自己应用的业务逻辑部分,其他的方面都交给平台去处理。
近几年,我们越来越多地听到多云、分布式云、混合云这样的提法,其中原因应该是Kubernetes带来了云厂商中立的可能性,于是很多公司开始基于Kubernetes去做多云、分布式云、混合云的方案或产品。但在FaaS领域却很难实现厂商中立,因为每个云厂商都有自己的FaaS平台,通常这些云厂商的FaaS平台都会和自己云上的后端服务绑定。
CNCF Serverless白皮书其实也提到了这一点:各个厂商由于编程模型、事件及消息接口,还有后端服务的不同导致用了一个厂商的FaaS平台后,就很难迁移到别的厂商的FaaS平台。
那么有没有可能去构建一个云厂商中立的FaaS平台?如何去构建这样一个平台?
首先,不开源肯定是不太可能做到厂商中立的,不开源就成了另一个厂商。
CNCF Serverless白皮书在2018年发布,里面提到Serverless缺乏标准化,成熟度方面也有所欠缺。四年过去了,其实这两个方面都有所改观。
首先,在标准化方面,据CNCF 2021年的调查报告显示,正在使用以及正在评估使用Kubernetes的受访者已经达到创纪录的96%,Kubernetes逐渐走向底层,成为更上层平台和服务的基础设施。
其次,四年以来云原生Serverless领域也有了很多进展,其中最著名的Knative就是在2018年开源的,四年以来,Knative已经发布了v1.0版并且现在已经捐给了CNCF。本文不会过多介绍Knative,而是会着重介绍另外两个云原生Serverless领域的技术——KEDA和Dapr。
KEDA全称是Kubernetes Event-drivenAutoscaling,顾名思义,KEDA主要用于Kubernetes上分布式应用的自动伸缩,其独特之处在于可根据众多事件源特有的指标进行伸缩,包括开源的中间件及各大云厂商的后端服务等事件源。我们可以用KEDA来解耦事件源与分布式应用的自动伸缩,KEDA使得应用可以根据使用中间件或服务的特有指标进行伸缩,无论该中间件或服务是云上托管的还是开源的。
Dapr全称是Distributed Application Runtime,中文译为“分布式应用运行时”,它是把分布式应用的各种能力抽象成多个Building Block,比如有的应用有状态管理的需求,需要存取一些状态,Dapr有一个State Management的Building Block可以提供这方面的能力;有一些事件驱动的分布式应用,有发布订阅的需求,Dapr有一个pubsub的BuildingBlock可以提供这方面的能力;再比如很多应用都有输入和输出,于是Dapr提供了Input/Output Binding的Building Block。总之,使用Dapr可以把分布式应用和其依赖的后端服务进行解耦。
具体来说,FaaS平台一般都需要支持多语言,通常需要和多种后端服务去打交道。我们假设一个FaaS平台需要支持5种语言,并且需要和10个messagequeue去对接。不用Dapr的情况下,每种语言都得用每个messagequeue该语言的SDK去对接这个message queue,总共就会有50种实现;在使用Dapr的情况下,每种语言仅需要用Dapr的SDK去和Dapr对接,再由Dapr去和各个message queue对接,这时候只需要5种实现。可以看到在引入了Dapr之后,极大降低了FaaS平台的复杂度。
相当于我们在Serverless的FaaS和BaaS之间额外添加了一层Dapr。
Dapr相当于FaaS访问BaaS的一个统一的接口。
几个星期之前我们把如何在OpenFunction中使用Dapr面向Dapr全球社区做了一个分享,Dapr的两位联合创始人Mark和Yaron对OpenFunction这种用法很欣赏,也很高兴看到Dapr被应用到越来越多的Serverless Function runtimes中。
OpenFunction主要由3个部分组成:Build、Serving和Events。在Build和Serving之上还有一层抽象Function,由Function来控制函数的Build和 Serving。Function是用户和OpenFunction交互的统一接口,用户无需和Build以及Serving打交道。
Build主要负责把函数的代码构建成函数的镜像。采用的是Cloud NativeBuildpacks技术,这也是最近几年涌现出来的一项云原生构建技术,它的特点就是不需要依赖Dockerfile就能够把函数的镜像给build出来。目前已被Google Cloud、IBM Cloud、VMware、Heroku等公司采用。现在OpenFunction也支持用另外三种依赖于Dockerfile的构建技术去Build应用包括buildah、BuildKit和Kaniko。以后也会陆续支持用这些依赖于Dockerfile的构建技术去构建函数镜像。
Serving应该是OpenFunction中最关键的部分,主要负责运行函数。目前Serving包含了同步和异步两个函数运行时。同步函数目前支持用Knative作为运行时,我们也计划支持用KEDA http-addon作为另一个同步函数运行时,目前我们已经有Maintainer积极参与到了这个项目中。OpenFunction比较有特色的应该是它的异步函数运行时。很少看到在开源或者是云厂商的FaaS平台里有专门的异步函数运行时,如果有异步函数也多是通过把事件源的事件转换成一个HTTP的请求后再去处理。OpenFunction的异步函数是直接对接事件源的,由KEDA和Dapr构成,这是OpenFunction所特有的。
OpenFunction Events的作用类似KnativeEventing。但Knative Eventing在设计与使用上都过于复杂,于是OpenFunction受Argo Events的启发设计了自己的事件管理框架OpenFunction Events,其由EventSource、Sink、EventBus和Trigger等部分组成。
当一个函数创建后,Function会创建一个Builder,Builder会创建ShipwrightBuild和BuildRun,结合Shipwright的BuildStrategy,Shipwright会生成Tekton的TaskRun。TaskRun中包含多个Step,函数代码就是在这些Step的控制下构建成函数镜像。
如前所述,OpenFunction现在支持Knative同步函数运行时和OpenFunction异步函数运行时。二者都可以和Dapr集成,区别在于Knative同步函数运行时因为输入来自HTTP请求,只用到了Dapr的output binding和pubsub的publish部分;而OpenFunction异步函数运行时的输入和输出都是通过Dapr对接中间件或云服务,所以用到了Dapr的input和output binding或pubsub的publish和subscribe.
我们未来计划支持KEDA http-addon作为另一个同步函数运行时,同时也将探讨通过pool技术优化函数冷启动的时间。
OpenFunction Events是受Argo Events启发,其和Argo Events的不同之处在于OpenFunction的EventBus由于使用了Dapr的pubsub,与底层的message queue是解耦的,这意味着它并不会绑定到某个云平台或者开源软件上。当EventSource收到外部事件后,有两条路径:它可以直接触发一个同步函数,也可以将事件写入到EventBus进行持久化。事件写入EventBus后也有两种处理方式:可以直接触发一个异步函数;也可以定义一个Trigger来对事件进行筛选,筛选出感兴趣事件后,可触发一个同步函数,或者将筛选出的事件写回到EventBus作为输出。
Functions Framework是OpenFunction的另一个重要组成部分,由context、plugins、Runtime、Framework这构成。Framework读取OpenFunction的Context,将Function的上下文读取出来,并且查看这个函数有没有插件,如果有的话则将插件注册进来,然后创建Runtime并且启动。当同步或者异步函数触发后,由Runtime控制函数运行的生命周期。首先会执行插件的pre-phase hooks,再调用用户函数,之后调用插件的post-phasehooks, 最后处理这个函数输出。
加入插件机制的很大一部分原因是想在这个函数执行的前后,执行一些用户不需要关心的自定义逻辑。比如OpenFunction和SkyWalking的集成,就需要在这个函数执行前后做一些处理,这个我们稍后展开。
函数的性能对用户来说比较重要,于是经过和SkyWalking社区的充分讨论和合作,OpenFunction在v0.6.0实现了与SkyWalking的集成,用户经过简单的配置就能通过SkyWalking v9监测函数的性能。
以上面的同步函数触发异步函数为例,为了监测同步和异步函数的性能,用户只需要在函数的annotation做如下配置即可(也可以将配置放入configmap以对所有函数生效):
做好上述tracing的配置,部署函数后,用户将可以在SkyWalking V9的FaaS页面看到如下函数调用链及性能数据:
完整示例详见:https://github.com/OpenFunction/samples/tree/main/functions/tracing
SkyWalking与OpenFunction 集成Demo详见:http://demo.skywalking.apache.org/functions
OpenFunction有个社区用户“驭势科技”是自动驾驶行业的,他们把OpenFunction应用于自动驾驶,下面我们看看OpenFunction在驭势科技的应用。
如上图所示,自动驾驶的场景比较复杂,设计的模块比较多,包括车辆检测、调度命令分发、环境感知、行人规避、路由规划、底盘控制、多车协同等。
原因有以下几点:
不同的客户要求部署到不同的云厂商
一些客户的车端数据比较敏感,要求放到和公有云隔离的环境
数据处理逻辑多样,来自同一数据源的数据在不同场景下的处理逻辑不尽相同
数据处理逻辑经常变化
自动驾驶涉及的模块较多,不同的模块由不同的团队负责,需要多语言支持
大量车端数据需要实时处理
自动驾驶车辆也有潮汐的特性,数据处理需求有高峰和低谷
此外,不同的云厂商有不同的后端服务,如果没有一个云厂商中立的FaaS平台,对于同一处理逻辑则需要为对接的每一个云厂商都实现相似的服务。
驭势科技使用OpenFunction处理车端数据的归档:
同步函数接收请求、分解任务并发送至消息队列
消息队列中的子任务触发异步函数运行
用不同语言编写的异步函数处理不同的子任务,并将处理结果发送至不同的后端服务
利用消息队列解耦生产者和消费者函数
据CNCF Serverless白皮书所示,FaaS可以应用到众多场景:
HTTP REST APIs forweb applications
Mobile backends
Executing logic inresponse to storage changes like S3, database (insert, update, trigger, delete)
Performinganalytics on IoT sensor input messages (such as MQTT messages)
Stream processingat scale (ETL, analyzing or modifying data in motion)
Business logiclike order processing, stock trade processing
Chatbot
Serving machinelearning and AI models
Batch jobs orscheduled tasks: Jobs that require intense parallel computation, IO, or networkfor only a few minutes
Continuousintegration pipelines
概括地说,Serverless架构主要解决IaaS、PaaS、CaaS无法高效解决或解决不了的问题:
高效地解决新问题:Solveda brand new problem efficiently where an on-demand model wasn’t available
高效地解决老问题:Solveda traditional cloud problem much more efficiently (performance, cost)
解决“大”的问题:Showed a dimension of “largeness”,whether in size of data processed or requests handled
解决弹性的问题:Showedresilience by scaling automatically (up and down) with a low error rates
解决快的问题:Brought asolution to market much faster than previously possible (days to hours)
OpenFunction已经逐渐被越来越多的公司采用:
国内某电信公司采用OpenFunction构建云函数计算平台
驭势科技 (UISEE) 采用OpenFunction处理车端数据
全象低代码平台采用OpenFunction实现插件机制
也有越来越多的社区贡献者参与到了OpenFunction项目中来:
主要Maintainer来自KubeSphere团队
SkyWalking PMC成员张伟@arugal实现了SkyWalking和OpenFunction Go版Functions Framework的集成
驭势科技 (UISEE) 正在参与Node.js和Python版Functions Framework的开发
全象团队参与了命令行工具ofn的开发
@Geffzhang正在参与dotNet版Functions Framework的开发
OpenFunction已经在2022年4月26日经CNCF技术监督委员会(TOC)投票被正式接纳为CNCF沙箱项目,欢迎更多的社区贡献者参与到OpenFunction项目中来。
OpenFunction将在之后的开发中,将重点放在如下功能上:
支持更多语言的异步函数框架包括Node.js、Python、Java和.NET
支持将Java函数编译成Native程序运行在Quarkus环境中
使用KEDA的http-add-on作为Knative Serving之外同步函数运行时的又一个选择
支持OpenTelemetry生态作为SkyWalking之外的另一个函数Tracing的方案
增加OpenFunction控制台
实现Serverless工作流
对在边缘运行的函数有更好的支持
预研基于Pool的冷启动优化方案
使用WebAssembly作为更加轻量的运行时,结合Rust函数来加速冷启动速度
最后,举两个两个函数的例子,一个异步函数,一个同步函数触发异步函数。
下面是用异步函数进行日志告警的例子,K8s的日志被收集并发送到Kafka后可以触发异步函数,异步函数找到日志中的某些错误后可以发送告警到Notification Manager,最后Notification Manager会发通知到Slack。
函数的代码及定义详见:https://github.com/OpenFunction/samples/tree/main/functions/async/logs-handler-function
下面是一个同步函数触发异步函数的例子,同步函数接收HTTP请求,处理后将处理结果通过Dapr发送到Kafka,Kafka中的Event进而会触发异步函数。
同步函数的代码及定义详见:https://github.com/OpenFunction/samples/tree/main/functions/knative/with-output-binding
异步函数的代码及定义详见:https://github.com/OpenFunction/samples/tree/main/functions/async/bindings/kafka-input
聚焦云原生新技术、新实践,帮助开发者群体赢在开发范式转移的新时代。欢迎关注CSDN云原生微信公众号~
扫这里↓↓↓加入CSDN云原生交流群