字节跳动云原生技术发展历程解析

发表时间: 2022-09-21 12:56

以 Kubernetes 为代表的云原生技术底座支撑了字节跳动业务的快速发展。从微服务场景开始,Kubernetes 逐渐演化统一支撑了字节内部的大数据、机器学习以及存储服务等多种形态基础设施。

1. 字节跳动云原生历程

1.1 技术体系概览

从技术体系的底层逻辑上来看,字节跳动采用的是一套清晰的分层技术体系。一些常见的前台业务,比如今日头条、抖音、西瓜视频等都建立在一系列共享的技术中台和基础设施服务上。

基础架构必须不断地演化自身的平台服务能力,才能适应业务的快速发展。

举个例子,字节跳动目前有超过 10 万个在线服务,在线集群中有超过一千万的 Pod,这些服务每天都有超过 2 万次的变更。平均来看,字节的业务系统每五天就会更新一遍。为了处理数据报表和机器学习训练,每天有超过 1.5 亿的离线任务数量处理数十 EB 的存储资源。

字节的基础设施面临的是一个规模巨大且持续快速变化的业务场景。

1.2 字节云原生推进历程

在快速变化和规模挑战下,云原生技术,特别是与云原生相关的资源调度技术在字节是如何发展的呢?

  • 2016 年,字节跳动云引擎 TCE(Toutiao Cloud Engine)启动建设。以 Kubernetes 作为底层容器编排引擎,提供快捷高效的应用部署方案;
  • 2018 年:微服务架构升级。完成核心业务微服务迁移,并在 TCE 之上构建服务框架、Mesh、监控告警等基础设施;
  • 2019 年:“推广搜”云原生。把“推广搜”的物理机服务与在线服务进行全面融合,实现统一容器化调度;
  • 2020 年:在离线调度融合、存储云原生。融合资源管理形态,简化供应链选型;优化运维效率,开启数据库、缓存等存储系统的云原生化改造;
  • 2021 年:联邦化多集群演进。从资源多云到应用多云,实现全场景应用编排和资源管理的标准化和统一化。

目前基础架构的重点建设领域是基于联邦化的多集群资源的统一管理和统一调度

1.3 字节云原生发展动机

从研发和资源效率来看:

  • 研发效率上:云原生技术体系的底层资源模型简化了服务部署等方面的运维管理成本,使得业务团队可以更加聚焦于自身核心业务逻辑开发,从而实现快速的业务迭代;
  • 资源效率上:字节大范围地合并资源池,增加资源交互弹性。在大型资源池下,基础设施团队可以集中通过调度等手段去优化资源效率,帮助业务团队获得更低的资源成本。

从研发和资源效率来看:

我们把和云原生相近的技术体系分成了 DevOps、Cloud Native 以及 Serverless 三代。

  • DevOps:更多强调管理和运维的自动化。主流的服务开发模式是以虚拟机作为底层的资源抽象模型,以 Jenkins 之类的一些自动化管理平台来部署单体应用,进而实现运维管理自动化;
  • Cloud Native:以微服务模式为主。在资源方面以容器作为更小、更灵活的资源交付单元,辅以 Kubernetes 等容器编排引擎,来管理服务的部署和运维。开发者的效率得到了更大的释放,极大增加了业务产品自身的迭代效率;
  • Serverless:开发者以函数或者极度简化的微服务代码来表达自身的业务逻辑,以事件作为数据模型来表达服务上下游之间的请求和响应。把容量管理、请求路由和服务治理等运维层面的需求下沉到底层的基础设施来统一支持,服务开发者只需聚焦在自己的业务逻辑上。开发和生产的效率会进一步提升。

这三代技术总体是沿着两个路径在往前推进,分别是产品前向一体化,以及资源规模化。这两种思路从两个角度分别推动着技术体系的演进。

  • 产品前向一体化:这种思路的核心是如何标准化地把业务的计算逻辑、数据管理模型、资源管理等方面的共性需求抽取出来,沉淀到基础设施当中,使得开发者可以用更少、更简洁的代码高效表达自身的需求;
  • 资源规模化:这种思路更多体现在优化上,关注资源池本身的规模化优势,通过大量的并池、资源的混用以及调度等优化手段,实现资源成本降低的目的。

从技术体系迭代来看,字节跳动技术体系往后迭代方向可以总结为下面的主题:

  • 无需管理的基础设施
  • 自动扩展和伸缩
  • 提升开发效率
  • 提升资源效率
  • 按需付费,节省成本

我们希望朝这些主题方向努力,最终形成下一代的 Serverless 基础设施。

