字节跳动云原生大数据平台运维管理实践

原创
06/28 16:10
阅读数 266
云原生大数据是大数据平台新一代架构和运行形态。随着字节跳动内部业务的快速增长,传统大数据运维平台的劣势开始逐渐暴露,如组件繁多,安装运维复杂,与底层环境过度耦合;对业务方来说缺少开箱即用的日志、监控、告警功能等。在此背景下,我们进行了一系列云原生大数据运维管理实践。通过云原生的方式进行运维管理,最终达到弱化业务方对状态的感知,屏蔽环境的差异,统一不同环境下的使用体验。
作者|字节跳动资深研发工程师-罗来锋
 

业务现状与背景介绍

字节跳动过去几年在支撑自身业务的过程中积累了很多 大数据 领域的引擎工具,目前也在探索将这些引擎 工具的 能力进行标准化、产品化的输出 。在此 过程 中主要有以下几个难点
  • 组件 繁多 大数据 领域完成一项工作需要很多组件配合。比如分布式大数据存储及各种任务执行引擎: Flink Spark 及各种 ETL OLAP 工具和调度 ETL 的任务调度工具,还有支撑工具引擎的运行日志监控系统和项目用户权限的辅助系统等;
  • 部署复杂 :这些系统的 组件 繁多,相互配合也非常复杂,导致部署变得困难。比如部署一套完整的生产环境,可能会涉及到多个依赖和 配置管理 。有强依赖,比如各种任务引擎对底层 大数据 存储的依赖;也有 弱依赖 ,比如任务引擎对日志监控系统的依赖;甚至还有 循环依赖 ,比如消息中间件可能需要采集日志,但日志采集本身又依赖消息中间件, 另外 它们的配置 会形成相互嵌套;
  • 环境耦合 :比如任务执行引擎可能需要嵌套 大数据 存储配置,日志采集可能需要感知每个 组件 的目录以及它的格式等。部署复杂就会造成环境的耦合,因为日常需要维护这些复杂的配置及依赖等,日积月累下就会与这套环境形成了一个深度耦合造成移植困难。
随着近几年 云原生 概念的兴起,我们也尝试将这些工具进行云原生改造来解决以上问题。
 

云原生场景特性

  • 无服务状态感知: 用户可以使用功能而不需要关注背后的运行状态,也不需要关心背后的逻辑;
  • 极致弹性伸缩 用户隐藏运行状态后,在 云原生 场景下的伸缩更为极致,按需使用可以使成本降低显著;
  • 快速故障转移 :当故障发生时 借助极致的弹性伸缩特性,可以快速 下线 故障节点,补充新的正常节点,从而实现 快速故障转移, 并且 这个故障转移对用户 来说也是无感无损的动作
以上这三个特性会相互促进,形成一个良性的循环。

云原生演进方向

对于上述所说的云原生化改造,主要归纳总结了以下几 个大的演进方向:
  • 组件 微服务化 :通过将 整体服务按职责 划分成多个小的组件, 整体架构上 更加 高内聚低耦合 降低整个环境变更复杂度,更加方便大规模合作开发;
  • 应用容器化 :容器提供了可移植性,可以保证环境间的一致性;
  • 基础设施不可变 :通过将所有内容进行封装,从而实现底层基础设施的隔离,进而保证基础设施不可变,可以带来部署的一致性、可靠性和简单性,对环境的状态也更加可控;
  • 声明式 API :通过声明式 API,用户只需要声明自己想要达到的状态,后端服务尽力去满足,使用户无需感 知具体过程 ,整体环境更加稳定,而且功能的变更与演进也会更简单,同时也简化了使用门槛。

架构演进

云原生大数据简介

云原生 大数据 主要是构建在容器上的,这里的容器可以是 公有云 容器服务 ,也可以是 私有云 的容器底座,私有云的容器底座可以是 开源 K8s /基于 K8s 改造的底座,整个云原生大数据可以分为 三大平台和一大支撑体系 ,三大平台分别是调度层、引擎层和平台层。在容器之上是资源调度层,负责统一管理调度整个集群的计算、存储和网络等资源。调度层上面的核心引擎层主要是 字节自研的统一 数据存储系统,兼容 HDFS 语义的同时支持对接标准的 S3 对象存储 。存储层的上一层是 Flink Spark 等各 字节自研或优化的 计算引擎 消息 中间件 、日志搜索及实时分析 引擎等工具 。最上面即是平台服务层,负责将这些引擎能力封装整合成一个对外输出的产品。
本次介绍的运维管理平台支撑了上述的三大平台,提供日常 组件 运维的管理功能,为了更好地适应整个 大数据 云原生 的改造,我们对运维管理 模块 也做了云原生的 改进

