方糖猫Blog
  • 首页
  • 文章
  • 书架
  • 标签
  • 归档
  • 关于
Posts

三万个 Flink 作业怎么自动扩缩容:Netflix 两代 Autoscaler 的教训

方糖猫 方糖猫
2026年10月2日 - 约 17 分钟读完 - 全文 6574 字

目录

  • 背景一:Flink 是什么,扩缩容为什么贵
  • 背景二:开源社区有什么方案,谁更好
  • 为什么这个问题在 Netflix 躲不掉
  • 第一代:从外面看的 autoscaler(2019)
  • 第二代:从里面推理的 autoscaler(开源方案)
  • 落到三万个作业:三个工程缺口
  • 效果与调校
  • 三条可以带走的教训

相关文章

  • Spark 与 Flink 入门:批与流的两种血统,视频公司里各干什么活
  • C 端 IPTV 产品的 Java 高并发架构设计:高可用、高性能、零信任与缓存一致性
  • 千人千面 + 无限瀑布流:推荐 Feed 的完整实现架构
  • Netflix 服务拓扑系统笔记:实时依赖地图是怎么被生产环境捶出来的
  • Netflix 实时服务拓扑图笔记:把「谁依赖谁」从考古变成活地图

三万个 Flink 作业怎么自动扩缩容:Netflix 两代 Autoscaler 的教训

目标读者:三类人——

  • 平台/数据工程师:正在或准备给 Flink 作业做自动扩缩容,想知道选哪个方案、坑在哪(看"背景二"和"三个工程缺口");
  • 架构师:在给实时计算平台做选型,需要"外部指标 vs 内部推理"两条路线的完整对比(看两代方案的对比表);
  • CTO/技术管理者:关心自研和开源的边界,Netflix 从自建收敛到开源的过程是一个现成的决策样本(看"三条可以带走的教训")。

没跑过 Flink 也能读:正文前两节是背景知识;跑过的可以直接跳到"第一代"开始看。

之前那篇《软件知识的分层与获取地图》说过:方案层的知识没有教材,只有案例,大厂技术博客是主力来源。最近去读了 Netflix 技术博客的 A Tale of Two Flink Autoscalers,讲的是他们怎么做流处理作业的自动扩缩容。这篇是我的学习笔记,把里面的架构知识点整理出来。

问题的形状很普遍:作业负载随昼夜、活动、故障转移大幅波动,按峰值配资源浪费,按均值配资源高峰期 lag 爆炸。Netflix 的答案是先后造了两代 autoscaler,现在正在从自研收敛到开源。两代的设计差异和迁移教训,比算法本身更有价值。

不过进入正题前,先补两段背景:Flink 是什么、扩缩容在这上面为什么天然就贵;以及开源社区现在有哪些现成方案、谁更好。

背景一:Flink 是什么,扩缩容为什么贵

Apache Flink 是开源的流处理框架,核心能力是"对无界数据流做有状态计算":数据像水管里的水一样源源不断地来(Kafka 消息、日志、点击流),Flink 一边流一边算,算出实时结果(实时报表、实时推荐、风控告警)。

一个 Flink 作业(job)在逻辑上是一张算子组成的有向无环图(DAG):数据从 source 进来,经过 map、keyBy、window、join 这些算子的处理,最后写到 sink。

flowchart LR
    A["source<br/>读 Kafka"] --> B["map / filter<br/>转换清洗"]
    B --> C["keyBy + window<br/>分组聚合 带状态"]
    C --> D["sink<br/>写下游"]

要理解扩缩容为什么难,需要知道四个机制:

  • 并行度(parallelism):每个算子可以拆成多个并行实例(叫 subtask),分摊在集群的 TaskManager 进程里跑。所谓"扩缩容",本质就是调整各算子的并行度、增减 TaskManager。
  • 状态(state):keyBy 之后的窗口、聚合结果都存在状态里,大作业的状态可以到 TB 级。状态按 key 分布在各个并行实例上。
  • checkpoint / savepoint:Flink 定期把状态整体快照下来,用于故障恢复。改并行度意味着状态要在新数量的实例间重新分配——所以扩缩容的标准动作是"做 savepoint → 停作业 → 按新并行度重启 → 重新加载状态",状态越大恢复越慢。
  • 背压(backpressure):下游算子处理不过来,会沿着数据流反向"顶住"上游。背压是判断"瓶颈在哪个算子"的关键信号,也是第二代 autoscaler 的数据基础。

一句话:无状态服务扩缩容是加台机器的事;有状态流处理扩缩容是一次"带着全部家当搬家"。这就是为什么"自动扩缩容"在无状态微服务上早就普及,在 Flink 上却直到这两年才有成熟方案。

背景二:开源社区有什么方案,谁更好

给流处理作业做自动扩缩容,社区里前后出现过几条路线:

方案 原理 粒度 现状
Flink Reactive Mode(1.13 起) 作业自适应集群里现有的 TaskManager 数量,配合 K8s HPA 按 CPU 伸缩容器 整个作业一刀切 简单可用,但按容器指标决策,看不见作业内部瓶颈
Apache Flink Autoscaler(Flink Kubernetes Operator 内置) 从作业内部逐算子算"真实处理速率",按需逐顶点调并行度 逐算子 社区主力方向,本文的主角
云厂商托管服务(如 AWS Managed Service for Apache Flink) 平台按容器利用率自动伸缩 整个作业一刀切 零运维但粒度粗,复杂有状态作业效果有限
各家自研(Netflix 第一代、当年 Twitter Heron 的 Dhalion 等) 外部指标(CPU、lag、背压)+ 自研策略 整个作业一刀切 大多是"社区没有成熟方案时"的历史产物

谁更好?对 2026 年刚开始做这件事的团队,答案基本收敛了:

  • 作业跑在 K8s 上 → 直接用 Flink Kubernetes Operator 自带的 autoscaler,顶点级、背压感知,是当前最通用的最优解;
  • 平台是自建的(不在 K8s 上、有自己的控制面)→ 核心算法库已经独立出来,可以像 Netflix 一样只接四个接口(下文详述),不必自建;
  • 作业简单、无状态、量不大 → Reactive Mode 或云托管的粗粒度伸缩就够用,别上重武器;
  • 从零自研外部指标 autoscaler → 2019 年的合理选择,2026 年不太成立——Netflix 自己正在从这条路迁走,这就是用脚投票。

有了这个背景,Netflix 这两代方案的故事就好读了:它几乎把上面每条路线都趟了一遍。

为什么这个问题在 Netflix 躲不掉

先看规模:2026 年,Netflix 在多个 AWS 区域跑着 3 万多个 Flink 作业(2017 年开始用 Flink)。这决定了"人肉运维"这条路根本不存在。

这三万个作业到底在干什么业务?按原文的分类可以拆成两大块:

  • 大头是托管平台 Data Mesh 自动生成的数据管道——所以这三万个作业的主体确实是"大数据相关",不是在跑在线业务服务。Data Mesh 是 Netflix 的托管数据搬运/处理平台(2023 年他们在博客公开过架构):用户在界面上声明"数据从哪来、到哪去、中间做什么转换",平台自动生成 Flink 作业——典型场景是 Kafka 之间的数据搬运、数据库变更(CDC)实时同步到数仓(Iceberg)、事件路由分发。用户大多根本不直接接触 Flink,自然也不会去管扩缩容。
  • 少数但增长快的是各团队自建自运维的自定义作业,承载真正的实时业务逻辑:个性化(实时推荐特征计算)、广告(2022 年广告套餐上线后的曝光与计费遥测)、直播活动(体育赛事直播的实时监控)等。原文里靠 autoscaler 省下 110 万美元的案例,就来自做客户端遥测和日志的团队。

形态差异也很大:从 Kafka 搬运工式的单算子作业,到带分支、join、TB 级状态的复杂流水线都有——记住这个差异,后面两代 autoscaler 的能力边界正好卡在这里。

顺带澄清一个容易误解的点:首页千人千面的推荐,不是每个用户打开时都走一遍 Flink。Flink 是流处理引擎,干的是"事件来了就算",不在用户请求链路上。个性化系统是经典的三层结构(Netflix 早在 2013 年就公开过这套架构):

flowchart LR
    A["离线 offline<br/>批量训练模型<br/>计算全量候选集"] --> D["在线存储<br/>画像 特征 候选集"]
    B["近线 nearline<br/>行为事件进 Kafka<br/>Flink 实时更新画像特征"] --> D
    D --> C["在线 online<br/>打开首页时排序组装<br/>几十毫秒返回"]

你在观看、暂停、评分时产生的行为事件,由近线层的 Flink 作业实时消化、更新画像;你打开首页那一瞬间,在线服务只做"查 + 排",几十毫秒返回。把重计算从请求路径挪到事件流上,正是个性化系统扛住全球用户规模的关键设计——本文说的"个性化"自定义作业,干的就是近线这一层的活。

更麻烦的是约束条件:在这个平台上,扩缩容不是免费的。默认动作是"做 savepoint → 优雅停掉作业 → 按新规模重启",大状态作业一次要几分钟。这意味着扩缩容策略不能冲动——每次动作都是分钟级的中断。

于是一个真正难的问题摆在面前:如何在没有人的情况下,让每个作业在需要时拿到需要的资源,同时不把任何东西搞挂?

第一代:从外面看的 autoscaler(2019)

Netflix 的第一个答案本身就是一条流处理作业:跑在自家 Mantis 平台上,消费遥测系统 Atlas 吐出的集群级实时指标——每个作业的 CPU、网络、Kafka lag、摄入速率、消费速率。