2. 资源管理实践

在大量字节业务完成了云原生改造,实现了资源统一托管之后,从全局来看,如何才能够高效地管理并经营好集团资源,这是我们首先面临的问题。要回答好这个问题,需要先解释理想状态下的资源管理模型。

在资源管理的理想状态下,我们给开发者提供的是一个统一的资源入口,在这个入口下,用户可以从统一的资源池获取资源。

面向业务和应用方面,我们希望开发者可以极度灵活地获取所需资源,像获取“自来水”一样获取各种形态的资源。虽然他们自身的资源需求复杂,有各种各样形态和要求,但是都可以做到随用随取、随取随有的状态。

资源管理方面,我们希望给用户呈现的是统一的资源池场 —— 一个充分并池混合的资源池。这个资源池具备全局最优的资源效率,能够统一管理多区域、多计算架构的资源。不同的业务形态和团队之间的资源就可以灵活调配。

但落实到实际资源需求场景,各业务用户对于资源的需求是极度复杂的。

从整体来看,字节内部目前托管的平台租户包含在线服务、机器学习平台、数据平台, FaaS 以及部分的存储业务。这些平台租户的应用模型有很大的差别,包含无状态的应用模型、有状态的模型、批式应用等等。

除了业务场景的复杂需求外,安全、性能以及容灾等方面也会为底层的资源管理带来冲击:

  • 以性能角度为例,不同的业务系统,对于底层的资源算力、计算平台架构都有不同程度的感知力,需要根据不同的业务情况针对性做到最优的性能优化收益;
  • 在容灾和安全隔离方面,需要分割不同的业务线常使业务系统能够在各自的容灾域、安全范围内做到互不影响。在复杂的业务分割诉求下,过度的资源分割不仅会带来资源管理上的复杂度,也会给统一的资源并池以及优化带来障碍。

总体来看,资源统一管理有挑战也会带来可观的收益。在实际执行过程中我们需要适当的结合运营、运维和调度等手段,达到有效的资源管理。

2.1 统一资源管理挑战与收益

这里我们总结了资源统一管理方面的挑战和收益。

挑战:

  • 把不同形态的资源调度应用放在同一个队列、同一个集群中统一管理。比如说常驻服务和批处理任务;
  • 不同应用对底层资源的隔离能力要求不同,如何把强隔离的应用和低资源成本需求的应用放在同一集群、节点中,给单机资源管理以及调度系统带来不小的挑战;
  • 资源统一管理后也给平台性能、安全和价格方面带来了挑战。

收益:

  • 统一的资源池使得资源占用成本更加透明化,可以清晰看到各个业务线在资源侧的投入情况,方便做资源经营层面的分析;
  • 资源的交付弹性增大。不同业务线在统一的资源池上做链路治理优化以及跨业务的资源协调变得极度容易。业务形态之间的互补性,天然也会带来一些资源并池优化方面的收益空间。

2.2 资源统一解决思路

为了解决资源统一管理这个问题,我们提出了三个思路:

1)抽象能够提供的资源售卖模型,方便不同的业务线、业务系统准确地表达自身的需求;

2)创建一套统一的 Quota 管理平台,这个平台可以让开发者们灵活地管理自身的各类资源;

3)资源分层调度系统使得单机集群对字节内部所有计算资源做到快速灵活的交付。

2.2.1 资源模型抽象:QoS & 弹性分级

在资源模型方面,我们给应用提供的资源形态以 CPU 维度为例一共分为三级:

  • 独占核/dedicated_core:以独占的形式去获得物理核,这些 core 上除了应用自身以外不会运行其他租户的进程。我们又细分了 Numa 的拓扑分配以及忽略拓扑结构的两个子类,提供了对微拓扑结构上的优化选项;
  • 共享核/shared_core:把不同的应用的 Pod 运行在一个共享 CPU 的 Pool 上,这样可以同时针对不同应用形态在 CPU 调度域上的划分,更细粒度地隔离开应用之间的影响;
  • 回收核/reclaimed_core:在共享核的基础上,通过混部控制系统的方式去回收部分的低优资源,我们可以低优混部的共享方式去提供算力的供给。

目前字节内部的应用弹性资源交付也是有三类诉求:

  • OnDemand 按需交付:对于应用的实际使用体验是一种比较理想的状态,属于用户随要随有的模式。但是资源管理方面,很容易引发大批量的资源闲置问题,字节目前主要在函数类的场景下小规模使用;
  • Reserved 资源预留交付:字节主流的资源管理形态,对单一服务、资源的队列都能够做到资源总量上的保障;
  • Spot 竞价交付:目前处于数据中心中高增长的资源形态,主要通过弹性扩缩、混部回收得到部分资源,再以竞价的模式给业务方提供使用。弹性的资源交付形态搭配不同的 cores 保障等级,可以支持不同业务场景的资源售卖模型。

