字节跳动云原生体系
字节跳动内部云原生技术的使用贯穿组织技术体系各层面,整体如下图所示:
● 研发体系层:包括 CI/CD流水线、可观测平台、研发效能平台、混沌工程平台等;
● 服务平台层:包括云原生框架体系、服务网格、无服务器计算以及边缘计算等;
● 基础设施层:包括容器管理平台、计算存储和网络的 Paas平台;
● SRE 体系:通过 SRE 整体能力的建设把研发体系到基础设施管理流程串联起来;
● 云原生安全:涵盖业务安全、身份安全、网络安全等云原生安全能力。
这些能力的建设主要根据业务需求逐步推进,演进过程如下图所示:
● 2015 - 2017 年:主要业务为今日头条,面对各种新闻客户端应用的激烈竞争,敏捷迭代尽快将产品的功能推向市场十分重要。为了提供快捷高效的应用部署方案,公司在 2016 年启动 TCE 平台的建设。当时TCE专注于服务的生命周期管理,如新建、升级、回滚、高可用、弹性扩展的容器服务,如今TCE 发展成庞大的私有云平台。
● 2018 年:启动了 Service Mesh 的原型开发。当时字节跳动内部多语言版本造成微服务治理框架不一样,既无法做到统一管理,又会有很多重复造轮子的工作。为了统一公司内的工具体系,同时启动了计算 PaaS 和存储 PaaS 的建设,开始统一公司级别的 SRE 体系和监控中心建设。
● 2019 年:公司级服务树实现统一,后续可以基于服务维度出账单,以应用视角管理资源。Service Mesh 经过开发及试用阶段,有了全量推广。云基础视角来看,抖音在 2018 至 2020 年间发展快速,成本不断增加,服务器规模体量越来越大,团队关注重点转向资源利用率的提升,推进在离线混部架构;为应对大规模集群问题,第一代的集群联邦解决方案实施。从 SRE 的视角来看,平台集成了各种 PaaS 能力,包括数据、运维、监控等能力,构建了统一的部署监控、报警治理一体化的工具矩阵;“推广搜”的物理机服务与在线微服务进行全面融合,实现统一容器化调度并达到全量托管。
● 2020 年:由于业务天然依赖边缘渲染,团队强化了边缘计算能力;各种底层软硬件也进行了优化,比如打造智能网卡、自研 DPU、优化 SSD 控制器等;其次,为更好的在离线融合以及解决第一代集群联邦的问题,团队构建了第二代的集群联邦。继续推进在离线混部架构,通过自研的融合调度器丰富了混部调度能力和资源管控,进一步提升资源调度效率,实现了常态化混部。完成数据库、缓存等存储系统云原生化改造。在 SRE 体系上,由于已经有了工具基础,会关注如何更快速定位问题,因此进行了底层的容器监控优化,如利用 EBPF 实现内核级别的监控。同时对容器隔离进行了优化。
● 2021 年:除了针对微服务框架、服务治理、编排调度,监控运维等方面架构的继续优化,最重要的是:云基础产品开始面向 ToB,火山引擎正式推出公有云服务。
截至2021年底,字节跳动已经建设了完善的云原生基础设施:拥有 200 多个生产集群,共计 50 万节点,容器数超过 1000 万;拥有 10 万多在线微服务,平均每日变更数达 2 万次,离线任务数超过 1.4 亿。
字节跳动大规模K8s混合部署实践
字节跳动私有云平台 TCE 的底层使用 K8s 作为编排调度的系统,字节内部几乎所有无状态服务都以容器的形式部署在 TCE 上,无状态服务主要包括各种微服务和算法服务等。随着业务的增长,TCE 上接管的服务数量越来越多,集群规模越来越庞大,导致了资源成本的不断增长,因此必然要关注集群的整体资源利用率的问题。
为了解决资源利用率的问题,首先要对业务的流量特点进行分析。
上图所示,在线业务的一个特点是每天请求量有明显波峰波谷;同时,业务会倾向于申请比实际需求更多的资源以确保服务的稳定性,但这也会导致集群的整体资源利用率特别低,造成大量的资源浪费。
解决思路
在线业务动态超售
针对上述发现,实际做法是实现在线业务的动态超售。动态超售是指动态控制和调整服务的资源申请量以减少冗余资源,服务级别动态超售的目标是在不影响业务 QoS的前提下提升服务的资源利用率。实现方式主要包含:
● 资源控制:通过 SysProbe 组件,收集实例级别的容器资源利用率 metrics 和 Pod 的 meta 信息,并将这些推送到 Spark 里面做聚合分析。之后每次服务上线,业务会通过 TCE Platform 提交一个 DeploymentRequest,包含了业务配置的资源申请,TCE U8S 组件会去查询 SysProbe 提供的 API,根据每个应用的历史数据计算出其实际需求的资源并作出相应的控制。
● 资源调整:集群里很多长期不升级的服务占用了不少资源,而且资源利用率非常低,但是无法通过第一种方式对其调整。因此通过 VPA Controller watch 所有的 deployment,一旦发现有长期没有更新的 deployment,就会主动修改其 request 并进行更新。
● 弹性伸缩:最后结合 POD 的弹性伸缩来回收流量低谷时期的资源,从而大幅提升资源利用率。
在离线业务混部
下一步需要考虑的是如何利用这部分在线业务节省下来的资源。因为所有在线业务的低谷时段几乎都是吻合的,比如在凌晨之后,一般所有在线 APP 的使用频率都会减少,因此不能通过扩容在线业务的方式来消耗这部分资源。但是字节内部存在很多离线任务需要大量的计算资源去进行处理,例如视频转码、模型训练等。这些离线任务对资源的需求与时间无关,天然适合使用在线闲置资源,开启在离线混部架构,能够把在线业务的闲置资源出让给离线业务使用。
字节的大数据业务都是基于 Yarn 体系的,在线应用和一些 AI 应用部署在 K8s 上,最开始的时候我们把 Node manager 做了一些修改之后和 Kubelet 一起部署在了 K8s 的 node 上。总体来说,整个平台是由以 K8s 核心控制面为主的在线编排调度系统,以 Yarn 为主的离线调度系统,以及Sysprobe 组件为中心 Hybrid controller 构成。离线业务混部示意图如下:
在离线业务混部要实现的效果是:在优先满足在线微服务的需求的前提下,把剩余资源尽可能多的供给离线业务使用,当在线业务需要更多资源的时候,可以快速地调回离线侧的资源。
从具体实现角度来说,通过 Sysprobe 系统监控,获取单机层面的各种容器的资源使用情况;通过机器学习算法,推导出该集群上可以出让给离线侧去使用的资源;将这些信息传给 node manager ,动态上报到中心的 RM 进行资源的统一展示。Hybrid controller 主要是负责集群整体的容灾降级策略和水位控制相关的事务。
整个调度系统分为三个层面构建:集群层面、节点层面和内核层面,分别承担了三种资源调度的角色。
● 集群层面:K8s Scheduler 和 Yarn 的 ResourceManager 负责完成集群层面的调度,把容器调度到合适的节点。
● 节点层面:但是当节点层面在线业务发生 QoS 抖动时,需要做出更快的响应,此时分钟级的调度响应延迟通常是不能接受的。因此,系统在节点层面进行处理,提供了 Sysprobe 的 QoS Controller 组件,动态实时地调整节点的实际资源分配。当在线业务发生 QoS 抖动,需要更多资源时,能够快速将离线资源回收回来,实现秒级响应。
● 内核层面:由于Sysprobe是处于用户态的角色,通常也会受到单机层面高负载等异常情况的影响,因此需要在内核级别,比如在 CPU 的调度器,IO 的调度器上做更深度的定制。这样能够实现更强的系统层面的能力,更好地兼顾延迟敏感型的在线微服务和吞吐型离线任务对于计算资源和网络带宽资源的需求,达到整体效能的最大化。
由此,初步的在离线混部完成,但还有一些需要继续优化的内容。比如对于系统而言,资源抽象还不够完整,只有部分类型的离线业务利用了这些不稳定的资源;系统各自独立,在一个 node 上既部署了 Kubelet 负责在线业务,又部署了 Node manager 负责离线任务,资源管理体系分离,上层平台独立建设,底层服务器供给运维分开,导致更大范围的共池复用比较困难。
统一资源池,常态混部
为应对上述挑战,新的融合调度器被开发出来。其能统一管理在离线资源,后续也能支持更多资源的管理和更多类型任务的调度,如下图所示:
首先需要通过 K8s 管理离线应用,这就需要支持 Yarn 的 Gang scheduler以及其他调度算法。因此选择去掉 Yarn 的 Slave 节点相关的管控逻辑,将其 Resource Manager Operator 化;相应的调度逻辑下沉到自研 K8s 调度器 Godel Scheduler,只保留对应的离线作业生命周期管理以及周边功能,进而实现离在线无缝迁移和并池的能力;将公司所有 Yarn 上的存量大数据业务无缝迁移至新的调度平台,来实现在离线资源池的统一。最后,获取了单集群的在离线统一调度能力后,就可以通过集群联邦对多个集群的在离线资源进行统一管理。
联邦化:Global Scheduling 和 Quota
在 2019 年引入了第一代联邦系统,实现了超大规模集群管理,提供了以下能力:
● 用户体验:通过资源池化,SRE 团队不需要管理自己的集群节点,降低维护成本;集群版本升级在 mumber 集群层面实现,用户无感知。
● 自动容灾:集群或者机房故障时,自动实现全量容器的迁移;
● 运维效率:支持集群快速升级换代、上线下线;
● 多云多集群:自建 IDC 和公有云 IaaS 快速接入。
随着更大范围的离线和在线的融合需求,联邦系统演进到了第二代。第二代联邦系统的升级主要体现在:
● 轻量级多集群管理:用户看到的是单集群的视角,又能够保证 member 集群层面的资源复用。实现了原生 Cluster 和 Namespace 范围的资源访问隔离,以及 K8s原生对象和 CRD 对象的创建和访问的隔离。
● 联邦化 Workload:能够实现全场景应用联邦化,一般社区的联邦方案,通常只能接入有限的负载类型;而字节内部实现了各种离线大数据场景,机器学习场景等业务的统一接入,结合全局的资源管控和优化,能够实现进一步的调度;同时,方案融合了离线和在线的容灾体系,进一步实现整个数据中心层面的资源共享和复用。
调度系统终态视图
基于上述的调整和升级,调度系统最终演进成如下图所示的分层架构:
首先,单集群调度层面会运行承载所有的资源,运用统一的调度器、统一的资源管理、统一的隔离能力来实现资源的共享和复用;其次,联邦层实现整个数据中心层面任务的统一编排调度和资源管理;再对接到上层,实现不同的租户隔离能力和接入手段;最后,对接上层的 Paas平台、机器学习平台和大数据平台等不同的业务系统。
服务 QoS 保障
对于大规模的混部集群而言,需要注意的是服务的 QoS 保障,因此监控非常必要。
● 基于 eBPF 内核机制,在 SysProbe 系统中集成了内核监控的能力;
● 在 CPU 和内存层,复用了 Cgroups 的数据,同时扩展增加了 throttle、组线程数和状态,以及实例 Load 等更能反映实例负载真实情况的一些指标;
● 在 BlockIO 层,通过识别 Hook 的 VFS 层关键的函数以及系统的调用,实现了准确识别实例的读写行为;
● 在网络 IO 层,探测服务的 Socket 级别的连接的状态,以及实时的 SLA,比如 SRTT 的抖动。
基于这些监控机制,能够不断发现并且解决混部过程中遇到的影响业务 QoS 的问题,最终实现完善的隔离机制。
上图中左侧流程是发现问题并且解决问题的飞轮模型,右边为实际解决的问题,如 CFS 调参的扩展解决应用 PCT99 毛刺,以及其他内核 bug 等,都是通过这种机制发现并不断优化。
业务效果
在常态场景下,业务效果如下图所示:
资源利用率情况,在线集群和离线集群的资源利用率从原来的平均 23% 提升到 63%,节省 40% 左右的服务器成本;
业务 QPS 指标,即在离线混部对业务 QPS 的影响,整体影响很小。
在保证在线业务资源需求的情况下,离线任务被驱逐的概率很低。
有了稳定的在离线资源调度系统,在需要应急资源的情况下,可以通过系统进行调配。比如2021 年春晚抖音红包雨活动,活动期间需要大量的服务器来支持。但如果仅为支撑春晚活动而去采购过多计算资源,活动结束后肯定会浪费,所以团队除少量采购服务器之外,通过采用离线资源拆借和在线混部出让的方案解决了临时资源问题。
离线资源拆借。字节跳动内部有很多离线任务,例如模型训练等,这些任务在时间上并没有特殊约束。所以春晚活动就对这部分业务所占用的服务器进行了拆借,设置离线出让策略后,这些服务器可以在 5 分钟内转换成在线红包活动的可用状态。
在线资源出让。春晚当天,字节跳动还有大量服务器在支撑其他在线服务。所谓在线混部出让,即在保证其他业务稳定不受影响的前提下,在服务器上插入部分春晚作业。
基于这两种技术方案,在硬件设备有限的情况下,为春晚活动提供了充足的算力。
火山引擎云原生服务
火山引擎云原生服务的产品矩阵示意图如下所示:
2021 年12月,火山引擎发布公有云服务,对外输出字节多年来积累的技术和经验,云原生方向也以PaaS 的形态上线了很多服务。
● 容器服务:包括托管的 K8s 管理平台 VKE、弹性容器平台 VCI,和边缘容器服务以及多云混合云的产品 veStack;
● 敏捷开发:包括持续交付、镜像仓库等产品;
● 服务治理:包括服务网格、混沌工程等产品。
上述产品共同组成了云原生产品基础矩阵,火山引擎云原生服务不仅仅提供了托管应用的能力,也融入了字节跳动在多年的实践过程中沉淀的 K8s 的稳定性,可观测性,服务网格等方面的技术积累和最佳实践,方便用户快速地使用相关技术。
作者:张峰