Spark / Flink 跑在 Kubernetes 上真的更香吗?聊聊那些没人提前告诉你的性能坑
Spark / Flink 跑在 Kubernetes 上真的更香吗?聊聊那些没人提前告诉你的性能坑
作者 | Echo_Wish
这两年,大数据圈有个非常明显的趋势:Kubernetes 正在逐渐成为新的大数据平台底座。
以前我们跑 Spark、Flink,大多数是在 YARN 上。
现在很多公司开始迁移:
- Spark on K8s
- Flink on K8s
- Airflow / Argo 调度
- 对接对象存储 + Lakehouse
架构听起来非常优雅:
Kubernetes
│
├── Spark Job
├── Flink Streaming Job
├── Kafka
├── Trino
└── ML Training
一套平台,统管 批处理 + 流处理 + AI。
但现实往往是:
架构很云原生,性能却很“离谱”。
很多团队在迁移到 Kubernetes 后,都会踩到一些 非常隐蔽的性能坑。
今天咱们就用实战 + 代码,聊聊:
Spark / Flink 跑在 Kubernetes 上,最容易踩的几个坑。
一、Spark on Kubernetes:资源配置不对,性能直接腰斩
很多人刚把 Spark 迁移到 Kubernetes 时,最常见的问题就是:
CPU、内存配置看起来没问题,但任务就是慢。
先看一个常见的提交命令:
spark-submit \
--master k8s://https://k8s-api:6443 \
--deploy-mode cluster \
--name spark-etl-job \
--class com.echo.etl.Main \
--conf spark.executor.instances=10 \
--conf spark.executor.memory=4g \
--conf spark.executor.cores=2 \
--conf spark.kubernetes.container.image=echo/spark:3.5 \
local:///opt/spark/app.jar
看起来很正常对吧?
但这里有一个 隐藏问题:
executor.memory ≠ pod memory
在 Kubernetes 里,Pod 还需要额外内存:
- JVM overhead
- shuffle buffer
- off-heap memory
如果你只配置:
spark.executor.memory=4g
Kubernetes Pod 实际可能只给 4GB limit。
结果就是:
OOM Kill。
正确方式应该这样:
--conf spark.executor.memory=4g \
--conf spark.executor.memoryOverhead=1024
同时在 K8s 里设置:
pod memory ≈ executor.memory + overhead
否则就会出现一个经典现象:
Spark UI 没报错,但 Pod 一直被 Kubernetes 杀掉重启。
很多团队排查半天日志才发现是 cgroup memory limit。
二、Shuffle 在 Kubernetes 上容易“炸盘”
Spark 最大的性能瓶颈之一就是 Shuffle。
在 YARN 时代:
NodeManager + 本地磁盘
而在 Kubernetes:
Pod 生命周期 = 临时
很多团队最开始会这么配置:
volumeMounts:
- mountPath: /tmp
name: spark-local
但如果底层是:
emptyDir
问题就来了:
emptyDir 默认在容器文件系统
这意味着:
Shuffle 数据会写进容器层。
后果:
- IO 非常慢
- 容器层膨胀
- Node 磁盘爆满
正确姿势是:
volumes:
- name: spark-local
hostPath:
path: /data/spark
或者:
local SSD
Spark 配置:
--conf spark.local.dir=/data/spark
否则 Shuffle 稍微大一点:
性能直接掉 50% 都不奇怪。
三、Flink on Kubernetes:Checkpoint 是个大坑
Flink 跑在 Kubernetes 最大的坑之一就是:
Checkpoint 存储。
很多人一开始这么写:
state.backend: filesystem
state.checkpoints.dir: file:///flink/checkpoints
看起来 OK。
但问题是:
Pod 重启 = 本地状态丢失
所以生产环境一定要用:
- S3
- OSS
- HDFS
- MinIO
例如:
state.backend: rocksdb
state.checkpoints.dir: s3://flink-checkpoints/
state.savepoints.dir: s3://flink-savepoints/
在 Kubernetes 上,对象存储是最稳的。
否则一旦:
Pod reschedule
状态就没了。
四、Flink Slot 和 Kubernetes Pod 不匹配
很多团队在 Kubernetes 上跑 Flink 时,还有一个经典问题:
资源利用率非常低。
例如:
TaskManager
CPU: 8
Memory: 16GB
slots: 1
结果:
7核 CPU 全部闲置
正确配置应该让:
slots ≈ CPU cores
例如:
taskmanager.numberOfTaskSlots: 8
这样 Flink 才能并行执行 Task。
否则你会看到:
CPU 使用率只有 10%
但任务却跑得很慢。
五、Kubernetes 调度延迟:流处理的隐形杀手
Spark 批处理影响不大。
但 Flink 流处理就不一样了。
Kubernetes 调度流程:
JobManager
↓
K8s API
↓
Scheduler
↓
Node
↓
Container Runtime
如果集群很忙:
Pod Pending 30s
那 Flink 扩容就会非常慢。
这时候需要:
Cluster Autoscaler
+
Pod Priority
+
Node Pool
例如:
priorityClassName: flink-high-priority
这样关键流任务不会被普通任务挤掉。
六、Spark Driver 是单点
Spark on Kubernetes 还有一个经常被忽略的问题:
Driver Pod = 单点
如果 Driver 挂了:
整个 Job 直接失败
解决方案:
checkpoint + retry
例如:
--conf spark.task.maxFailures=8
--conf spark.stage.maxConsecutiveAttempts=5
或者使用:
Spark Operator
提交 Job:
apiVersion: sparkoperator.k8s.io/v1beta2
kind: SparkApplication
metadata:
name: spark-pi
spec:
type: Scala
mode: cluster
image: spark:3.5
mainClass: org.apache.spark.examples.SparkPi
driver:
cores: 1
memory: 2g
executor:
cores: 2
instances: 5
memory: 4g
Operator 会自动:
- 重试
- 管理生命周期
- 监控状态
七、我对 “大数据 + Kubernetes” 的一点真实看法
很多公司现在流行一句话:
“全部云原生。”
但我这些年看到的现实是:
不是所有大数据任务都适合 Kubernetes。
适合 K8s 的场景:
- AI 训练
- Spark ETL
- Ad-hoc 分析
- 多租户
但不一定适合的:
- 超大 Shuffle
- PB 级离线计算
- 超低延迟流处理
有时候:
Spark on YARN
反而更稳定
所以技术选型一定要想清楚:
我们到底是要“架构好看”,还是“系统稳定”?
这其实是两个完全不同的问题。
结尾
如果你准备在 Kubernetes 上跑 Spark / Flink,我非常建议你提前想清楚三件事:
第一:存储怎么做
Shuffle
Checkpoint
State
第二:资源如何匹配
CPU
Memory
Slot
Pod
第三:调度延迟是否可接受
Batch vs Streaming
Kubernetes 确实是未来的大势,但它不是银弹。
很多时候:
架构越先进,踩坑越多。
但换个角度想,这也是技术最有意思的地方。
我们做工程的,其实就是不断在这些坑里:
摔一跤 → 总结经验 → 再爬起来。
慢慢的,你就会发现:
原来那些“性能玄学”,背后其实都是 系统原理。
- 点赞
- 收藏
- 关注作者
评论(0)