云原生上的运维实践

  • 资源占用率低 :运维管理模块 不是面向用户的 产品核心功能,所以它的存在感要足够低,资源占比要足够小,甚至在一些小型场景下要可以被忽略不计;
  • 伸缩性强 :在日常的运维管理中,因为日志监控跟集群的规模是呈正相关的,那么 所有运维管理相关的功能要有跟环境进行快速水平伸缩的能力
  • 稳定性高 :运维管理对稳定性的要求很高,即使在发生故障的情况下,也要能够快速恢复,并且也需要对其他 组件 提供 容灾 管理的能力;
  • 可移植性强 : 运维管理模块的一个大目标就是支持整个 大数据 产品的快速移植,那么就要求它本身就 不能有环境耦合的问题 并且 所有的相关功能都需要 支持 插拔式设计,可以灵活的 在不同环境提供一套完整的运维管理功能
  • 环境感知弱 向上层业务屏蔽 由于 环境差异带来的运维管理的差异性, 保证上层业务 可以用统一的方式使用不同环境 的运维管理功能
所以为了满足以上要求总结了接下来需要关注的几个方向。在环境管理方面需要我们抽象出一套统一的环境模型去适应不同的部署;另外还要有一个灵活便捷的 组件 管理服务统一管理组件 元数据 的依赖、配置等信息;最后还需要拥有功能抽象的能力,比如对常见的日志 监控、告警等功能可以通过抽象统一对上层 业务 屏蔽 环境差异性
 

环境管理与组件服务

环境管理

可以将整个环境按照功能划分成三个逻辑区域,分别是控制面、系统面和数据面,需要注意的是这三块区域只是逻辑区域的划分,并不是物理环境上的隔离。比如在一些场景下控制面可以与系统面进行合并,甚至在一些小型场景下,三个面也可以合并在一个 物理集群 内。
  • 控制面 :用来提供弱业务承载,是全局唯一一个负责环境管控、成本核算及服务网关等支撑性的工作;
  • 系统面 :在每个逻辑单元内 唯一的,但是整个系统中可以有多个逻辑单元,比如在同城多活/异地多活的场景下,每一个逻辑单元都可能对应着一个机房/一个区域,多个逻辑单元间的关系依靠控制面协调;
  • 数据面 :用于提供引擎运行所需的计算、存储、网络等资源,并且在系统面的统一协调下多个数据面可以形成一 个逻辑上的联邦集群
 

组件服务

通过对环境的区域划分对组件进行层级的划分,主要可以分成 系统级、集群级、租户级和项目级。系统级负责大部分的业务管控逻辑;集群级则主要完成日志数据/监控数据 Agent 和内部自研的调度器及 Operator 等的采集工作;租户级主要用于支撑特定大用户独占的组件;最下层的项目级就是用户的作业实例、中间件实例及其他第三方工具等。通过这里的划分就把整个部署划分为了网格形式,使每个组件只需要关注自己所在的网格,很好的 屏蔽了组件与环境信息的耦合。
 

组件服务:Helm 定制化改进

K8s 对单个资源的支持十分友好,对特定领域的操作也十分丰富。但是简单的服务 需要多个资源的配合,比如 Deployment 承载业务逻辑就需要 ConfigMap 去保存它的配置,然后又为了方便地对外暴露服务需要通过 Service 统一访问入口,但是这里的资源协调在 K8s 中并没有提供很好的工具。在 开源 的解决方案中很多开源 组件 基本上都提供了迁移 K8s 的 Helm Chart,但为了更好地融入开源的生态体系,我们也基于 Helm 构建了自己的组件服务。
由于 开源 Helm 命令行工具 并不适用于 云原生 场景下 组件 间的 API 调用,所以我们对开源 Helm 进行了深度服务化定制,在常见的部署、卸载、升级、回滚等需求中通过 API 的方式进行对外暴露,并增加可视化界面,同时还支持了一些深度的仿真部署,让 户可以快速的进行部署、验证、调试等工作,也对层级配置做了精细的划分使组件在部署时可以进行合并覆盖,另外在组件部署时配置了对资源的动态修改,通过以上措施使上层的业务组件可以更加关注在自己的业务领域。
 

磁盘管理

原生的 K8s 对无状态的负载支持是十分友好的,但对有状态的负载 支持就 有点差强人意,主要体现在本地磁盘的使用上,我们分析总结了以下痛点:
  • 环境耦合 :在 K8s 使用本地磁盘时需要提前感知到本地磁盘的挂载点、类型、大小等,会造成一定的耦合现象;
  • 利用率低 :缺少全局统一进行存储调度和管理的 组件 ,导致组件与组件间无法形成高效的 混部 ,使得磁盘整体利用率偏低;
  • 隔离性差 :磁盘以整盘的方式使用会导致利用率低,但如果不以整盘的方式使用,又会使 组件 与组件间缺乏隔离性;
  • 维护难度大 :在业务运行期间由于 组件 对磁盘的动态需求往往需要做 扩缩容 调整,但是提前协调各个使用方的操作使时间跨度大、链路长、维护难度高。

