后 Hadoop 时代,字节跳动如何打造云原生计算平台 | 卓越技术团队访谈录

2022-11-28 16:50:33 浏览数 (1)

采访嘉宾 | 李亚坤

编辑 | Tina

技术永远是在“更新”或“替换”中得到发展。

在大数据行业里,2006 年 Hadoop 的诞生,给我们带来了变革意义的改变,大数据生态组件也开始层出不穷。各种不同体型的企业都喜欢选择开源大数据软件来搭建自己的系统,无论是先前十分繁荣的 Hadoop,还是后来涌现出来的 Kafka、Flink 等,都被广泛地使用着。

十多年来,这些系统经历了多轮技术洗礼,我们也随之需要根据新的技术潮流不断地调整甚至做技术转型。以 Hadoop 三大组件来说,计算引擎 MapReduce 基本被 Spark 取代。在数据上云的时代,对象存储也取代了一部分 HDFS 文件系统。近几年,云原生又火了起来,行业里再次开始了对大数据体系的云原生改造,同时 Kubernetes(K8s) 的流行,也让同为资源管理的 YARN 地位越来越尴尬。

然而,过去的技术体系在很多企业系统里仍发挥着很重要的作用,在技术更新迭代的时候,业务不能随意变动,那么我们在新旧技术共存的条件下,如何发挥新技术的最大潜力?

字节跳动成立于 2012 年,也是大数据崛起之时,跟众多中小企业一样,字节跳动也是 Hadoop 生态组件的重度用户。这十年在业务演进的过程中,字节跳动锤炼出了自己的一套方法,他们在保持接口不变的条件下,对底层做了大量的定制化甚至是重写工作。作为火山引擎云原生计算研发负责人,李亚坤见证了字节从几千台机器到几十万台机器的成长过程。InfoQ 通过采访李亚坤,一起回顾了字节是如何应对大数据技术的不断淘汰和革新,同时还能做到让产品达到优异性能。

云原生计算体系

一个企业能够利用数据解决问题,那么背后都会有一套完整的工具和技术堆栈。

依据 2021 年的公开数据,字节跳动发展至今,已在全球拥有 19 亿用户,于 150 个国家和地区提供产品和服务。业务的数据存储和日志规模每日已达到 EB 级别,实时推荐峰值每秒达到百万次。

支撑这些服务的,是字节跳动打磨的一套云原生大数据技术栈,涵盖了从数据接入、数据存储、数据计算到数据服务的所有环节。其中,存储层是基于 HDFS 进行深度定制的 CloudFS Iceberg,中间件包括 Kafka 和字节自研的 BMQ,计算引擎使用的是 Spark / Flink,还包括资源调度和混部,以及 HSAP 和外围服务。这套系统能管控达到几十万台机器,行业内达到 10 万级别体量的,就是非常领先的了。

在这套系统中,为了支撑 EB 级别的数据存储,字节跳动用 C 重写了一套 HDFS,集群规模扩大的同时,也能兼顾性能。同时存储层也能托管各种不同的企业存储系统,对象存储、原始 HDFS 存储都能平滑融入使用。

在资源管理和调度层面上,由于服务规模巨大,字节跳动也经过上千次的修改,提供了定制化的 YARN 服务,并可平滑迁移到 K8s 上。YARN 是 Hadoop 集群的资源管理系统,被字节多款产品重度依赖。

消息中间件 BMQ 也是字节跳动用 C 重写的一套存算分离架构的消息队列服务,同样支持 Kafka 系统的平滑迁移。

在云原生发展趋势下,字节跳动于 2016 年开始启动 TCE(Toutiao Cloud Engine)云引擎,2018 年开始将核心业务迁移到了这个容器平台上,随后在离线、在线业务全部容器化的基础上,开始进行进行在离线混部调度设计和存储的云原生化。全部云原生化后,这套系统,包括底层调度能力、存储、中间件、计算引擎,就能在各种 K8s 集群上整合并拉起来。

李亚坤总结说,这套系统很重要的一点是“一出生就是长在开源上,不管演进多少年,这套开源的协议始终不变。无论是 HDFS、Kafka、YARN,还是 Spark、Flink,都承载着巨大的用户体量。这套协议有时候可能没有那么好,没那么规范,但是我们也必须得把它继承下来。在这套协议存在的前提下,字节在内部其实做了非常多的定制化,以及各种各样的重写,付出了非常大的工程力量,让整体性能得到了非常大的提升。虽然用户可能用起来跟原来的接口差不多,但是实际上底下已经发生了翻天覆地的变化。”