2.2.2 分层资源调度

如何有效地把资源诉求准确高效地交付到开发者手上需要一套完整的调度系统来支持。字节内部是通过一套分层的调度系统来实现调度交付的。

单机调度主要是扩展了 Kubernetes 的单机资源管控:资源的微拓扑结构感知和资源的分配策略,主要解决了如何让不同 cores 形态的 Pod 统一运行在一个节点之上。Kubelet 内新增的 QoS Resource Manager 这个组件,主要负责容器的资源管控链路上按照应用的微拓扑亲和性要求给 Pod 分配包括 CPU 内存以及 GPU 网卡等设备,在单机拓扑结构上的信息可以通过 CRD 上报到调度器,以调度器中心抢占或者调度的形式把 Pod 分配到合适的节点上。同时这个组件能够在框架上灵活的扩展。

SysAdvisor 是一套单机层面的策略管控实现的组件,可以持续观察 Pod 在细粒度上面的系统指标如何结合不同业务形态的资源使用模型去做预估,去决策在每一个资源维度上如何给 Pod 分配合适的资源量。

集群中心调度器需要解决的核心问题是如何让不同形态的应用在整个集群里自由地调度。需要满足不同的调度语义细粒度的要求,充分降低集群空置率。在调度性能方面,同时要满足低频次和批式的大吞吐的调度场景。针对各种应用场景提升调度场景的质量也是集群中心调度器需要解决的问题。

下图总结了调度功能层面下不同场景对于调度器的需求:

目前字节使用的调度器是参照 Kubernetes 框架的分布式调度器。这套调度系统主要的中心式组件有 Dispatcher、并行式的 Scheduler 还有中心式的 Binder。

其中,Dispatcher 主要负责把应用以及集群内部的节点资源分配到具体的 Scheduler 上。Scheduler 在主体结构和 Kubernetes 原生的调度器结构类似,主要处理的就是 predicate 和 priority 的调度计算逻辑。Binder 可以解决不同 Scheduler 视角下调度结果的冲突,并且用 SchedulingUnit 替换了原生的 Pod 语义。这样可以更加方便地处理常驻任务中 per Pod 调度以及批式场景下的 per batch 调度。

当集群整体的资源占用水位很满的状态下,乐观并发调度中很容易出现调度冲突的问题。我们的解决思路是在 Scheduler 的每次响应结果里,针对调度申请给出多个候选节点,然后统一送到 Binder 里去解决冲突,这个设计可以降低 Binder 里解决冲突的失败概率。

过去的混部方案是基于 Kubernetes 和 Yarn 的联合系统管控的方案,并在每一个节点上同时运行 Kubernetes 和 YARN 的管控组件。此外还有一个居中的协调组件负责分配两套系统分别可见的资源量。在联合管控的模式下,单机层面每个节点里 agent 占用资源量级不大,但在整个集群里是一个非常可观的资源量。如果能够实现一套统一的链路, 管理不同形态的资源,对于资源优化的架构以及效果都能得到不错的简化和收益。

在新的统一调度架构下,我们的混合部署架构也做了一系列的调整。保留了平台层 Kubernetes 的 API 以及 Yarn 的 Resource manager 两个入口部署应用的能力。底层的系统上是全部收敛到了基于 Kubernetes 的管控系统上,可以实现大数据系统平稳的底层系统的切换工作。

在新的架构上,不论是在线服务还是离线作业的 Pod 都可以通过一个公共的 Kubernetes API 以及统一调度器去安排资源的调度。这样不仅实现了资源分级模型上管控链路的复用途径,有更大的空间考虑在线、离线业务在同一个集群中运行时资源层应该如何分配及协作。

2.2.3 全局调度

考虑到字节跳动的整体规模,单一的集群能力不足以满足管理字节全球数据中心的需求,并且在应用之间的隔离、多区域的容灾以及算力的标准化问题上字节也有更加细粒度的调度要求。

为了解决在一百万节点的规模下处理好全局范围内资源和业务维度之间的匹配问题,字节跳动的全局调度器加入了联邦层,可以实现大颗粒度的应用和资源调度匹配逻辑。

