触发这件事情的原因有两部分,第一是趋势,因为前端发展趋势使得前端工程师的工程化效率正在降低,作为网易的基础软件团队,网易数帆需要思考能否从基础软件层面解决前端的困扰,第二部分是当前前端工程师正面临服务端渲染的问题。
首先说趋势,体现在整个前端发展过程中。从以前的 Web 工程师,到前端工程师的职位的出现,当时还只是逻辑分工。第三阶段是前端工程师时代,是一个前后端分离的时代。到我们目前正在处于的前后端(BFF)时代,前端工程师需要去负责部分后端的数据。甚至有些公司已经到了全栈工程师的时代,前端工程师需要负责后端所有的数据。
第二阶段和第三阶段,前端人员不需要关心后端资源,只需要把自己的代码写好,由后端或者是运维帮他们去做发布。但是目前所处的阶段,前端人员是需要去关心后端数据的,将来的全栈工程师时代,他们还需要去直接从数据库里边获取 / 操作数据,这个时候我们会发现,他需要操心低层的资源,因为他需要把他的程序跑在服务端的后端,而前端工程师实际上不擅长操作和运维后端资源,因此会使得前端工程师的工程化效率降低。
第二点是我们当前正在面临的问题,因为前端工程师目前在做服务端渲染(SSR),采用这个技术的时候他需要自己申请机器,自己部署,同时他还得关注机器的状态,并且他还得时常地翻阅基础设施提供一些运维指南去做运维,其实这个事情是他们很不擅长的,并且对他们来说是一个负担。
同时从后端运维的视角来看,前端工程师是在浪费资源。这有两个原因,第一,很多业务属于活动类型的;第二,前端工程师在申请机器的时候,他往往按照上限来预留资源,这就导致了大量的浪费。因此这就产生了“前端工程师在浪费资源”的问题。但其实前端工程师不是故意的,因为他们不擅长运维服务器。因此我们需要解决前端工程师正在面临的痛点。
从前端工程师的角度,他们的核心诉求就是只写代码,聚焦核心业务,去创造核心价值,把一些服务端后端运维的事情完全交付给基础设施去做,他们不需要去 care 这个事情。Serverless 的先进理念,其精华正是复杂度转移,使得业务人员能够聚焦他的核心场景,所以我们想打造一款 Serverless 产品,来解决前端工程师当前的困扰。
打造 Serverless 平台之前,首先要对这个目标做拆解,然后再去做产品选型、技术选型。首先看目标拆解,第一点业务层的需求是即用即上,也就是说前端他想上线一个业务,他不需要去关心太多,他想什么时候上线就可以什么时候上线;第二点就是前端工程师不想去做后端资源的运维;第三点实际上不是前端工程师的需求,而是基础设施层面的一个需求,就是尽量地能支撑好业务,同时也能够省机器省钱。
这相对应的目标拆解为:
即用即上——需要在技术上具备一键部署的能力;
免运维——需要把具体的运维沉淀到基础设施层;
节省开支——要求我们这个业务状态相对来说是无状态的,具备高弹性的能力。
因此我们的 Serverless 平台要能够集成 CI/CD,同时也能够进行容器化,工作在 Kubernetes 上,具备自动扩缩容的能力。
再看产品选型。既然公有云 Serverless 做得这么成熟,我们能否直接采用公有云产品来满足我们业务开发需求?我们的回答是 NO,因为我们需要去满足一些定制化的用户需求,而公有云的 Serverless 产品会受到很多资源包括使用的一些限制;第二个因素是我们的业务存在一些环境依赖,前端所依赖的一些数据库、中间件,都是运行在网易公司的私有云环境中,这些东西公有云环境上都没有;第三点也是追求技术自由,避免厂商锁定。 所以,我们需要在私有云平台中打造自己的 Serverless 平台。
在技术选型上,我们基于三个原因选型了 Knative。
底层要基于 Kubernetes: 因为数帆对 Kubernetes 的运维手段比较成熟,网易本身有大量的业务运行在 Kubernetes 上,我们有专业的团队来做相应的支撑。
基于镜像去构建 Serverless: 因为网易本身的业务是比较灵活多变的,会存在一些程序启动时间比较长的业务,这种业务不是很适合 FaaS。因此我们考虑让它把整个启动过程压缩,通过镜像打包的一种方式来去解决。另外我们还要求易扩展成 FaaS。这些 Knative 都能满足。
背景深厚:Knative 后面还有 Google、IBM、Redhat 这些大厂作为支撑。
轻舟 Serverless 平台设计全景图如下,从下往上有基础设施层、服务层、应用编排层和业务层。其中我们比较关注的是应用编排层和服务层。整个 Serverless 平台工作在已有的基础设施之上,并且我们通过这个 Serverless 平台后端,把它所需要的资源纳入平台组件能力,比如说 CI/CD、Knative API 网关,把这些资源结合起来,满足我们 Serverless 的具体需求。
轻舟 Serverless 平台具体的构成,从下图可以看到,Serverless 的核心,我们可以理解成额外做的核心组件,包括一个 Serverless 前端控制台和一个 Serverless 后端。这个核心负责把现有的一些组件联合起来:通过 CI 去完成镜像构建的需求;通过 Knative 去做部署;通过 API 网关去做具体业务的数据链路打通;同时还需要 Gitlab 来存储代码;为了更高的开发效率,需要集成 Web IDE,这样前端开发人员可以只在一个浏览器上就完成他所有的需求;同时为了运维能力,需要去支持平台层面的日志平台,以及监控告警的平台;还需要一些预警,就是 Serverless 跑批和预警的一些组件。
轻舟 Serverless 平台在这样的构成下,它具体的流程,我们从业务开发者的视角来看,业务开发者先通过 Web IDE,或者是其他的本地开发工具来完成编码,再把代码 Push 到 GitLab 上去,然后就会触发构建镜像,或者他可以选择手动触发,或者是通过命令行触发。当然这一步也可以把相关的一些资源,降级的静态资源,构建到放到对象存储里面去。Serverless 控制台构建完镜像以后,会通过 Knative 的 Service 去发布这个程序。整个发布过程它会主动地拉取刚才构建的镜像,去做应用的部署和数据链路的打通。这就完成了整个业务的一键部署。
一键部署的业务流量模型,首先流量从外网打到外层的基于 Envoy 的 API 网关,API 网关会把流量发给内层的 Knative 网关。随着访问压力的增大,我们要求它能够扩容;而随着资源处在访问的低档期,为了能腾出更多的资源,我们要求它具备缩容的能力。同时这个 Serverless 平台还必须考虑,当这一部分产生了异常的情况下,是否具备降级的能力。我们通过外层的 API 网关去做降级和和静态资源的获取,通过这种方式,如果 Serverless 平台或者 Knative 这一层出了问题,用户可以一键切到已经准备好的静态资源中,不至于让业务产生异常。
打造 Serverless 平台还有很多必要的考虑。首先平台构建形态方面,为了满足用户的效率需求,我们支持了 FaaS 层平台;为了满足业务复杂又多变的需求,我们支持了传统工程的形态。
其中 FaaS 形态最主要的目的是让我们的业务方,特别是前端开发者,可以完全在 Web 浏览器上编码、发布,也就是说他只要身边有一台电脑,通过浏览器就可以随时随地完成他的业务目标,不需要安装一些开发环境。除了这种方式以外,考虑传统使用者的诉求,我们也支持集成到 VS Code 中,同时还支持最传统的通过命令行直接构建 FaaS 的方式,当前我们支持的还只是 Node.js,这个是属于我们用得比较多的技术栈。
对于传统工程形态这种方式,Knative 当前是支持的,这种方式最主要的作用是,用户不需要学习新知识就可以直接使用 Serverless,可以像以前一样开发代码,同时它的运营实施比较灵活。一些启动过程很慢的业务,也完全可以采用传统工程形态去做,这样不至于说这类业务没有办法在这个平台上运行。
所以说,我们做了这两种产品形态来满足业务方在不同状况下的使用需求。
因为 Knative 网关只能通过子域名的方式去做业务区分,而我们传统业务,特别是现有的很多线上业务,往往是使用 Host+Path 这种方式去做区分的。为了满足用户的这种使用习惯,我们当然可以修改 Knative 层的开源实现,但是开源又是在不断迭代的,我们考虑再三,做了一些框架结构,通过外置的一层 API 网关去做具体的 Host+Path 的区分,然后转换成 Knative 所支持的子域名方式去做应用区分。
同时,我们引入外层的 API 网关还有另一个好处,当 Knative 这一层出问题时,我们可以通过这一层网关去做灰度降级,来保证业务的稳定性。
当然,加一层外置网关也会导致数据链路变长,它从外层 API 网关,到内层 Knative 网关,再到 Activator 组件,甚至到 QueueProxy,最后才到业务容器。这样会导致 QPS 比较低,这方面后文我们会给出轻舟团队具体的解决办法。
平台需要的日志、监控告警、BaaS 等能力,由于轻舟平台已有现成的封装,Serverless 平台直接集成。
此外,为了适应 Serverless 这种业务场景,我们额外开发了一个预警的组件,去模拟前端业务方发布一个业务,发布之后把它给部署到 Serverless 平台上,然后检查它能否很好地完成扩缩容。
接下来分享在打造轻舟 Serverless 平台的过程中,我们对选型的 Knative 这个开源组件所做的事情,主要分为三个部分,第一部分是我们在数据面上做了哪些东西,第二部分是我们在控制面做了哪些优化,第三部分是说使用了 Knative 组件,我们遇到了哪些问题,又是如何解决的。
我们在数据面做的主要工作是数据链路调优。上文也提到 Knative 整个数据链路比较长,我们通过对 Knative 的压测,确定 Knative 的性能有很大的问题。我们通过以下的 5 个步骤来做优化:
在整个的数据路径上,把 Activator 从数据路径上去除,因为我们是面向 Web 场景,去除之后不会产生任何业务问题。
把 Knative 做升级,从以前的 0.9 升级到 0.14。通过 1 和 2,整个 QPS 提升了大概 50%。
为了满足我们一些核心业务对延迟或者是对高性能的需求,我们采用 Fast HTTP 优化了 QueueProxy,把 QueueProxy 的 CPU 使用率降了 50%,同时在降低 CPU 使用率的情况下,它的延迟也降了 30% 左右,QPS 也提升了 30%,也就说使用更少的 CPU,反而带来更多的 QPS 和更低的延迟效果。同时我们做了一个 Revision Deployment 级别的灰度,来降低修改 QeueuProxy 带来的业务风险。
在第 3 步优化完 QueueProxy 以后,我们引入了网易轻舟的 Sockops 组件来做框架优化,通过 eBPF 技术实现 QueueProxy 和业务容器之间的 Sidecar 方式的链路优化,QPS 可以在之前的基础上再额外提升 20%,同时延迟降低 8% 左右。
针对一些特殊业务需求,我们选型更高性能的容器网络,叫 SR-IOV 网络,来满足它的需求。从我们的实测来看,SR-IOV 容器网络在延迟方面比普通的容器网络大概要降低 10%,同时它的 QPS 是接近物理机的。
通过这 5 个步骤,我们满足了不同的业务方的需求。一般来讲,做完 1 和 2 能满足差不多满足一半的业务需求,做完 3 和 4 基本上能满足绝大部分需求,第 5 步是满足一些特殊的业务场景的需求。
控制面上我们主要解决了 Knative ksvc Ready 时间变长的问题。Knative ksvc Ready 时间变长有两个原因。第一个原因是 Serverless Knative 网关,我们采用的是轻舟的 API 网关,它的控制面是依赖于 Pilot,而我们的业务集群又是和服务网格在一起运行的,在默认情况下,Knative 的 Pilot 能感知到服务网格的 VS、DR、SE 这些资源,这就导致了它去做一些无关的资源运算,从而 Knative ksvc Ready 时间变长。
第二原因,Knative 有一个组件叫做 network-istio,这个组件需要对整个数据路径去做健康检查,只有在健康检查成功以后它才把 ksvc 置成 Ready 状态。但健康检查有一个特点,它是指数级别回退的,这就导致了如果 5 秒内它没有检查通过,它就只能在 10 秒内才能发现这个东西是健康的;如果它在 10 秒内还没有解决这个问题,检查出来业务已经 Ready 了,它就只能在 20 秒内才能发现业务已经 Ready,这就导致 ksvc Ready 时间变得更长。
我们解决的办法有两种,第一种是针对于第一个问题,Knative 网关它只 care 自己的 Namespace 级别的资源,去做一些资源隔离,它不 care 同一个集群内的服务网格的相关的其他资源。
第二种就是我们调整了 network-istio 健康检查回退机制,调整成前 20 秒内每秒钟检查一次,20 秒以后才进行指数级回退,通过这种方法防止 ksvc Ready 时间慢的问题。
Knative 层我们主要遇到了如下的困难:
Knative 的一个 Autoscaler 组件,它是不支持 HA 的,但是 Knative Autoscaler 组件是扩缩容的核心组件,因此这是业务无法忍受的。
一个老生常谈的冷启动问题,我们经过实测,整个的冷启动过程需要 5 秒以上,如果算上拉镜像的时间,甚至可能需要 6~7 秒。
做 Knative 适配的时候,和我们内部的容器网络有些冲突,它会导致 Knative 的一些 Webhook 启动失败。
适配轻舟 API 网关的时候出现 503 问题。
net-isito 组件做完健康检查以后它的连接不会释放,这就导致它的连接大量的堆积,最终导致业务异常。
我们的解法如下:
针对 Autoscaler 不支持 HA 的问题,我们通过 Pick Knativ0.19 这个版本的一些代码去解决。
针对冷启动,我们通过一些预留和一个默认实例来避免冷启动。
Webhook 这个问题,最主要是和我们内部容器网络的冲突,我们通过调整网络方式为 hostNetwork 来规避。
适配轻舟 API 网关出现 503,最主要也是 Knative 本身的一些问题,因为网关的控制面上存在一些特殊符号,它没有办法识别,这种情况下 0.14 版本的 Network-isito 就没有办法继续往下工作,不过这个问题在 Knative0.15 得到了修复。
健康检查连接不释放,这也在 Knative 0.15 得到了解决。
当然我们在一开始踩这个坑的时候,Knative 社区还没有解决这些问题,后面我们解决完问题以后,发现社区也已经解决了。所以说我们也是通过检验了,因为我们是尽量地少动 Knative 本身的代码,也是通过这种升级的方式来解决的。
目前轻舟 Serverless 平台在网易内部已经上线了大概 200 多个前端应用,当然在 2020 年的 Q4 我们也 Release 了一个商业化版本满足对外的需求。我们做完这个事情以后,前端人员的体验是怎么样的呢?
首先,我们从前端人员那边收集到的数据,他们以前去部署环境,新员工可能要两周左右,老员工也要三天左右,有了 Serverless 平台,整个部署时间统一降低到三分钟内搞定。
其次,因为 Serverless 平台的一些封装使得了前端人员不再需要关注后端资源,所以说他们从业务选型上,可以考虑一些服务端渲染的技术来提升业务的首屏体验。
再次,从趋势上来说,免去运维的困扰更加符合前端趋势,这让前端人员可以轻松地去开发 BFF 层的一些需求,甚至可以支撑他们往全栈工程师去过渡,通过这些支持给前端提供一个更大的灵活度。
展望未来,我们需要在网易公司内部业务场景继续打磨轻舟 Serverless 平台,让它变得更加稳定,功能更加强大,主要聚焦三个方面:
首先我们会提升 Serverless 平台的产品能力,考虑使用 Knative 的 Eventing 组件,去融合我们的轻舟中间件、对象存储这些底层设施的资源,来满足业务更加多变的需求。
其次是我们 Serverless 平台上线以后,前段时间业务方反馈它整个的调试化过程的效率降低了,所以我们打算提供一些本地的调试套件去解决调试的困扰。
最后,我们会紧跟着 Knative 社区,关注 Knative FaaS Kn 的实现,同时也考虑去对接公有云的一些 Serverless Framwork,一些 API 规范标准,这样将来如果业务方有需求,它完全可以无感地从私有云平台上迁到公有云平台上,所以我们打算做这么一层封装。
刘勤龙,网易数帆轻舟事业部资深开发工程师,云原生社区 Knative SIG 发起者,8 年服务端开发和优化经验,负责网易轻舟四层负载均衡数据面设计,参与轻舟服务网格性能优化,目前专注于轻舟云原生 Serverless 平台的开发和优化工作,支撑 Serverless 在网易云音乐、网易严选、网易传媒的落地工作。主要关注 Kubernetes、Istio、Knative、Cilium 等技术领域。
傅轶,网易数帆轻舟事业部高级研发工程师,目前专注网易容器和微服务平台研发,致力于网易内部容器技术及其生态体系建设,对 Kubernetes、Serverless、日志服务有深入研究,具有丰富的云原生分布式架构设计开发经验与项目实践。
QCon+ 全称 QConPlus 案例研习社,是以线上为主的综合的学习平台。希望能够帮助用户更加便捷、高效地了解行业最佳实践和技术发展趋势。内容源于实践,面向社区。演讲嘉宾依据热点话题,面向 5 年以上工作经验的技术团队负责人、架构师、工程总监、开发人员分享技术创新和实践。扫描下图【二维码】或点击【阅读原文】在 QCon+ 上查看本文的相关案例视频。