计算引擎:Spark 和 Flink 保持持平

字节跳动有很多移动端产品,包括抖音、今日头条等,“计算能力”应用到了这些产品的每个环节,实时推荐、风控安全、实时大屏等等。在进行实时信息流推荐时,每次用户刷新,App 就会从亿万级别的内容库里,选出用户感兴趣的内容,经过粗排、精排,对这些内容进行打分排序等一系列计算,选出用户感兴趣的内容,这些都跟计算相关。

2014 年,Flink 被捐献给 Apache 基金会,从此走进了大众的视野。2017 年,字节跳动开始调研并逐步使用 Flink 进行流式计算,历经两年完成了从 JStorm 到 Flink 的迁移。另外在离线分析场景下,虽然 Spark 也能无短板的全面替换掉 MapReduce,但字节跳动的计算引擎也有一些特殊的地方,就是目前 Spark、Flink、Primus、MapReduce 四种计算引擎并存,其中 Spark 和 Flink 是保持持平的:流式计算每天所需资源超过 400 万核,批式计算资源超过 500 万核。

Flink 的流行其实也让业界重新思考了流批计算架构,从 2020 年开始,不少企业开始了基于 Flink 的“流批一体”实践,字节跳动也做过相关探索。

理论上,流批一体有一些价值,拿字节的实时推荐举例来说,推荐中需要先用历史数据去训练一部分模型,然后读取生产中最新的用户行为日志数据,训练出一个最贴近用户当前状态的模型。这种场景下,当前历史数据训练环节和实时数据训练环节在两个系统里是完全分割的。很多企业里,负责流处理和负责批处理的,会按照两个团队来划分任务,流团队负责处理生产上的最新数据,而批处理每天跑一下历史报表。有时候两支团队的数据和程序没办法对齐,会得出不一致的结果,这时候使用流批一体变成一支团队体验更为友好。

然而推荐场景下,流式计算本身存在一个问题,那就是会因为一些数据的晚到,或读取到了窗口之外的数据,带来精度上的损失。所以流式数据仅仅是作为参考,还是需要去以“天”级别重新跑一次历史数据,得到生产上的唯一的真实的结果。

目前字节跳动的 Flink 批处理功能使用场景还处于相对较少的阶段。在一些标准的、基于人操作的离线场景下,Spark 的批处理也比 Flink 更有优势,而字节内部绝大部分的分析就是关于人的这种商业分析。据李亚坤介绍,虽然现阶段 Flink 的批处理功能还没有得到特别大规模的应用,但从业务实际场景中体现的价值来看,可预见在字节跳动的批处理场景中,Spark 和 Flink 都将会得到广泛应用。

经过 5 年快速发展,字节跳动形成了超大计算规模,这同时也给大家带来了一些挑战。首先是资源的问题。当资源达到一个限度后,新资源的调配就会更难。Flink 每天平均 400 万核,已经不是一个小数目,所以云原生计算团队的办法是让调度系统支持更细粒度的资源申请。

在此之前,可能一个容器最少申请一个核,那么就是以一核两核这样的整数力度去增长。但很多时候任务可能只需要比如 2.1 核或者 2.5 核就能够跑起来了,这时候用户只能去申请三个核。如果一个 Flink job 可能是 1000 个容器构成的,就浪费近千个核,规模越大,浪费越严重。通过在运行时动态调节 Flink 任务的使用资源的方法,字节跳动可以在用户无感的情况下,将资源节省下来给更多业务使用。

另外就是服务器的规模达到一定数量后,机器的崩溃几乎是必然会出现的,这时候就需要在运行时去做容错。Flink Exactly Once 的特性决定了任何一个单机故障都会导致整个 Flink 作业的重启。在大规模模型训练场景下,需要上千个容器的时候,重启时间一次,要重新调度一次上千个容器,然后要去拉上千个容器的镜像,对线上效果的影响将会被无限放大,数据就相当于不实时了。在此背景下,云原生计算团队修改了 Flink DAG 的 Failover 实现,使得在特定的 Topology 下,单 Task 失败可以只重启单个 Task,从而实现了非常短的时间内的故障恢复。

资源调度

降本增效是每家企业应该考虑的问题。