统一调度

为此我们开发了一套统一的 CSI (容器存储接口)来用于管理,不仅能够统一采集 群的 所有 磁盘 信息 也可以进行统一管理。在此基础上我们 将整个磁盘的使用场景分成了三类,分别是共享容量卷、共享磁盘卷和独占磁盘卷
共享容量卷即容量是共享的, 这类场景 对 IO 不敏感,也不需要很强的空间容量的限制,但对于灵活性要求更高,比如典型的 大数据 作业的 临时数据存储、日志等;
共享磁盘卷对 IO 也不是很敏感,但对隔离性、持久化有一定的需求,需要在 出现故障时 能够找回,但是找不回的情况也不会产生灾难性的后果,其中最典型的场景就是缓存;
独占磁盘卷需要高度的 IO 隔离特性,典型的场景如消息中间件 Kafka HDFS 等。

磁盘管理概览

在磁盘 管理 中将其分成两大块区域,第一块区域是 K8s 维护的,比如常用的 EmptyDir,这个部分推荐用来存储配置数据或者少量的临时性数据。
剩下的区域就是上文提到的通过 CSI 进行统一管理,主要细化成了三块区域,分别对应的前面提到 三个存储卷,共享 容量卷 基于简单的本地路径的方式进行支持; 对于 共享磁盘卷会先会把所有的磁盘组装组合成一个 Volume Group ,当业务组件申请共享磁盘卷时可以创建一个逻辑卷使用,从而达到隔离的效果。
独占磁盘卷就是拥有整块磁盘,然后通过 统一的 CSI 抽象成一系列的 Storage Class,上层的业务组件可以根据自己的需求申请对应的存储 如果是 公有云 云盘 或者 中心化存储的场景下,仍然 推荐通过 这套 CSI 给业务提供各类的存储卷,以达到容量管控的同时也可以通过这个 CSI 将磁盘信息与 组件 解耦
 

统一的日志监控告警

日志

日志也是产生可移植性困难较大的一个因素,为此我们也做了统一的日志采集的链路管理,以达到业务隔离、高效采集、公平分配、安全可靠。
对于日志采集目前支持两种方式,一种是 侵入式采集 即提供各种 Collector,主要支持 Java Python 两种方式,由于这种方式具有侵入性,大部分 组件 习惯使用基于文件的采集,因此我们也通过 Filebeat 支持文件方式的采集。采集后汇聚到本集群的日志代理上进行流量管控,后续再汇聚到统一的中心化存储中用统一的 API 支撑日志搜索场景。也对定制化引擎提供了针对性的 API使用户可以根据具体场景使用对应的 API。
第二种是 Filebeat 采集 ,在容器场景下是一个基于文件的采集,和基于物理机的采集不同,主要区别在容器视角,日志存储路径与实际的物理存储路径也是不一样的。为解决这个问题首先通过定制的日志规则 CRD 声明自己的采集规则,然后再通过 组件 部署服务,随着整个组件的创建 、更新 ,Filebeat 的 Discovery 机制可以动态地发现日志规则的 CRD 创建、变更 或删除 通过 Filebeat 加载的机制 成、加载成自己的日志采集规则。
Filebeat 的部署形态中如果能够感知到集群的节点信息并拥有对应的权限,那么就可以部署成 DeamonSet 的方式,使整 资源占比 低,集群是共用一套 Filebeat 进行 数据 采集 。由于在公共云的 容器服务 场景下是感知不到具体的节点信息的,也没有部署 DeamonSet 的权限,所以这里支持通过 Sidecar 的方式注入到具体的一个 Pod 中负责日志 采集 ,通过这种方式我们就 统一 了在容器里基于文件的 日志 采集。

日志数据链路

云原生 的场景下,日志采集远远不只是统一采集链路,而是要用尽可能低的资源消耗支撑日志的高效采集需求。因为云原生场景天然地面向 多租户 ,那么租户与租户间, 组件 与组件间的流量差异会很大,不能因为单个租户不正常的流量对整个日 采集造成扰动。所以我们在每个集群内部的日志 代理中 ,会针对租户做流量管控,当发现大流量异常的时采取 限流 或者 熔断 措施。同时也要保障多租户场景下的公平分配、日志采集的故障转移、云原生场景下 Pod 重建/主动升级等,这几个部分都是后续将主要投入的大方向。
 

告警

