作者 | 刘勤龙
策划 | 田晓旭
首发 | InfoQ
Serverless 技术正在获得越来越多的认可。CNCF 2019 年报告显示,41% 的受访者表示已经在使用 Serverless,另外 20% 的受访者表示计划在未来 12-18 个月内采用 Serverless 技术。
Serverless 技术关注者对其价值点讨论⼤多是基于公有云场景的云函数等产品,其关注点在资源支付方式更加细粒度,和公有云 Baas 的粘合上,和私有云环境中业务团队关注的价值不太契合;在我们对业界落地场景调研以及同业务团队⼀起实践后,我们发现私有云环境中业务团队关心的 Serverless 价值可概括为三点:
提效: 加快业务团队迭代效率, Serverless 对开发流水线重新对分工,业务开发人员聚焦业务,无需操心运维和扩容等诸多事项;
降本:按需实时弹性可避免资源浪费,最大程度发挥资源优势;
解耦:支持事件触发,将各个组件通信的逻辑变成事件进⾏解耦合,非常适合业务的扩展和变化;
其中“提效”和“降本”为核心价值,解耦为重要考虑点。
我们认为 Serverless 出现不是为了替代现有的 Serverful(传统云)框架,两者是互补的关系,Serverless 有其业务场景优势(后续⽂章再展开),合适最重要。笔者目前工作是聚焦轻舟 Serverless(“轻舟”系网易研发的云原生基础设施平台代号)和业务团队⼀起实现业务开发的提效、降本和解耦。当前开源 Serverless 方案很多,而选型强大活跃开源社区方案让我们能够持续改进自己的 Serverless 平台。基于此诉求,我们很早便选型了 Knative,因为从一开始其社区非常活跃,有 Google,IBM,RedHat 等大公司参与,其次是标准先行。而事实也在慢慢印证了我们的选择。
如图所示, Knative 占据了 34% 的份额,遥遥领先于第⼆名 OpenFaaS,Knative 是搭建 Serverless 平台的首选。( 数 据 来 源 于 CNCF 2019 年 社 区 调 查 报 告 )
目前,网易轻舟云原生团队已经和网易云音乐前端团队合作共建云音乐Serverless 部署平台ALPACA,将Serverless 用于前端场景。该平台架构如下图。
其中轻舟负责底层能力,Knative 是其中的核心能力,我们基于其业务场景,对Knative 进行了压测分析,也做了性能调优POC,本文主要从性能角度,基于Serverless 前端使用场景对Knative 进行分析,尝试揭开Knative 核心数据路径性能真相并给出调优思考。
本文暂不讨论流量如何导⼊到ALPACA 平台,先聚焦到ALPACA 平台Knative 系统内部本身。
Knative 系统内部数据路径是, Knative 网关 ->Activator->Queue Proxy-> 业务 App;社区推荐使用的方式是不注入 Sidecar 以获取更佳的性能,因此我们讨论场景是“不注入 Sidecar,管控面使用 Pilot,Knative 网关使用轻舟的 API 网关产品”。
Knative 系统内部默认的数据路径如上图,用户业务流量经过一层 Knative 网关,经过 Activator 到达 Queue Proxy 代理组件,最后到达应用程序。
从上图可知,默认情况下流量经过三层代理(Knative 网关、Activator、Queue Proxy)后才到达应用 APP,每⼀层代理均可能是性能的拦路虎。
我们的分析思路如下:
Knative 网关这⼀层承载了流量管理、灰度发布等功能必须存在,当前使用轻舟 API 网关产品充当,性能均调优过,本身性能没什么问题,非本次核心关注点
App 为用户的业务代码性能无法控制,平台层面不可操作,也非关注点
于是将性能分析要点集中到剩余的两层代理(Activator 和 Queue Proxy)上。
首先 Activator 作用是:
冷启动充当看门人,业务 POD 0-1 流量会经过 Activator 组件
突发流量的保护者,将 Activator 加⼊到核心路径,充当缓冲,流量将会在 Activator 缓存( 0.8 版本加入功能)
其次 Queue Proxy 作用是:
Queue Proxy 以 Sidecar⽅式和应⽤容器部署到同⼀个 Pod,Queue Proxy 是为了配合完成扩缩容事宜以及满足 App 可观测性要求,以 Sidecar 方式部署主要考虑到对 App 应用无侵入,功能描述如下:
完成观测性数据收集向 Autoscaler 同步当前的并发监控,以便实现自动伸缩功能
代理业务容器, 完成指标的统计,并将对应的数据汇报给后端的⽇志 / 监控 / 分布式跟踪服务
谈到 Activator 必要性,需要先了解当前 Serverless 难题“冷启动”,所谓冷启动是 Serverless 缩容到 0 后,重新从 0 扩容到 1 的过程,该过程目前是非常慢的,也是业界难题,根据社区对 Knative 冷启动分析得知,冷启动时间大概 6s ,很显然 6s 的冷启动任何业务都⽆法容忍;当前可以尽量优化冷启动时间,但是想达到 ms 级别,做到业务⽆感知,挑战非常大,目前有两种解决思路:
方向 1: 温启动,通过冗余方式建立预热池来解决,当需要 0-1 时候,从预热池取,然后将用户程序注入,省去建立容器的过程
方向 2: 通过默认预留实例来规避
目前该问题我们先通过方向 2 来解决,至于方向 1 也在考虑,但是涉及到的开发工作量大,且需要对 K8s 框架改动,需要根据需求触发。所以目前在我们使用场景中不需要 Activator 帮助从 0-1 扩容。
对于突发流量保护功能,在使⽤场景中可降低扩容触发的并发要求,预留出一定的 Pod 计算能力来抵御突发流量;因此在目前业务场景中,Activator 存在必要性⼀般,可以考虑将其从核心数据路径中彻底去除。
Queue Proxy 核心作用是收集 App 的指标(并发、RPS 等)来决定扩容,当前以 Sidecar⽅式部署是非常有必要的:
所以 Queue Proxy Sidecar 是非常有必要的,但是相比裸业务容器而言,会增加⼀层代理(该场景就像服务网格 Server Sidecar⼀样),导致业务容器性能降低,这是选择这种架构所要付出的资源成本,我们要做的就是将该成本降到最小。
如上图,总结下分析结论,从性能⾓度出发,我们需要关注:
从上文的分析结论可知,我们得到了具体的性能关注点,于是对这些关注点进行实际的性能测试。
在 Knative 框架下,性能可以通过 CPU 和实例个数横向扩展性能,所以后续测试均固定在单个业务容器,通过对比测试来发现性能瓶颈,业务容器选型社区简单的 go 语言实现的 helloworld 服务程序(镜像为 hub.c.163.com/qingzhou/knative/demo/helloworld-go:v0.1),采用测试工具 Hey( https://github.com/rakyll/hey )使⽤HTTP 长连接进行测试。
root@pubt1-k8s59:/home/liuqinlong# cat performance.yaml
apiVersion: serving.knative.dev/v1alpha1
kind: Service
metadata:
name: helloworld-go
namespace: default
spec:
template:
metadata:
labels:
app: helloworld-go
annotations:
autoscaling.knative.dev/maxScale: "1"
autoscaling.knative.dev/minScale: "1"
spec:
containers:
- image: hub.c.163.com/qingzhou/knative/demo/helloworld-go:v0.1
env:
- name: SIMPLE_MSG
value: "helloworld-go"
hey -z 60s -c 70 --host "helloworld-go.default.example.com" " "
如下结果显示,虽然 Activator 采用 HPA 进行性能扩展,但是其扩容非常慢,如果性能测试时候没有来得及扩容 Activator,对整个延迟影响效果巨⼤只有 920Qps。即使 Activator 扩容成了 8 个发现 p90 延时也在 7ms 以上,Qps 约 7 千。
避坑说明:在测试过程中,需要确认 Activator HPA 扩容是否生效,笔者测试过程中默认环境中没有安装 metrics-server 导致 Activator 无法 HPA 扩容。整个核⼼路径中 Activator 默认限制单个 CPU,其使用率达到 100%,导致 QPS 非常低(才 920),P90 延迟要 111ms,p99 延迟要 195.2ms
经过前文的分析 Queue Proxy 以 Sidecar⽅式存在是 Knative 架构要求,当前测试 case 情况下,加⼊Queue Proxy Sidecar 后,相⽐原⽣容器,QPS 从 3.9w->3.1w,P90 延迟翻倍(2.5->4.2)。
相对来说,在相同并发压力情况下,因为新增⼀层代理延迟肯定提升,QPS 会跟着降低。但是我们发现 CPU 损失代价有些大,CPU 使⽤率达到了 1497% (Server CPU 才 482.4%),理论测试的 App 为 hello world 程序业务逻辑⾮常简单,业务处理延迟不长,Queue Proxy 和当前测试 App CPU 使用率比值最好是 1:1,所以 Queue Proxy 存在 CPU 异常消耗的问题,需要进行调优解决。
注意:v0.14 Queue Proxy 性能要比 v0.9 版本 Queue Proxy 性能要好,后⽂Queue Proxy 测试版本均采⽤的是 v0.14 版本,下面给出性能对比:
QPS 提升 (31891.7207-20505.6853)/20505.6853 = 55%(计算过程后文不再赘述)。
经过对 Knative 性能测试,进⼀步确认了下面性能调优点:
1. 数据路径上去除 Activator
2.Queue Proxy 和 App 路径优化
3. 优化 Knative Gateway 性能
分析对比去除 Activator 路径带来的性能收益,如下表,将 Activator 移出核心数据路径后,QPS 能力提升三倍(7776.1257 -> 25569.5698),且 P99 延迟大幅降低
1. 组件优化
经过对 Queue Proxy-> App 路径分析,有两种优化方法,阐述如下:
优化方向一: 优化 Queue Proxy
优化 Queue Proxy HTTP 代理解析过程,延迟大幅度降低(4.2->2.4 ms),且 CPU 使用率大幅度降低(代理 CPU 使用率接近 Server CPU 使用率),QPS 小幅度提升。
优化方向二:将 Queue Proxy 替换成 Envoy
优化后 QPS 相对社区 Queue Proxy 版本提升 23%,延迟也大幅度降低,代理 CPU 使用率接近 ServerCPU 使用率。
2. 框架优化
框架上协议栈穿透,通过 sockmap 以及 sock redirect 特性加速 Queue Proxy 和业务容器 App 之间通信。原理如下图:
基于轻舟的基于 EBPF 的高性能网络加速组件–SOPS,开启该功能,测试结果如下:
注:单位百分百 CPU 支撑的 Qps= Qps/(Gateway(cpu%)+ QueueProxy(cpu%) + server(cpu%))
Knative 社区也在讨论 Envoy 代替 Queue Proxy,但是具体何时未知,考虑到工作量较大,我们打算 follow 社区进度;从性能角度和使用场景考虑,当前优化 Queue Proxy 也不差,所以先优化 Queue Proxy 来满足业务需求。
网关性能优化方面轻舟网关团队已经做了较多工作,性能也较为可观,在 Knative 当前使用场景,我们计划将 Gateway 底层网络更换成轻舟高性能网络,继续降低延迟,提升 Gateway 性能天花板,降低 CPU 使用率,使得单位百分比 CPU 支撑更多的业务 QPS。
Knative 框架内,默认情况下引入了三层代理路径,固定压力(70 连接)下测试发现,默认情况下 Knative 性能表现非常不佳;经过调优(去除 Activator 这⼀层代理 + 使用 Queue Proxy v0.14 并优化 + 使用 Sops 加速 Queue Proxy 和 App 路径)Knative 框架性能表现还是非常优异的。
和裸业务容器相比:
1、单位百分比 CPU 支撑的 QPS 5:1
2、链路的变长,当前测试场景和测试方法下,链路延迟 p90 提升 2.5->4.7 约 2.2ms
从性能角度看 Knative 业务容器和裸业务容器,直接使用容器性能是最好的,使用 Knative 业务容器牺牲还是可以接受的。
下面进一步探讨,Knative 和 K8s 到底是什么关系?
从功能角度看,Knative 框架是 K8s 补充,工作在业务层次,解决业务的 “什么时候该扩容”,“怎么扩容”,“什么时候触发业务运行”等问题,是专业搞定业务自动扩缩容和事件触发的功能组件。
Knative 框架支持功能并不什么新鲜事情,其功能特性,完全可以通过,K8s 容器 + 智能网关 + 自定义扩容数据收集机制和并发控制 + 自己编码事件机制来代替 + 上层业务封装逻辑来替代,但是需较多的研发投入,而且对接 API 为私有 API 接口,对用户有绑定。
从自动扩容角度看,业务的扩缩容也可以通过 HPA 来完成,但是这种方案速度较慢,一般 3-5 分钟,无法适应业务快速扩容需求。
我们基于 K8s 角度对 Knative 框架下一个定义:Knaitve=K8s++,是⼀种对 K8s 补充,是一种通过牺牲 CPU 和局部的延迟,换取业务流量管理能力(红绿发布、回滚、流量管理)和业务扩展能⼒(自动扩容能力、事件机制等)的开源软件框架。
前文性能分析均是基于单个业务 Pod,Knative 本身性能可从单个业务 Pod 横向扩展出多个业务 Pod 来进行性能扩容而且其非常擅长这一点,这里不赘述。下面介绍业务流量如何从外部导入 Knative 系统,因为 Knative 系统内部扩展性没什么问题,我们需要使得接入 Knative 系统部分具备更强的横向扩展能⼒,以满足业务扩容的性能需求。
在选型之初,我们打算按照如下架构图,流量接入到 Knative 系统。流量经过 Nginx Https 代理 -> 轻舟网关 -> 轻舟 Knative 网关 -> Queue Proxy -> 业务 App,其中:
Nginx:主要作用是 HTTPS 加密
轻舟网关:主要做 URL 路径和域名路径的转换、业务降级
轻舟 Knative 网关:主要实现红绿发布、流量管理等功能
上图中业务路径比较长,特别是经过了三次 7 层网关(Nginx、轻舟网关、轻舟 Knative 网关),且 Nginx 和轻舟网关存在能力集重复问题,所以我们打算做如下调整:
使用轻舟网关接管 Nginx 的 HTTPS 的能⼒,缩短七层代理的路径,采用轻舟网关自动降级功能,业务降低避免了人为操作,为了方案的可扩展(性能横向扩展、未来 IPv6 需要等)和部署的灵活性,引入低延时的轻舟的四层 LB。架构如下图,做到各个层可横向扩展。
因为方案降级需求,轻舟网关需要连云外的降级资源,但 Knative 网关无法管理该云外资源,所以目前无法将 Knative 网关和轻舟网关融合成一个网关。未来 Serverless 平台不再需要云外的降级资源,再将轻舟网关和轻舟 Knative 网关合并,达到如下流量框架,进一步缩短流量路径,达到最佳性能。
实现轻舟网关和轻舟 Knative 网关的融合需要修改 Knative,原因是:Knative 默认通过域名来区分应用,非常适合公有云,但是往往私有云业务场景,域名是固定且受限的,甚至有时候固定嵌入到客户端代码中,所以通过域名进行应用区分非常不适合,对于 HTTP 协议来说,我们需要使用请求路径来区分应用。
基于云原生化的泛前端部署平台依赖的底层 Knative 场景,通过分析,我们发现 Knative 社区默认情况下性能非常差,配置调优(不注入 Sidecar + 将 Activator 从数据路径中去除后 + 使用 Queue Proxy v0.14 版本)后,除 Queue Proxy CPU 偏高外,性能还可以,特别是经过调优 Queue Proxy、框架上协议栈穿透优化以及业务流量导入路径缩短后,性能可满足绝大部分业务需求,目前社区对于性能也在继续优化中(v0.14 相比 v0.9 QPS 性能约有 55% 提升),我们相信社区 Knative 数据路径的性能会越来越好。
作者简介:
刘勤龙,网易杭州研究院资深云计算开发工程师,7 年服务端开发和优化经验,负责网易轻舟四层负载均衡数据面设计,参与轻舟服务网格性能优化,目前专注于轻舟云原生 Serverless 平台底层的开发和优化工作。主要关注 Kubernetes、Istio、Knative、Cilium 等技术领域。