导读
随着云计算和容器化技术的快速发展,越来越多的大数据团队开始将传统的集群架构迁移到 Kubernetes 环境中。Kubernetes 作为一种灵活、弹性强的容器编排平台,凭借其高效的资源调度能力和自动化管理功能,成为了现代企业在云原生架构中不可或缺的组件。然而,在大数据应用场景中,尤其是在离线计算和大规模任务调度的需求下,Kubernetes 是否能够完美适配 Spark 等分布式计算框架,仍然是业界关注的热点问题。本文将围绕大数据团队如何在云上实现弹性架构的转型,探讨为何选择避免直接使用 Spark on Kubernetes 模式,而是依托现有的资源管理架构进行迁移。通过分享团队在迁移过程中遇到的挑战与经验,本文旨在阐明在追求高效资源管理与稳定性之间所做出的技术选择与权衡,尤其是在面对高吞吐量的任务调度和多租户管理时,Kubernetes 的适用性和局限性。
1.
背景介绍
2.
oppo 大数据公有云原生架构设计
3.
极致弹性的挑战与解决方案
4.
未来展望
5.
问答环节
分享嘉宾|
闾泽军
OPPO
高级数据平台工程师
编辑整理|
陈思永
内容校对|李瑶
出品社区|
DataFun
背景介绍
在当前大数据领域,我们面临着两种主要的环境:一种是国内 IDC 自建环境,另一种是海外云环境,主要使用 EMR 服务。
1.
国内 IDC 自建环境
在国内的 IDC 自建环境中,我们构建了完整的大数据技术栈。我们首先从任务调度、业务统一接入层、计算引擎、资源调度、到底层的 HDFS 存储等方面入手,通过逻辑架构来构建这一技术体系。
我们还建设了一个完整的运维系统,涵盖集群管理、数据服务、生命周期管理、成本账单和任务诊断等多个功能。在过去几年里,我们对每个模块进行了大量优化,以提升系统性能并降低运维复杂度。
然而,自建环境有其固有的局限性。IDC 自建的机器供应量是固定的,因此,在资源调度和任务调度方面,虽然我们进行了很多优化,使得资源的利用率有所提升,但从运营角度看,成本控制依然存在问题。由于机器成本的固定性,我们无法实现显著的成本下降。
2.
海外 EMR 环境
在海外,我们主要使用 EMR 服务,运行在弹性计算环境下。与 IDC 自建环境不同,EMR 提供了弹性资源管理的能力,使得我们能够更灵活地扩展计算能力。然而,EMR 的架构虽然提供了更高的弹性和资源管理的灵活性,但仍然存在一些挑战。
尽管 EMR 提供了基于 EC2 的弹性计算资源,支持自动伸缩,但它依赖于云厂商提供的基础设施和技术框架,如 EC2 实例的管理、存储和计算引擎(例如 Spark)的优化等,这些方面的调整受限于云厂商的技术策略和伸缩效率。例如,EMR 的资源调度和伸缩策略有限,我们无法像 IDC 自建那样对其进行高度定制化的优化,这导致了部分资源利用效率不高。
此外,EMR 的弹性伸缩特性虽然有助于提升资源的灵活性,但在实际操作中也面临一些挑战,特别是在节点的伸缩过程中,可能会带来作业重启和重跑的问题。因此,我们采用了保守的伸缩策略,尽量避免频繁的节点回收,以确保计算任务的稳定性和可靠性。
为了降低成本,我们在 EMR 环境中尽可能使用 Spot 实例,以期降低计算资源的费用。然而,由于 EMR 环境中的计算节点是有状态的,伸缩效率受到一定限制,我们只能在一定范围内调整资源,而无法进行大幅度的弹性伸缩。
oppo
大数据公有云原生架构
1.
向公有云原生架构转型
面对 IDC 自建和 EMR 环境的种种挑战,我们开始探索将大数据架构迁移到公有云原生架构上,特别是通过 EKS(Elastic Kubernetes Service)等云原生平台来进行优化。过去一年中,我们已经在海外进行了一些试水工作,并且完成了整个架构的升级。这一转型的目标是提升资源的弹性和灵活性,同时降低成本,提高效率。
-
多云迁移能力
:我们可以通过云原生标准环境,将 IDC 自建环境中的各类组件迁移到公有云,形成统一的接入和访问层。这种标准化的迁移方式有助于提升系统的灵活性和可扩展性。
-
更高的灵活性和可控性
:与 EMR 相比,云原生架构能够提供更多的定制化和灵活性,尤其是在资源调度、引擎优化等方面。我们可以更精确地控制资源的使用,并进行针对性的优化。
-
更低的费用
:与 EMR 相比,EKS 等云原生平台通常具有更低的服务费用,这使得我们能够降低云资源的使用成本,尤其是在日常的资源调度和计算引擎使用上。
尽管云原生技术有其技术先进性,尤其是 Kubernetes(k8s)等技术的应用,但我们在实施时考虑了如何平滑迁移,避免对现有业务的影响。迁移过程中,我们特别关注以下几点:
-
业务层的无感迁移
:我们要确保在迁移过程中,尽可能不影响业务的稳定性,让用户感知不到架构的变化。
-
现有路径依赖的保持
:在迁移时,我们考虑到现有的运维系统、统一接入层等已建立的服务和路径依赖,尽量保持一致性,避免大规模的重构。
-
高效伸缩与资源供应的优化
:我们将重点优化资源供应,使得资源既不过剩,也不短缺,能够在需要时进行快速伸缩,以适应业务需求的波动。
2.
云原生架构的关键设计原则
-
使用 Kubernetes 作为计算服务的基座
:我们将 EMR 中的资源调度替换为 Kubernetes 集群管理,利用 Kubernetes 的弹性伸缩能力,使得计算服务能够按需扩展,并确保计算节点无状态化,从而提高系统的稳定性和伸缩性。
-
多云迁移的优势
:Kubernetes 的标准化架构使得我们能够方便地将工作负载迁移到不同的云平台上,例如 EKS、Google Kubernetes Engine(GKE)或阿里的 ACK,提供更多的选择和灵活性。
-
统一接入层
:我们通过统一的计算层和接入层来实现底层资源供应的无感切换,确保在不同的计算环境中,业务层的操作保持一致。
-
存算分离的架构
:在云原生架构下,我们采用存算分离的模式,计算和存储分离处理,这能够提升计算资源的伸缩性和存储成本的控制,同时避免计算节点的资源浪费。
通过这一系列设计和优化,我们的目标是构建一个高效、弹性且具备较低成本的大数据平台,在云原生环境中实现资源的自动调度、动态伸缩、成本优化,并最终提升整体的系统性能和可维护性。
3.
大数据组件的标准化部署与统一接入
-
统一代理服务接入
:在大数据架构中,多个组件(如计算引擎、存储系统等)通常会使用不同的接口和协议进行交互。为了简化部署与维护过程,可以通过使用一个统一的代理服务来接入所有外部请求,而不是直接暴露每个组件的接口。这种方法的好处是提高了安全性、简化了管理,并能统一控制流量。
-
资源调度和计算引擎接入
:例如,通过与 Apache Livy、Spark Server 等计算引擎的接入,实现了计算任务的分发和管理。这些计算引擎负责处理复杂的计算任务,而资源调度组件负责调度这些任务所需的计算资源。通过将这些组件容器化,并部署在 Kubernetes 上,能够提高弹性和可扩展性。
-
Kubernetes
与资源调度
:Kubernetes 作为一种容器管理平台,在资源调度方面提供了强大的功能。通过与 AWS EC2 等弹性计算资源的结合,Kubernetes 可以自动扩展资源池,以应对负载变化。在大数据集群中,通过 Kubernetes 的 Pod 管理和调度,可以更灵活地管理计算节点,并确保计算任务的高效运行。
极致弹性的挑战与解决方案
1.
弹性伸缩的挑战与实践
-
资源利用率问题
:在传统的云服务平台(如 EMR)上,伸缩器通常会根据队列中任务的 Pending 状态来决定是否扩容。然而,这种方式可能会出现问题。当任务队列中存在大量待处理任务时,伸缩器可能会过度扩容,而实际的资源需求并未完全达到预期。例如,在某个时间点,系统可能会扩容很多机器,但如果这些资源并未被实际利用,就会导致计算资源的浪费。
-
动态伸缩的需求
:为了应对这种资源浪费问题,需要一个更加智能的伸缩器,能够基于实时的资源使用情况来判断是否需要扩容或缩容,而不仅仅依赖于任务的 Pending 状态。这种伸缩机制的核心是对资源使用的动态监控,并结合任务的实际需求来进行调整。通过智能化的资源预测和调度,伸缩器可以在不浪费资源的情况下及时提供所需的计算能力,即扩的准,缩的快。
综上,这里凸显了多租户在弹性环境下的矛盾,一方面我们仍期望做一些限制,另一方面又期望弹出的资源被充分利用。因此我们需要一个从集群、队列、不同类型负载的资源需求进行精准建模。
3.
多种伸缩管控模式
-
自定义的弹性伸缩器
:为了能够根据实际需求实现精准的资源调度,采用了自定义的弹性伸缩器,基于 Kubernetes Operator 模式。这种方式可以通过定义一组自定义资源定义(CRD)来扩展 Kubernetes 的功能,使得调度系统能够更加灵活地根据负载情况来自动进行资源扩展或回收。
-
负载建模与趋势分析
:传统的伸缩系统通常是基于静态的资源请求量来进行伸缩,而这种方法并不能充分反映集群负载的变化趋势。我们增加了对负载变化的趋势建模,例如,当 Pending 任务数量持续增加时,意味着集群可能存在资源瓶颈,这时需要尽快扩容;而当 Pending 任务数量开始减少时,系统可以启动缩容机制,释放不再需要的资源。
-
时间周期性的伸缩
:除了动态伸缩外,系统还可以根据业务的周期性变化(如日常高峰期或季节性需求)来进行定时扩容或缩容。这种策略可以解决突发需求和周期性负载的问题,也便于运维人员快速干预。
4.
负载分类精细化资源管理
-
负载分类
:根据任务类型和资源需求的不同,采用不同的资源管理策略。比如,在 Spark 应用中,Driver 节点和 Executor 节点的资源需求和容错要求是不同的。Driver 节点对资源的稳定性要求较高,因为如果其所在的节点被缩容,整个任务可能会失败,导致需要重跑。相比之下,Executor 节点的任务容错性较强,因此它可以在某些节点被回收时继续运行其他任务。因此,不同的负载我们会给予不同的机型供应以及伸缩策略。
-
多集群统一管控
:云上多 az 的资源供应能力并不完全一直,我们针对不同 az 建设多个逻辑计算集群,在整个弹性伸缩过程中,伸缩器可精确管理不同 az 的资源供应,接入层会实时调配流量,以确保整体资源的稳定供应。
5.
伸缩极致的控制
-
分类管理和定制伸缩速率
:弹性伸缩不只是简单的基于负载来增加或减少资源,而是根据任务类型进行分类,并且根据任务的特点来定制伸缩速率。例如,对于
Driver
节点和普通
Container
,可以选择不同的资源策略:对
Driver
节点使用更稳定的资源,避免因负载高导致的扩容过度;而对于普通
Container
,则可以使用更便宜、弹性更强的
Spot Instance
资源。
-
趋势建模与精准伸缩
:传统的弹性伸缩可能会在任务负载激增时做出过度反应,导致资源浪费。相比之下,通过对增长趋势进行建模,可以更精准地进行资源扩展和缩减,确保集群资源始终与负载保持紧密同步,避免过度扩容或资源不足。
-
在云原生环境下,实现了
存算分离
(Storage and Compute Separation),这意味着计算节点在执行任务时不会存储数据,因此计算节点可以随时被回收。通过这一设计,节点可以迅速进行扩容和缩容,不会因为节点的回收而丢失数据,极大提升了集群的弹性。
-
定向无损缩容
:在 Kubernetes 原生的缩容策略中,Pod 的缩容顺序通常是根据其创建时间或负载来决定的,而忽略了 Pod 是否包含关键数据。通过引入
Delete Cost
等机制,Yarn 集群在缩容期间排空的节点精准对应到 k8s 节点,并进行定向标记优先缩容的,从而确保最小化业务损失,提升系统稳定性。
6.
机器资源优化
-
资源使用效率
:在云平台中,资源调度的效率直接影响了系统的整体性能。在资源使用率方面,目标是保持在 60%-70% 的高效利用率,这种“超卖”模式有助于降低资源浪费。
-
硬件架构优化(ARM 与x86 双架构)
:为了降低成本,并提升系统的灵活性,系统支持
ARM架构
和
x86 架构
双架构的无缝切换。ARM 架构在服务器领域逐渐占据一定市场份额,性价比更高,我们也基本全系采用 arm 资源。
-
任务的分类与分级管理
:任务根据其计算负载、资源需求以及执行模式进行分类,从而分配到不同的集群进行处理。例如,对于轻量级、短时间任务,可以单独配置到一个集群;而对于需要大规模计算资源的任务,则配置到另一个资源更为丰富的集群中进行处理。通过这种方式,不同类型的任务可以得到更加合适的资源配置,提升资源使用效率。
-
机型优选
:根据任务的具体需求,选择与其资源配置匹配的机器类型,避免在单一维度(如 CPU 或内存)上出现资源浪费。通过优化机器的配置比例,可以确保集群资源的高效使用。
7.
实时成本监控与异常检测
-
成本监控
:通过弹性伸缩管理器实现对云资源的实时监控,跟踪从底层计算节点到上层业务线的各项指标,进行
分钟级的成本监控
,而之前我们使用的 EMR 需要 T+1 的观测周期
,
这样可以及时发现资源使用不均衡的情况,从而进行优化操作。
-
异常检测
:当集群中的某些节点出现异常,如负载过高、节点掉线或网络问题时,系统能够自动检测并替换故障节点。通过实时的自检机制,能够最大程度地保证集群的稳定性和可用性。
8. Shuttle
弹性 worker
-
Shuttle
服务的优化
:在分布式计算中,Shuffle 操作(如 Spark 中的 shuttle 阶段)可能会导致大量的网络带宽占用和数据碎片问题。为了优化这部分性能,团队将 shuffle 操作迁移到远端
shuffle service
。这样不仅能够减少网络连接的数量,还能有效避免数据碎片问题。但在迁移过程中,也面临了一些挑战,特别是在任务规模突发增长时,原先的系统设计可能会遇到瓶颈。
-
状态服务的伸缩
:针对某些状态较为固定的服务(如 Shuttle 任务的 Worker 节点),在弹性扩容时,会面临存量任务和新增资源接入的问题。为了避免在扩容时出现数据丢失或不一致的情况,系统需要通过
分裂任务
的方式,将新的工作负载分配给新加入的节点,同时尽量减少对现有任务的干扰。这种分裂在一定程度上弱化了 RSS 的能力,但是我们获得了更强的灵活性和应对突发的容灾能力。
-
任务调度的挑战
:传统的任务调度系统(如 Airflow)面临的一个问题是,大多数资源需求并不高,而任务调度所需要的节点资源通常是固定的。这导致在大多数情况下,调度系统的资源负载较低,而在突发需求时,可能会出现资源不够用的情况。因此,如何将这些调度任务也做成
弹性 Worker
,根据负载变化动态扩容,是优化的一大方向。
9. OFlow
弹性 worker
未来展望
尽管目前 Kubernetes 提供了强大的资源管理能力,但依然有一些挑战和提升空间,尤其是在大数据和高性能计算场景中。未来的优化方向包括以下几个方面:
-
目前,集群资源选择仍有一定的人工干预。
未来可以通过自动化选型算法,根据任务的计算需求和云资源的价格变化,动态调整资源的类型和数量。
例如,实时获取实例的价格、
CPU
、内存和存储等需求,来自动选择最佳配置。
-
对于多租户系统,资源隔离不仅要考虑计算资源,还要包括网络、存储等多方面的隔离。
未来可以通过更精细化的安全策略和网络隔离技术,确保不同业务线的任务在相同集群中运行时不会互相影响。
问答环节
Q1
:为什么不直接使用 Spark on Kubernetes(K8s)模式?
A1:
稳定性考虑:
在迁移到 Kubernetes 环境时,团队更关注生产环境的稳定性,而不是直接选择一套新的技术架构。考虑到从 EMR 环境迁移到原生 Kubernetes 架构时,尽量希望做到无感迁移,避免大规模的技术重构
Kubernetes
的设计初衷:
Kubernetes 起初是为了处理有状态的在线服务而设计的,而 Spark 的离线任务调度通常涉及高吞吐量,这对 Kubernetes 的调度能力提出了较高的要求,可能带来调度开销的问题。因此,团队在考虑生产环境稳定性时,选择了不完全依赖于 Kubernetes 进行 Spark 调度。
A2:
多租户管理的挑战:
在 Kubernetes 环境中进行多租户管理时,Spark 的多租户管理并不是很理想。如果要在 Kubernetes 中做到高效的多租户管理,可能需要借助一些第三方调度器,比如 Volcano,Koordinator 等具备更好的 Quota 管理能力,这也带来了技术上的复杂性和稳定性顾虑。
-
选择自定义模式:
为了确保稳定性和迁移的平滑性,团队选择了基于现有架构(例如亚克巴斯)的解决方案,这样可以避免过多的新技术引入,同时在 Kubernetes 环境中实现弹性调度能力的增强,提供定制化的资源管理和多云迁移能力。
Q3
:Kubernetes 在 Spark 中的状态恢复问题
A3:
Pod 的状态恢复问题:
因为 Spark 任务会在 Kubernetes 集群中以 Pod 形式运行,如果某个 Pod 挂掉后,如何恢复其任务状态是一大挑战。虽然 Kubernetes 支持一定程度的 Pod 状态管理,但 Spark 任务的状态恢复在短期内会面临较高的技术难度,因此团队选择暂时不完全依赖于 Kubernetes 实现 Spark 的调度。
Q4
:关于 Spark Operator 和 Kubernetes 的选择
A4:
为什么不直接使用 Spark Operator?
因为在当前环境下,团队并不希望完全依赖新的 Spark on Kubernetes(K8s)模式,特别是在稳定性和技术迁移上,Spark Operator 的引入可能带来更多的技术不确定性。
Q5
:Kubernetes 作为大数据框架的优势与挑战
A5:
弹性能力与性能问题:
在大数据业务上,Kubernetes 提供了极好的弹性调度能力,但随着规模的扩大,Kubernetes 的调度能力和性能仍然存在一定的挑战。不同公司在大数据架构中选择不同的路径:一些公司直接选择 Kubernetes,另一些则选择在 Kubernetes 上做一层适配或优化,每种选择都有其优缺点。