告警体系整体是基于 开源 夜莺 改造而成的 ,在开源夜莺的概览中包括存储指标数据的 Prometheus 、存储告警业务数据的数据库及核心 组件 : WebApi 和 Server。 WebApi 用于承担用户的交互,比如规则的增删改查及执行指标查询等。Server 负责加载规则、生成告警事件、发送告警通知等。在开源夜莺中,Server 还承担着 Prometheus 的 PushGateway 职责,字节的产品有自己的用户体系和监控系统,所以对告警方面的定制主要集中在 WebApi 和 Server 上。

流程概览

用户首先通过 WebAPI 生成自己的告警规则,并持久化到数据库中, Server 再加载规则到自己的内存中,通过一 哈希 环决定处理哪些规则并转换成指标查询判断是否有告警事件产生。当有告警事件产生时会调用对应的 控制 模块发送告警通知,将告警事件回填到的数据库中,主要优化体现在以下方面:
  • 首先,我们的产品体系中有统一的用户体系,和运维管理平台一样,并在此基础上增加了用户组 值班表功能,使它更加符合告警领域的使用习惯;
  • 第二, 开源 夜莺 加载日志规则是通过全量的方式,但是会有潜在的性能隐患,我们通过将全量加载 改造 成增量加载,进一步消除相关性能隐患;
  • 第三,告警通知模块,与环境有强耦合关系。因为不同环境的告警通知的差异会很大,比如钉钉、企业微信、 飞书 ,还有短信电话等等。即使是同样的短信告警,不同的环境可能有不同的短信提供商,有不同的对接接口等, 所以我们提供了动态消息模版的能力。

动态消息模板

  • 通过动态消息模板可以引用告警事件的一些信息,进而可以组装出带有丰富上下文 告警信息,使告警系统的灵活性更广,体验感也更好。
  • 在通知方式上设计将其做成一个个插件,用户只需要针对不同的环境开发不同的发送插件就可以了,这种操作也可以使我们的核心流程保持一致。

通知模块

在通知模块中通过 Server 生成告警事件,再由前面的消息模板渲染得到一个真实的告警消息,然后将这个告警消息发送给通知模块,由通知模块结合通知方式及对象生成通知记录并放到 队列 中。为了更好地适应各种环境,这里的队列可以是真实的消息队列,也可以是通过数据库模拟的消息队列。最后由若干个 Worker 并发消费信息调用不同的发送插件发送消息;Worker 之外还有一些定时的 线程 轮询 /巡检整体发送的状态对发送失败的消息进行重试,当重试次数达到一定量的时候生成运维上的告警。
开源 夜莺 系统还有一个比较大的特性是动态阈值,在整体的使用中就是事件发生、触发告警、然后人工反馈到训练分析形成循环 监督学习 的过程,并不断调整动态阈值的生成规则。

监控

在整体的监控架构概览中可以看到上层就是前面所说的数据面,数据面中的每个集群都会有一个Prometheus用来采集、汇聚本物理集群所有组件的监控数据,这里的 Prometheus 本质是一个 Agent,不会承担任何数据存储、查询等职责,最后由 Prometheus 把采集到的监控数据 Remote Write 到系统面的监控系统中。
监控系统用来保存所有的监控数据。为了方便对数据存储进行水平伸缩也做了一层抽象,背后的真正实现可以是公有云上现有的云监控服务、也可以是 S3 的对象存储服务,还可以是我们自研的大数据存储服务,在部分私有云的场景下,也可以对接用户自己自定义的存储服务。在本层的统一存储中通过加一个 Query 服务承担所有监控数据的查询服务,并形成了可视化的大盘展示及前端交互。
对于 查询服务也做了一些针对性的优化。比如监控数据只有新增、没有更新,频繁对某个时间段的监控数据进行反复检索的场景引入了水平拆分的能力,通过将时间跨度大的查询拆分成多个小跨度的子查询并发执行,然后聚合回忆查询速度。另外由于监控数据本身不可变,我们引入了缓存,可以对部分查询出的数据做 一个缓存加上查询场景方便预测。通过以上两个优化相互促进提高了整体的查询效率。
经过整体优化后的监控系统核心优势是可以支持各种环境下的一键指标采集;在性能优化上支持了数据的预聚合、降采样等能力,丰富了整体的功能体系;并且对接了大数据存储,某种程度上可以具备存算分离的特性使得整体系统的水平伸缩能力得到了很大的增强;最后也把监控和其它的运维工具,比如日志、告警、链路追踪等功能做了深度整合,优化了产品的整体使用体验。
 
 
展开阅读全文
加载中
点击引领话题📣 发布并加入讨论🔥
打赏
0 评论
0 收藏
0
分享
返回顶部
顶部