提升服务器资源利用率,对于拥有大量机器资源的公司来说无疑是一个很重要的问题,也是企业研发实力的一种体现。有种说法是业界的资源利用率一般是 20%,但谷歌很多集群的利用率可以做到 60%,整体平均能达到 50%;国内企业比较偏向于业务,一些好的企业平均利用率处于 30% 到 40% 的水平。

特定集群的利用率比全局利用率更有代表性。全局利用率背后可能不仅仅是技术的问题,很多时候是跟业务相关的,比如在线业务在申请资源的时候,业务一般是按照最高峰值评估资源去申请资源,这就导致了对资源预估不准,申请的资源远大于实际使用资源。还有一些业务会比较重要,就会用独占的方式,并且要预留相当多的资源给临时的扩容去使用。

据李亚坤介绍,字节跳动计算资源的平均利用率超过 40%,部分混部集群的综合资源利用率达到 60% 以上,因为资源主要由基础架构部门进行统一规划,并在容器和 Kernel 底层都做了不少技术优化。字节跳动国内业务的容器规模很大,比如离线任务容器的生产和销毁每天接近两亿个。这种全量容器混部的方案不仅提升了利用率,并且灵活性也更高。

字节跳动每天有千万级别的作业,做好资源调度是一个非常重要的需求。

在字节跳动发展早期,调度系统首先满足的是批式计算,主要采用的是 Mesos 和 YARN。在 2010 年前后,Mesos 被 Twitter 这样的公司大量使用,后来逐渐下线,融入到了 K8s 中。而来自 Hadoop 体系里的 YARN,因为能比较好的满足批式计算,一直被各大小企业普遍采用。

任务混排、资源隔离是 Hadoop 体系比较大的硬伤。YARN 本身资源管理的粒度不够,单集群规模也只有 5K,字节跳动在这上面做了不少改进,开发了以千分之一核为最小粒度的功能,并且将单个生产集群扩大到了 5 万节点的规模。然而跟大多数公司一样,字节的离线场景基本构建在 YARN 生态上,在线则运行在 K8s 生态上,这样导致了一个问题:在离线两个集群基于不同的物理机器,资源相互隔离,资源无法共享。

在 YARN 服务规模巨大、场景复杂的前提条件下,设计资源统一池化和调度方案,如果选择将业务全部切到 K8s,用 K8s 的 API 再实现一遍,将是一个伤筋动骨、成本巨高的事情。而且 K8s 原有的调度器更适合在线的微服务,对于离线其实不是很友好,但字节跳动希望统一的池化能够同时对在线和离线友好,所以字节云原生计算团队选择了一个比较特别的方法重写了调度器系统。

云原生计算团队以 YARN 系统的需求为出发点,将 YARN 底层调度和单机运行时能力委托给了 K8s,但是用户还可以继续直接使用原来的 YARN 接口,这也相当于重写了 YARN。重写之后,YARN 作为一个中间的调度状态,底层资源管理统一是 K8s,上层调度也统一是 K8s。另外,单集群的规模越大的话,资源的利用效率越高,因此字节团队也对 K8s 进行了很多优化,比如对 API server、ETCD 做了很多深度定制,现在单集群管理规模达到了 2 万台机器。

重写工作进行了差不多两年,调度系统融合后,在 Kubernetes 集群的基础上增加三个组件:

  • Yodel:模拟实现 YARN 的 ResourceManager,支持 YARN API 及其 AM 管理、Quota 管理、权限管理等功能。
  • Unified Scheduler:高性能调度器,取代 Kubernetes 原生调度器,提供了强大的多租户资源隔离能力,以及更丰富的调度策略。
  • BigData Plugin:单机大数据插件,用于辅助 Kubelet 完成大数据作业的 Localization、Shuffle 等工作。

在离线业务都统一使用同一个融合集群。具有多租户资源隔离和管控的 Unified Scheudler 统一对集群中所有 Pod 进行调度,统一管控了在离线资源的动态划分。

在线服务按照原有接口,提交到 API Server;离线作业按照 YARN 接口,提交到 Yodel,无需任何改动。Yodel 具有和 YARN ResourceManager 一样的功能,并且可以把 YARN Resource Request 转换成 Kubernetes Pod,再转换成 YARN Container。

