这几年数据圈里真正在被反复验证的路线,就是“云原生 + 大数据”,而 Kubernetes 基本成了绕不开的底座。我前后花了小一年时间,把原来跑在裸机 Hadoop YARN 上的一堆 Spark、Flink、Hive 任务,慢慢迁到一套基于 K8s 的弹性数据处理平台上。这里不谈概念包装,只说真实落地的过程、拆解思路和踩坑记录,适合正在做大数据平台容器化、想把资源利用率提上去、又找不到“从哪下手”的团队参考。
这套方案最终解决的核心问题其实很朴素:计算资源不该再跟着固定几台机器走,而是跟着任务走。任务来了,Pod 秒级拉起,算完自动释放;深夜批处理高峰和白天实时流量高峰错开,靠队列策略承接;存储和计算彻底拆开,对象存储服务长期数据。整篇内容会按照“为什么这么设计、组件如何选型、具体怎么一步步搭、线上遇到什么鬼问题、成本和后续怎么控制”来展开。
1. 为什么我把大数据任务搬到 Kubernetes:方案思路与取舍
1.1 核心痛点:固定集群带来的资源诅咒
以前用 YARN 管理大数据任务,集群规模是“预配”出来的。你得预估半年后的业务峰值,然后为了那个峰值把几十台机器常驻在机房里。白天跑实时任务和即席查询,CPU 可能只用 30%;凌晨批量任务全开的时候,队列又开始互相抢资源。加机器要审批走采购流程,减机器又怕下次峰值来了撑不住,管理员每天就是做配额拉扯的裁判。
这样的固定资源池最大的问题还不是钱,是响应速度。业务的探索性分析和临时数据任务没有固定节奏,今天数据团队想跑一个半小时的 SparkETL,资源不够就得排队,等前面的大任务把队列释放,黄花菜都凉了。如果是在 Kubernetes 上跑,情况完全不同:任务描述从“提交到队列中的 application”变成了一组“Pod 模板”,调度器按真实请求分配资源,任务结束了 Pod 立刻被回收。机器不够时集群自动扩容,空闲时缩容到最小规模。
我说的资源利用率提升,不仅仅是 CPU 曲线好看,而是团队可以真正把硬件资源当成一个动态池子。同一个 Kubernetes 集群里,白天跑 Web 服务的 Pod、夜间跑数据清洗的 Spark Executor,只要通过节点亲和性和优先级划分好命名空间,就能共享同一批机器。这种混部模式比“大数据一套集群、业务一套集群”的物理隔离要省一半成本。
1.2 选择 Kubernetes 而非自研调度系统
去大数据化这个方向,行业内反复摇摆过。有人提出干脆把计算层直接让 Spark 自己调度,也有人想用自研的 Mesos 或者单纯的 Docker Swarm。但折腾一圈后,Kubernetes 的胜出不是因为它技术最前沿,而是因为它生态完整、API 统一、操作习惯深入人心。
就 Spark on K8s 而言,作业提交后请求的是 Kubernetes API,Pod 调度、节点故障转移、日志采集这些都是 K8s 生态现成的能力。相比自己在 YARN 上定制调度策略,K8s 的声明式 API 让大任务和普通容器任务可以接受同一套身份认证、网络策略、监控体检。团队里不用养一批只懂“大数据调度”的人,运维手里已有的 K8s 经验能无缝迁移过来。
选型另一个关键点是“存算分离已经不可逆转”。云原生时代如果继续坚持 HDFS 存储与计算节点捆绑,计算资源就得跟着存储副本的分布去调度,天然别扭。而对象存储和各类云盘服务已经成熟,把 HDFS 当“数据底座”反而成了成本大头。Kubernetes 的工作负载天然以无状态为假设,计算层容器随时可死、可重建,数据放在远端的对象存储里才合理。架构上想明白这一点,后面的组件选型几乎不纠结。
1.3 什么情况下不建议盲目上这套架构
让我泼一盆冷水。如果你们的业务量很小,每天跑几十个任务,集群规模五台以内,直接维持现有 YARN 甚至单机调度都不亏。因为迁到 K8s 在初期需要付出一笔不小的学习成本和基建改造费用:镜像构建、RBAC 权限、网络策略、持久卷,这些都不是白的。
另外,如果团队没有足够强的容器化运维经验,强上 K8s 会把问题放大。大数据任务比普通微服务更挑剔——镜像动辄几个 GB,Executor 的数量可能瞬间几十上百,Pod 频繁调度会暴露 DNS 性能问题和节点端口不足的资源管理问题。一个经验不足的团队很容易在“从机房到容器”的路上被一堆琐碎问题拖下水,造成排障成本上升。
我的观点是:这套方案最适合数据量中等偏上、任务峰谷明显、团队已经想清楚了“长期追求弹性”的技术路线。如果只是开会听到“云原生”这个词,想跟风改造,建议先拿一两个非核心 Spark 任务试运行半年,再决定是否全面切。技术选型是为了解决问题,不是为了追逐热搜。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 组件与架构拆解:Spark、Flink、存储与调度四张王牌
2.1 Spark on K8s 的两种姿势:Operator 和原生模式怎么选
Spark 跑在 K8s 上有两条常见路线。一条是直接用 Apache Spark 原生提供的 spark-submit --master k8s://https://...,另一条是用 Google 开源的 Spark Operator 来管理 SparkApplication 这一类自定义资源。
原生模式适合跑批任务相对固定、开发栈还停留在脚本提交方式的团队,你基本只需改一行提交命令和配置,就能把任务塞进 K8s。然而它有一个不便之处:任务的生命周期没有和 K8s 资源风格统一,作业结束后要写额外的 hook,失败后要自己处理重试。Operator 模式等于把 Spark 任务视作 K8s 里的一等公民,你在 YAML 里声明跑几个 Driver、几个 Executor、镜像地址是什么,Kubernetes 会帮你盯状态和重试。
我在生产中选的是 Operator 模式。理由有几个:重试逻辑可以内置,GitOps 友好,代码仓库里存的是描述任务状态的 YAML,而不是一段只能自己在终端跑的脚本。而且每次任务执行的全程状态(Submission、Running、Succeeding、Failing)都会作为自定义资源状态暴露给上层
