本章通过整合其他开源项目,在平台上提供更多的函数计算服务。
6.1 函数计算服务的设计目标
基于Nomad的边缘计算平台函数计算服务方案设计功能包括:
l 监控
l 告警和自动扩缩容
l 负载均衡
l 调用链追踪
这些功能的开发基于上述FaaS函数运行时的实现,也促进FaaS函数运行时设计实现中降低耦合,开放接口。函数计算服务方案的思路是在FaaS函数运行时上整合云原生项目,提供相应的服务。
6.2 监控
监控功能负责对边缘计算平台函数计算系统中的组件运行情况和负载情况进行监控。在组件运行或函数计算系统的负载出现异常情况时,由报警系统根据监控数据第一时间向函数计算系统的管理人员发出告警,将因故障造成的损失降到最低。除此之外,监控收集的数据还可以成为函数计算系统的自动扩缩容、按量付费等提供决策依据。
如图6.1所示,监控系统可以接受组件运行情况数据和系统负载数据,并将这些数据根据数据性质和数据类型使用合适的数据库进行存储。监控的第三方服务模块可以与第三方服务进行对接,比如监控可视化服务、弹性扩缩容服务和告警分发服务等。
图6.1 监控与其他模块关系图
实现上,监控使用Prometheus,如图6.2所示,增加Prometheus和StatsD的容器工作负载,作为system作业类型调度。Prometheus是最初在SoundCloud上构建的开源系统监视和警报工具包。现在Prometheus是一个独立的开源项目。Prometheus实现了高维数据模型,许多公司和组织都采用了Prometheus,项目拥有非常活跃的开发人员和用户社区。Prometheus于2016年加入了CNCF,是继Kubernetes之后的第二个托管项目。
图6.2 函数计算监控功能设计
Prometheus收集FaaS API网关和FaaS函数运行时组件的指标数据。如表6.1所示,FaaS API网关组件需要添加以下观测指标,以便整合Prometheus监控函数的健康状况和行为。
表6.1 函数计算监控功能Prometheus采集指标
| 指标 | 类型 | 描述 |
|---|---|---|
| gateway_functions_seconds | histogram | 函数调用耗时 |
| gateway_function_invocation_total | counter | 函数调用次数 |
| gateway_service_count | counter | 函数副本数 |
| http_request_duration_seconds | histogram | 服务HTTP请求花费的时间 |
| http_requests_total | counter | HTTP请求的总数 |
通过NATS提供队列和异步函数执行。同步函数调用/function/路由,异步方法调用/async-function路由。http_request*指标记录/system/*路由的延迟和统计数据,监视FaaS API网关及FaaS函数运行时。
监控可视化使用的Grafana。Grafana是数据源开源的分析和监控解决方案,一个开放的可观察性平台。Grafana具有可插拔的数据源模型,并绑定了对许多最流行的时间序列数据库的支持,Grafana自2.5.0起包含Prometheus数据源,基于Grafana定制的函数监控部分仪表板如图6.3所示。
图6.3 Grafana函数监控部分仪表板
6.3 告警和自动扩缩容
如图6.4所示,告警和自动扩缩容由配置的规则触发,规则引擎负责规则配置和规则托管,规则触发服务负责查看被托管规则的阈值,在达到被托管规则的阈值时,规则定义的动作会被触发,规则触发服务使用AlterManager。告警规则是基于Prometheus灵活的PromQL定义的。Alertmanager负责处理由诸如Prometheus服务器之类的客户端应用程序发送的警报,将重复数据删除,分组和路由到集成的接收者。
图6.4 告警和自动扩缩容功能
通过规则引擎来定义函数计算动态调整系统中函数的告警规则和自动扩缩容规则,根据监控数据进行告警,根据负载情况进扩缩容。同时FaaS API网关需要提供/system/alert路由,处理AlertManager告警和自动扩缩容的请求。
实现上,通过Prometheus整合AlterManager完成规则引擎模块的对接,根据Prometheus的监控指标,配置AlertManager告警规则。如图6.5所示,新增Altermanager、NATS Streaming的容器工作负载,NATS Streaming用于异步调用队列。自动扩缩容的规则细化,应当抽象不同的自动扩缩容策略,支持QPS、CPU,支持用户自定义指标。
图6.5 函数计算告警和自动扩缩容功能设计
由于边缘计算环境下需要考虑节能,需要根据应用访问的实际负载,动态启停应用程序;支持缩容到零个函数副本,可以减少无人访问的应用对资源的占用,降低边缘端能耗。缩容到零见4.3.2 FaaS-Idler实现,也可以通过Altermanager实现。当前函数自动扩容设置的规则是最近10s函数调用率超过5,如图6.6所示。
图6.6 自动扩容规则
如图6.7所示,Nomad的Web UI中触发规则,正在扩容的函数作业。
图6.7 扩容中的函数作业
如图6.8所示,通过监控仪表盘查看到扩容过程。
图6.8 副本数扩容监控
6.4 负载均衡
如图6.9所示,不进行负载均衡的情况下,扩容出的副本调用数会严重倾斜。
本文使用使用Fabio进行均衡负载。Fabio是一个HTTP和TCP反向代理,可使用来自Consul的数据进行自我配置,为Consul管理的应用程序进行零配置负载均衡。
图6.9 未负载均衡时函数实例累计调用次数观测
需要说明的是在扩容前的函数实例累计的调用次数比后扩容的函数实例累计调用次数多,故而观察扩容后实例调用次数的变化,如图6.10所示,在使用Fabio进行函数计算负载均衡以后,负载被均衡地分流到每个函数实例。整合Fabio均衡负载,注册Consul服务发现的地址即可,无需人工干预。
图6.10 负载均衡后函数实例调用次数变化观测
6.5 调用链追踪
基于4.3.4函数工作流程的设计和实现,考虑到分布式体系结构时出现的大多数操作问题最终都基于两个方面:网络和可观察性。无服务器函数之间的网络连接和调试是一组交错的分布式服务,与单个整体应用程序相比,复杂度增加了一个大数量级。基于函数工作流程的调用链追踪能够降低故障排除的成本。
本文分布式追踪使用的Jaeger。分布式追踪系统核心步骤一般有三个:代码埋点,数据存储、查询展示。本文主要考虑代码埋点部分,数据存储、查询展示可整合现有开源云原生组件。数据被采集存储后,分布式追踪系统一般会选择使用包含时间轴的时序图来呈现该调用链。遵循OpenTracing规范,可以兼容不同的分布式追踪系统API,避免平台和厂商绑定。Jaeger是Uber开源的端到端的分布式跟踪系统,用于监视复杂的分布式系统中的事务并进行故障排除,兼容OpenTracing API。
一条函数调用链可以被认为是一个由多个函数处理组成的DAG,所以调用链追踪的实现可以在上文函数工作流上追加代码埋点。以RequestId标识用户的一次请求,所追踪链路上下文中的节点共用一个RequestId,调用链上经过的每个节点需要包含SpanID和ParentSpanID,分别表示正在处理用户请求的节点和正在处理用户请求节点的上一个节点。SpanID使用UUID随机数生成机制,保证唯一性,每次请求都有唯一的SpanID,ParentSpanid使用上一个节点传递过来SpanID。RequestId、SpanID和ParentSpanID的传递机制是基于Restful接口使用的HTTP协议,由客户端生成生成RequestId,在请求头添加具体的X-Faas-Worklow-Reqid。
代码埋点实现上,如图6.11所示,通过在OpenFaaSEventHandler中添加OpenTracing接口的属性,并在EventHandler的生命周期中跟踪。EventHandler其中涉及了Request、Execution、Node、Operation四种对象,其中Request、Node、Operation都有Start、End、Failure三个触发点,Execution有Forward、Continuation两个触发点。
图6.11 EventHandler实现增加OpenTracing
在触发faas-workflow的请求体中添加字段RequestId用于唯一标识请求,并将上游请求进行状态存储。状态存储本文具体使用Consul的键值对存储功能,也抽象了StateStore接口,可用于其他状态存储系统。
如图6.12所示,当前UI还比较薄弱,有一些个性化的分析需求如对比、统计延迟分布等无法快速满足,后期可以根据需求进一步定制满足函数计算调用链追踪更多的功能。
图6.12 函数调用链追踪