决策逻辑是几种信号的组合:lag 推导出的追赶时间、CPU/网络利用率阈值、历史性能基线、对近期摄入速率做回归预测,然后决定扩容,或者判断"更小的集群也扛得住未来窗口"从而缩容。

这个设计有两个当时看来很聪明、事后定义了天花板的选择:

  • 它独立于 Flink 平台运行。Flink 自己出问题时扩缩容不受牵连;而且按"流处理作业"的形态来构建,天然好扩展——每个 autoscaler 节点负责一部分 Flink 作业的指标,从没写过什么分片协调逻辑。
  • 成绩单不差:在几千个托管流水线上稳定砍下 25%-45% 的资源用量(Flink Forward 2020 分享过)。

但"从外面看"是有代价的,原文叫它 ceiling(天花板):

  1. 粗粒度 + 单一旋钮。它通过容器级指标推理整个集群,而且只能调一个数——TaskManager 总数,也就是作业里所有算子同比例缩放。对单算子简单流水线够用,对多算子、有状态的 DAG(广告、推荐、游戏团队越来越多拿来的正是这种)就无能为力——而这恰恰是它推理不了的作业类型。每接一种新 case 就得加定制逻辑,形不成通用能力。
  2. 指标会骗你。autoscaler 的天花板就是外部指标系统的天花板。有的作业明明忙得团团转,CPU 利用率上却毫无体现,作业卡在降级状态里,scaler 根本看不见。更狠的一次:一次网络迁移悄悄改变了部分流量的上报口径,Atlas 里 scaler 依赖的一组指标开始漏报——这个缺口在生产环境潜伏了很久才暴露。

到这一步,build vs buy 被重新摆上了桌面。

第二代:从里面推理的 autoscaler(开源方案)

2019 年自建时,Flink 社区没有成熟的 autoscaler;等 Netflix 重新评估时,已经有了:Apache Flink Autoscaler。思路发生了根本变化——不从外面看容器,从作业内部推理。

核心概念是真实处理速率(TPR, True Processing Rate):一个算子如果满负荷运转,能扛住的吞吐是多少。

Flink 会按 subtask 上报"每秒真正干活的时间占比"(和背压时间、空闲时间分开统计)。用观测吞吐除以忙碌占比,就能把容量外推到满负荷:

一个算子实测每秒处理 700 条记录、忙碌占比 70%,TPR = 700 / 0.7 = 1000 条/秒。

有了这个数,autoscaler 从 source 开始遍历整个作业 DAG,结合每个算子的 TPR、输入输出比率和目标利用率,逐顶点计算需要的并行度——保证没有任何一个算子成为瓶颈,而不是把整个集群当整体伸缩。

flowchart TD
    A["Flink 上报<br/>每个 subtask 的忙碌占比"] --> B["TPR = 观测吞吐 / 忙碌占比<br/>外推满负荷容量"]
    B --> C["从 source 遍历作业 DAG<br/>结合输入输出比率"]
    C --> D["逐顶点计算目标并行度<br/>没有算子成为瓶颈"]

两代方案的契约差异:

维度 自研(外部观察) 开源(内部推理)
信号来源 容器级指标(Atlas) 作业内 per-vertex 指标
决策单元 整个集群一个旋钮 DAG 逐顶点并行度
适用作业 单算子简单流水线 多算子有状态 DAG
配置粒度 全局统一 每个作业可自定义

对 Netflix 来说,决定性的就是后两行:开源方案能扩缩的恰好是自研系统天生处理不了的那类作业,而且每个作业可以带自己的配置(稳定期、阈值等扩缩行为参数)——这正是那些一直被人工扩缩容的自定义作业需要的。

落到三万个作业:三个工程缺口

算法层面拿来即用,社区已经把难的部分做完了。Netflix 的工作是让它在自己的体系里可靠运转——这才是"社区能跑"和"Netflix 规模能跑"之间的距离。

接入方式:独立库 + 四个接口。 开源 autoscaler 最初架构在 Flink Kubernetes Operator 里,而 Netflix 的 Flink 平台跑在自己的控制面上。社区后来做了一个关键决策:把核心逻辑抽成独立库,只留四个通用接口——携带作业元数据和 REST 信息的 context、状态存储、事件处理器、执行扩缩动作的 realizer。Netflix 的接入服务是一个 Spring Boot 应用,编排跑在 Temporal(持久化工作流引擎)上:一个编排工作流每分钟轮询一次控制面,为每个开启扩缩容的作业启动一个独立的长运行工作流。

flowchart LR
    A["Temporal 编排工作流<br/>每分钟轮询控制面"] --> B["每个作业一个<br/>独立长运行工作流"]
    B --> C["从 JobManager 拉<br/>per-vertex 指标"]
    C --> D["跑开源评估算法"]
    D --> E["realizer 通过控制面<br/>执行扩缩"]