在应用方向上,我们以应用的优先级作为主维度;在资源池方面,我们以机房作为主维度,这两个方面交叉搭配就形成了一个应用整体粒度上可以全局调度的分配空间。应用和资源的维度是很多的,如果我们选择过多的资源维度,全局的资源会被划分成过多的碎片,不利于管理。所以选取主维度需要考虑对于每一个资源池容量的影响不能低于设定的节点数量。

字节现有的全局调动器主要参考社区的 KubeFed V2 的框架。在社区的基础之上,我们也实现了两方面的扩展,一是关于联邦层访问语义的透明化改造。这个改造使得我们上层平台可以使用原生的Kubernetes 资源对象去操作底层资源,从而降低平台接入联邦资源池的成本。二是大面积改造了全局调度,使得多机房的容灾业务线之间的安全隔离,以及多代际标准算力等都可以在全局调度上统一实现。

3. 挑战与未来

目前字节内部各种形态的计算应用都实现了全量的云原生化以及资源层面的统一,整个集团全局的计算资源利用率平均超过 40%。

虽然仍然有提升的空间,但是利用率在可以预见的未来里很快就会达到瓶颈。我们认为下一代的基础设施依然是会沿着产品前向一体化以及资源规模经营两个方向去开展。

3.1 微服务治理

在产品演进方面,以微服务治理为例,字节跳动目前有超过 10 万个微服务来支撑系统建设,而这些微服务之间有着复杂的依赖关系。

上图是从生产环境里抓取的子业务线服务的拓扑结构,这个图里每个节点代表一个具体的微服务,而节点之间的边就代表服务之间的依赖关系。

在服务架构治理方面,因为这些服务都是散落在不同的业务团队和系统里,很难通过一个全局的视角去做架构层面的迭代,就会造成一些老的服务没有办法下线。

业务和业务系统之间的依赖关系也是直接在底层服务之间通过远程调用的方式实现。在跨业务线交互的场景里直接暴露细粒度的服务接口会使得服务架构治理的复杂度变得很大,往往需要牵涉多个部门协同工作才能推进服务的架构演进。

所以在微服务的产品层面,只需一套面向应用的解决方案去管理一个子业务线内所有的服务。

在资源开销方面,业务系统通过网络调用的方式实现互联,会有很多的数据资源花费在网络通信加解密上。随着微服务数量越来越多,这方面的资源开销也会变得越来越大。目前字节内部在推广函数类的平台,越来越多的服务会通过更细粒度的函数形式去表达自身的逻辑,微服务侧就会趋向于更加微小的方向。那么在架构治理以及资源开销方面的挑战会变得更加巨大。

3.2 资源利用率 vs. 资源有效利用率

资源层面在之前都是围绕着集群的底层资源平均利用率去展开优化,通过混合部署、弹性扩缩以及分时复用等多种形式取得了不错的平均利用率的优化效果。但是底层的资源利用率和实际的业务成本之间依然存在很大的代沟。如何更直接地解决应用层面的容量成本问题,是我们下一步需要重点关注的方向。

3.3 第三代基础设施产品迭代

总结来看,下一代基础设施在微服务这个领域有几个重点的的技术方向。

  • 更加极致地简化开发者在实际开发应用的体验,帮助开发者管理服务容量、服务路由以及其治理托管等,特别是以函数化的形式去表达核心的逻辑;
  • 以“应用”为中心的服务管理平台,从单一服务管理过过渡到分布式应用的直接管理;
  • 更细粒度的封装隔离单元,要考虑到极致的代码分发、细粒度的资源封装、更强的运行时控制等;
  • 多层级的调度智能化,更多考虑分布式应用中的调度优化问题。

以上实践经验我们把它总结成了 KubeWharf 开源项目并反馈到社区,感兴趣的同学可以了解更多相关内容。

同时,作为字节跳动旗下的云服务平台,火山引擎抽象了字节跳动的云原生实践思想,已经对外推出了包含上层解决方案和中层基础产品服务的云原生全系产品。

其中,云原生底座包含核心容器服务和镜像仓库两大产品,它们沉淀了字节跳动数年来建设容器平台的经验,除基本应用托管能力外,还提供高稳定、高性能、自运维等能力。

基于应用生命周期拆解,火山引擎也已推出一些包含持续交付等提升敏捷化和统一交付能力的产品,也有服务网格、应用观测、应用韧性这类可以从流量、监控、演练等方面保障业务平稳运行的产品。

相关阅读

字节跳动宣布开源 KubeWharf,一个实践驱动的云原生项目集

字节跳动基于大规模弹性伸缩实现拓扑感知的在离线并池

KubeZoo:字节跳动轻量级多租户开源解决方案

字节跳动云原生微服务多运行时架构实践