出品 | CSDN云原生
2022年9月1日,在中国信通院、腾讯云、FinOps产业标准工作组联合发起的《原动力x云原生正发声 降本增效大讲堂》系列直播活动第7讲上,作业帮基础架构负责人董晓聪分享了作业帮的云原生降本增效实践。本文整理自董晓聪的分享。
复杂化:技术栈极为丰富,涵盖多种主流语言。
作业帮于2020年初开始走一条云原生的道路,来解决发展过程中所遇到的稳定性、成本、效率及安全方面的问题。通过云原生的改造,用基础设施接管业务当中大量的非功能逻辑,以此实现弹性、可观测性、韧性、自动化及可持续性。
随着互联网红利的消退,企业希望能够实现成本效益最大化;
资源浪费现象普遍存在,节省不必要的浪费;
从技术人员的角度来看,期望写出更优质、更高性能的代码。
在降本增效的过程中,我们需要明确一点:降本但不能降质,在降低成本的同时,稳定性、效率、安全等均不能为此打折扣。
在多种限制条件存在的情况下,该如何开展降本增效的工作呢?
应用层首先要做的是提升性能,即提升单位资源能够支撑的业务并发量。对于作业帮来说,多元的技术栈给应用层的效能提升带来了挑战。
资源层降本增效的重点在于计算层面的优化,存在两大挑战:
拥有合适的机型后,实现业务平滑无感地切换。
资源调度层有着巨大的优化空间,同样也面临诸多挑战:
资源时间不均,互联网业务资源的使用存在明显的波峰、波谷。
应用技术栈改造
起初,作业帮采用PHP为服务端的主要开发语言,并使用ODP框架,能够有效地支撑新业务的快速构建及发展迭代。但随着业务的发展,以ODP为代表的PHP服务端技术栈遇到了诸多问题:
云原生适配不足,云原生带来诸多技术红利,如容器化、服务治理、DevOps等,但PHP适配性较低,无法与其结合。
于是我们选择使用Go语言代替PHP作为服务端的主要开发语言。Go语言框架ZGIN基于GIN衍生而来,是面向Web服务的开发框架,提供了开箱即用的常见组件和功能,侧重通用性和稳定性,兼顾性能和时延。
GIN是一个通用性应用框架,我们在GIN的基础上做了性能优化,用来适配底层基础架构技术方案,同时带来业务场景下的性能提升。
与GIN相比,HttpServer中一直以性能著称的fasthttp具有一个显著的优势:复用连接池。但我们并未在GIN的二次开发中实现连接池,而是将能力下移,借助底层架构的能力使多语言栈保持同等能力。大多数服务模块目前已经接入Service Mesh,上游请求与Mesh-Proxy建立连接,Mesh-Proxy和服务模块之间通过UDS通信且保持长链接,同时优化UDS的零拷贝问题。
效率工具方面,提供简单易用的代码生成工具,根据脚手架快速地生成服务代码。通过代码生成工具,规范业务对框架的使用,减少业务后期追查及维护问题的成本。
测试环境互通工具方面,提供简单的命令工具,将远端测试环境的流量代理到本地端口,也可以将本地模块的出流量转发到测试环境之上。
经过两年的发展,Go语言已演变成为作业帮使用最多的服务端语言,当前基于Go语言构建的模块总计已达600+,性能提升十分明显。如上图所示,使用Go语言重构应用模块后能够带来五倍以上的性能提升。
核心系统云原生改造
检索系统作为公司最底层的核心系统之一,是C语言栈下的倒排索引和综合策略索引,为智能推荐、资料智能分析以及搜索等提供底层支持。机器规模千台级别,计算核心达千万核以上,时效数据达百TB,数据日增量为TB级别。
检索系统底层设施主要包括两部分:数据产出服务与数据存储服务。
由于检索系统对低时延、高稳定性以及高吞吐性能的要求,早期选择使用本地存储,带来了计算与存储的耦合问题。随着数据量的增大,单机无法承载所有数据量,需要对数据进行切片,每个节点存储部分数据。出于高并发、高可用的要求,每个切片还需有多个副本。
当数据需要更新时,由于数据量庞大,数据产出服务无法一次将全量的数据存至线上的节点中,只能选择部分数据进行同步,随后以该部分节点为基准进行二次、三次分发。这就导致数据更新时间长、运维成本高、占用资源多等问题。
通过对检索系统架构的分析,可以看出,当前的关键问题都是由于计算存储耦合所导致。只有引入计算存储分离的架构,才能从根本上解决复杂度的问题。而新的解决方案应该具备以下能力:
数据集合的可伸缩性。
基于上述要求,在技术选型方面,我们选择了Fluid与JindoRuntime的组合。Fluid是开源的Kubernetes原生的分布式数据集编排和加速引擎,它通过数据的编排、应用数据的调度,解决云原生过程中所遇到的一些列数据问题,如访问数据的时延过高、多数据联合查询困难等。JindoRuntime是Fluid的一种分布式缓存Runtime实现,实现了HDFS、S3协议的访问与缓存加速。
通过云原生改造,检索系统变成了一个标准的无状态容器应用,数据应用的过程得到简化。
数据产出服务将数据传到对象存储中,Fluid驱动JindoRuntime完成对数据的加载。检索的进程通过Fluid挂载的PVC访问fuse,fuse将POSIX协议转化为对远端JindoRuntime的RPC访问。
完成架构改造后,对检索系统的瓶颈进行分析,发现导致性能无法提升的原因在于跨NUMA的内存访问带宽存在上限。
通过添加调度器,使进程绑定NUMA,保证CPU和内存的访问不跨NUMA,能够使该问题得到妥善解决。
资源调度层降本增效的核心在于解决上文所提到的在线集群负载不高、资源空间及时间分布不均等一系列问题。
自定义调度器
容器化改造使得机器的平均负载得到显著提升,但不同的机器之间差异较大,部分机器在业务高峰阶段还需进行额外的运维工作,如人工封锁高负载机器、人工驱逐不均衡Pod等。为什么会出现这样的问题呢?
主要原因在于Kubernetes原生调度器的调度依据以request为主,只考虑简单的指标,没有考虑未来的变化。于是我们通过自定义调度器来解决此问题:
除了将CPU和内存作为指标考虑外,添加更多因子多维考虑。
在离线混部
互联网业务都有明显的波峰、波谷,波谷时段有大量的剩余资源处在空闲状态,考虑到大数据离线计算需要大量计算资源并对实时性的要求较弱,我们可以在在线集群的波谷时期运行大数据离线任务,达到节省计算资源的目的,实现双赢。但在实际实施中,存在资源隔离无法避免干扰、隔离效果差等问题。
对此,我们使用腾讯TLinux的新特性,在内核的CFS之间,添加一个新的调度器Offline,实现CPU避让,并对空闲资源做预测调度。
Serveless广泛使用
Serverless一直是作业帮技术架构探索的核心方向之一,用于解决资源分布时间不均的问题。Serverless的主要运行方案有两种,一种是函数计算,另一种是K8s虚拟节点。K8s虚拟节点具有对现有服务无使用差异、用户体验较好、业务服务无感知的特点,可以基于现有基础架构进行迁移。
Serverless具有较强的成本优势,将在线服务高峰期弹性调度到Serverless上,可以大量节省资源成本。在推进Serveless的过程中存在诸多挑战,在调度层需要解决两个主要问题:
缩容时应如何实现优先缩虚拟节点上的Pod。
方案一:原生标签能力。通过Node Selector、节点亲和等利用虚拟节点资源,但这种方式是割裂的,同一个服务无法运行在两种类型的节点上。
方案二:云厂商调度扩展。用户无需指定Selector,当固定节点的资源不足时再调度到虚拟节点之上。但在集群资源已满的情况下,才进行虚拟节点调度会导致Pod部署过于密集,节点负载过高,影响业务稳定性。
方案三:自研调度器。将超过阈值的Pod调度到虚拟节点之上,其中阈值基于预测推荐+人工调整而定。这种方式可以有效兼顾稳定性和成本。
日志采集方面,采用CRD配置日志采集,将日志发送到统一的Kafka,通过资源的日志消费服务,消费云厂商的自有节点日志,实现虚拟节点和正常节点上的日志统一。
监控方面,云厂商虚拟节点实现的Metrics接口和Kubelet完全兼容,可以无缝接入 Prometheus,完成对Pod实时CPU、内存、磁盘及网络流量等的监控。
在分布式追踪方面,由于无法部署Daemonset形式的jeager-agent,我们在jeager-client端做了改造,通过环境变量识别Pod的运行环境,如果是在虚拟节点上运行,则跳过jeager-agent,直接将分布式追踪的数据推送到jeager-collector。
作业帮基于云原生进行了一系列改造,最终实现了降本增效,整体的降本服务度已达到40%,未来会继续探索更具性价比的降本增效方式。此外,作业帮运营工作当前已实现从靠人到靠平台的过渡,将进一步向BI化、AI化演进。
【原动力×云原生正发声降本增效大讲堂】第一期聚焦在优秀实践方法论、资源与弹性、架构设计;第二期聚焦全场景在离线混部、K8s GPU资源效率提升、K8s资源拓扑感知调度主题;第三期邀请4家业界知名企业分享各企业云原生降本增效技术实践,为开发者带来更多样化场景业务下的技术干货。扫描下方二维码或点击『阅读原文』进入活动专题页,带你体验云原生降本增效实践案例、了解如何解决企业用云痛点、掌握降本增效关键技能……