“每个作业一个工作流"是直接被 pain 教育出来的设计:最初用单一批处理循环评估所有作业,一个慢作业或异常作业能卡住它后面所有作业的指标采集和扩缩。拆开之后,单个作业出问题只在自己的工作流里失败重试,爆炸半径被隔离,接入更多作业时运行时水平扩展即可。

三个必须自己补的工程缺口:

  1. 高并行度下的指标采集。大作业上从 JobManager 拉指标成为瓶颈,部分原因在 Flink 运行时本身。Netflix 改了 JobManager:缓存瞬时指标名、统一清理而不是每次拉取都重扫,并加了服务端过滤让 autoscaler 只拉需要的指标。效果:可用规模从约 1000 个 subtask 提到 3000 个。这些改动在内部 fork 里,部分已贡献回上游(如 FLINK-36172)。
  2. 保住 forward chaining。由 forward 边连接的两个顶点必须同并行度——记录是在内存里走固定本地通道交接的。如果只扩一个,Flink 不会报错,它会悄悄把这条边转成网络 shuffle(性能完全另一回事)。Netflix 的 fork 会检测 forward 连接的子图,把它们当整体扩缩。
  3. 尊重 sink 的容量上限。有些 sink 写入能力有限,把上游作业扩上去只会把自己顶死。fork 里加了 async-sink 背压检测,阻止 autoscaler 把作业扩到 sink 吃不下。

最后还有 realizer 的安全检查:比如公司级区域故障转移期间,拒绝在被撤离的区域缩容;执行前确认新集群磁盘放得下作业的 checkpoint 状态;大集群再加一点 standby 缓冲。

效果与调校

开源方案的 autoscaler 去年在 Netflix 自定义作业上 GA,早期结果里有个硬数字:客户端遥测日志团队的 Flink 计算支出年化下降 58%,约省 110 万美元。效率来自三件事:

  • 静态配置永远要按峰值留量,autoscaler 吃掉了昼夜和周末低谷;
  • 性能优化或节后流量回落之后,不再依赖团队手工降配,autoscaler 持续跟进;
  • 统一容器规格带来更好的装箱(bin-packing)和更细的扩缩粒度。

但"缩得太狠"是另一个坑:砍过头 CPU 打满、lag 飙升,而系统没法立刻反应——每次重启后指标窗口和稳定期都要重建。Netflix 现在把目标利用率定在 0.45,低于社区默认的 0.7,故意用一点效率换稳定:对大型有状态作业,更少、更平稳的扩缩动作值这个价。

剩下的最大成本已经不是 scaler 的逻辑,而是重启和状态恢复本身。Flink 2 的分离式状态架构(状态放外部存储而不是本地磁盘)有望让扩缩代价与状态大小解耦——Netflix 已开始支持 Flink 2.2,准备实验新 state backend。

三条可以带走的教训

文章结尾总结的三条,泛化到 Flink 之外依然成立:

  1. 指标选择比算法精巧更重要。 最有价值的调试很少是关于扩缩数学的,而是"该信哪个信号”。调算法之前,先搞懂你的指标——口径会变、会漏报、会测不到真正的瓶颈(第一代 scaler 的两次事故都是指标层的)。
  2. 默认值要合理,但留出调节空间。 托管作业相似度够高,一套好默认值覆盖大多数——这正是平台的意义。但把单一配置强加给所有作业,是在惩罚不适配的那些:默认值 + 每作业覆盖,同时把需要深专业知识的旋钮藏起来。大多数团队永远不该需要思考 autoscaler 的存在。
  3. 先采用,再扩展(Adopt, then extend)。 2019 年自建是因为没有成熟选项;社区方案出现后,正确的动作既不是永远捍卫自己的投资,也不是连夜拆掉,而是:新工作负载先用开源的,把修复贡献回上游,再有计划地迁移存量。自研基础设施的真实成本,要在"本来可以采用现成方案"的对照下衡量。

一句话收拢:自动扩缩容的难点从来不在"扩缩"二字,而在于你凭什么相信指标、怎么隔离故障、以及什么时候承认社区已经做得比你好了。


来源:Samuel Yeboah, Francesco Di Chiara, Mingliang Liu, “A Tale of Two Flink Autoscalers”, Netflix TechBlog。

  • 系统架构
  • Flink
  • 流处理
  • 自动扩缩容
  • Netflix
  • 系统设计
  • 大厂博客
方糖猫
方糖猫(大可)

18 年后端架构,现在独立做 AI 产品。这里记录 AI 视频、AI Agent 与工程实践。

本文阅读量 次
© 方糖猫Blog 2026
本站总访问量 次
蜀ICP备2026001758号