在单机上,所有 Pod 统一由 Kubelet 启动和管理。原来 YARN NodeManager 具有的大数据特有功能移植到 BigData Plugin,辅助 Kubelet 完成,比如为大数据作业提前下载 Jar 包,这个过程又称为 Localization。

统一的资源池使得资源占用成本更加透明化,可以清晰看到各个业务线在资源侧的投入情况,不同的级别采用不同的资源保证策略,所以离线和在线任务不会因为“超额分配”产生冲突。不同业务线之间协调资源也变得更为容易,业务之间的互补性,也带来了一些资源优化方面的收益,从而达到降本增效的最终目的。

李亚坤说,这套融合系统最开始是为了解决“除夕”的问题。在互联网行业,每年除夕以及经历社会热点事件的时候,在线资源的需求会变得非常大。为了应对这些特殊时期带来的压力,以前字节跳动还常用将离线任务的机器全部清空,交给在线使用的原始方法,后来很自然地开始考虑从软件上实现 YARN 和 K8s 的混部,在平时就可以达到分钟级别出让百万核的效果。从去年开始,他们就开始使用这套新的系统,在除夕当天为在线微服务出让了 400 万核的资源进行有序扩容,让大家平滑顺利的度过了春节。未来方向上,跨机房资源治理,包括跨机房容灾管控,将会是接下来一个比较重要的发力点。

后 Hadoop 时代的云原生计算平台

以 Hadoop 为中心的大数据生态系统一直以来都是大部分公司构建大数据平台的选择。而随着容器技术的发展,传统大数据平台显现出来的问题也越来越多,大数据平台的云原生化已是大势所趋。

2021 年 3 月, Spark 正式宣布支持 K8s,2021 年 5 月,Apache Kafka 背后的商业公司 Confluent 也发布了 Confluent on Kuberneters。按照这个趋势发展的话,新的大数据组件更多可能会以云原生的方式发布,而 Hadoop 极大可能会被云原生存储 资源调度取代。

一方面是必须朝着云原生的方向演进,另外一方面很多企业也有自己的大数据集群,但在数据分析的能力上基本也是割裂的,在离线大数据集群达到几百、上千的时候,K8s 本身的支持能力还是不太够。这也给很多企业带来了业务迁移进退两难的境地,在生产环境中进行迁移或二次开发,这个工作对技术人员要求非常高。这种情况下,也许字节跳动的这套从存储到数据服务的闭环系统的实现方法能给大家带来启示。

在打造这套系统的同时,字节跳动调动了计算引擎、存储、中间件、数据服务等专门的研发团队,逐渐形成了一支云原生大数据团队。这支大数据团队逐渐发展,在火山引擎上对外输出大数据能力,提供 ToB 服务。在后 Hadoop 时代里,特别是针对一些金融行业专有云场景或已有 K8s 集群的企业,火山引擎云原生计算平台提供了从数据接入 BMQ / Kafka 到数据 ETL( 实时 Flink,批 Spark) 再到数据存储和加速 CloudFS 和日志搜索等一站式服务,以及 OpenStudio 管控(多租户隔离、访问控制、计量计费等)和 OpenOps(服务生命周期管理、安装部署、容灾高可用)运维交付平台。与业界已有的方案相对比,火山引擎这套解决方案可以对标 CDH,不过 CDH 基于 YARN 作为资源调度,火山引擎云原生计算平台基于 K8s 和字节跳动离线资源的混合调度能力做了更深一步、超前的优化和迭代。

对于 ToB 的服务如何能够紧跟业务和技术的发展,李亚坤表示,云原生计算团队提供的产品在核心引擎能力上都是内外复用的,这些引擎也在负责支撑字节跳动集团内部的大数据计算、数据存储、中间件,与外部客户的需求形成相互支持的形态。让团队在负责 ToB 的事情的同时也跟内部生态整合,更有助于了解业务和技术的发展,不会导致分叉的情况发生。

嘉宾简介:

李亚坤,火山引擎云原生计算技术负责人。硕士毕业于哈工大计算机专业,9 年大数据和云原生领域从业经验。2017 年加入字节跳动,构建了字节跳动千万级核心的集群资源管理和调度系统,支撑了全公司的数据平台、搜索、广告、推荐算法等中台,以及抖音、今日头条、西瓜视频、懂车帝、飞书等众多业务的大数据分析需求,带领团队完成了在离线资源混部、Hadoop 上云等众多项目。目前整体负责火山引擎云原生计算的技术团队。

0 人点赞