Flink 生产部署拓扑与组件职责
Flink 作业能从 Kafka 读到数据,并不意味着它已经具备生产恢复能力。任务运行在什么进程、状态存在哪里、谁负责申请资源,以及故障后从哪个位置继续处理,需要在部署时分别确定。
本文以 Apache Flink 2.3 文档为基线,讨论有状态流作业的生产拓扑。图中的连接区分任务调度、记录传输与状态持久化;对象存储、Kafka 和 Kubernetes 都是外部系统,不属于 TaskManager 内部组件。批处理的调度与 shuffle 策略另有配置,不能把下面的持续流水线直接套用到所有有界作业。
集群内外的职责
JobManager 负责协调执行,TaskManager 执行 source、转换和 sink。业务记录通常在 TaskManager 及外部系统之间流动,不需要经过 JobManager 转发。客户端负责提交;作业以 detached 方式启动后,客户端退出并不等于作业结束。
图 1:Flink 控制连接、业务数据与快照存储的边界
flowchart TB
C["客户端"] -.->|"提交"| JM["JobManager"]
JM -.->|"调度与协调"| TM["TaskManagers"]
S["外部数据源"] -->|"业务记录"| TM
TM -->|"业务记录"| D["外部接收系统"]
TM -->|"状态快照"| FS["持久化存储"]
JM -.->|"恢复元数据"| FS
图 1 的虚线表示控制关系。增大 JobManager 的 CPU 配额不能直接解决 sink 写入变慢;增加 TaskManager 也不能弥补不可访问的快照目录。故障定位要沿着发生问题的连接检查。
JobManager 内的三个角色
Dispatcher 接收提交并为 job 启动 JobMaster;每个 JobMaster 管理一个 JobGraph 的执行。Flink ResourceManager 管理 slot,并与资源提供方配合获取 TaskManager。这里的 ResourceManager 是 Flink 内部组件,和 YARN 的同名服务不是同一个进程。组件职责见官方架构说明。
图 2:JobManager 内的提交、单作业协调与资源管理
flowchart TB
subgraph JM["JobManager 进程"]
D["Dispatcher"]
J1["JobMaster:job A"]
J2["JobMaster:job B"]
R["Flink ResourceManager"]
D --> J1
D --> J2
J1 -->|"申请 slot"| R
J2 -->|"申请 slot"| R
end
R -->|"管理资源"| T["TaskManagers"]
J1 -.->|"部署与恢复任务"| T
J2 -.->|"部署与恢复任务"| T
图 2 表示逻辑职责,不要求把三个角色分别部署成三个 Pod。JobMaster 与 job 一一对应,也不意味着一个 Application 只能生成一个 job。
Session 和 Application 的部署边界
Session 集群先启动,再接收不同应用提交的 job。它适合需要复用既有执行资源的任务,但共享 TaskManager 的作业也共享进程故障影响范围:一个进程退出,该进程上多个 job 的任务都可能需要恢复。
图 3:Session 集群复用执行资源及其故障影响范围
flowchart TB
A["应用 A 客户端"] --> JM["长期运行的 JobManager"]
B["应用 B 客户端"] --> JM
JM --> T1
JM --> T2
subgraph T1["TaskManager 1:共享 JVM"]
A1["job A 的任务"]
B1["job B 的任务"]
end
subgraph T2["TaskManager 2"]
A2["job A 的其他任务"]
end
F["TM 1 退出"] -.-> A1
F -.-> B1
Application 模式把应用的 main() 放到 JobManager 侧运行,集群生命周期与该应用绑定。一次应用执行可以生成多个 job。对于需要独立发布、资源预算和故障范围的长期作业,按应用划分集群更容易管理;同一 Kubernetes 节点上的不同应用仍可能争用磁盘与网络。
图 4:Application 集群按应用隔离并承载一个或多个 job
flowchart TB
P["提交应用包"] --> A
subgraph A["Application A 专属集群"]
M["JobManager 执行 main"]
J1["job A1"]
J2["job A2"]
T["A 的 TaskManagers"]
M --> J1
M --> J2
J1 --> T
J2 --> T
end
B["Application B 专属集群"]
图 4 的隔离单位是应用。官方 2.3 部署概览以 Application 和 Session 为主要模式;旧资料中的 Per-Job Cluster 已属于弃用路线,不应与当前推荐模式并列给出无版本限制的部署建议。部署模式及生命周期。
资源提供方决定谁启动进程
Session/Application 回答集群供谁使用;Standalone、YARN、Native Kubernetes 回答进程由谁提供。Standalone 可以运行在裸机、容器或 Kubernetes 上,因此“运行在 Kubernetes”本身不能证明启用了 native 集成。
图 5:部署模式与资源提供方是两项独立选择
flowchart TB
P["部署设计"] --> M["应用与生命周期"]
P --> R["进程与资源供给"]
M --> S["Session:共享集群"]
M --> A["Application:专属集群"]
R --> ST["Standalone:外部启动 TM"]
R --> Y["YARN:申请容器"]
R --> K["Native Kubernetes:申请 Pod"]
Standalone 的 Flink ResourceManager 只能分配已注册 TaskManager 的 slot,不会自行启动新的 TaskManager。图 5 因而也决定了扩容操作的归属:Standalone 需要外部管理进程数量,native 集成可以按资源需求申请或释放 TaskManager。
图 6:Native Kubernetes 中 TaskManager Pod 的申请与注册
sequenceDiagram
participant J as JobMaster
participant R as Flink ResourceManager
participant K as Kubernetes API
participant T as TaskManager Pod
J->>R: 请求执行资源
R->>K: 创建 TaskManager Pod
Note over K,T: Kubernetes 调度并启动 Pod
T->>R: 注册可用 slot
R-->>J: 提供资源
J->>T: 部署任务
Flink Kubernetes Operator 是额外的生命周期管理组件,负责管理 Flink 部署资源;它不参与逐条记录处理,也不替代 JobMaster 的任务调度。Native Kubernetes 集成本身可以不经 Operator 使用。图 6 只画 Flink 运行时申请资源的路径。Native Kubernetes 与 Operator 的关系。
并行度、slot 与 TaskManager
TaskManager 是 JVM 进程,slot 是资源调度单位。默认 slot sharing 允许同一 job 中不同算子的子任务共享一个 slot。单一共享组、资源充足的常见流水线中,所需 slot 数可按最高算子并行度理解;分开共享组或设置更细的资源要求后,不能继续套用这个简化估算。
图 7:两个 slot 承载并行度为二的算子流水线
flowchart TB
subgraph TM["TaskManager JVM"]
subgraph S0["slot 0"]
A0["Source 0"] --> B0["Map 0"]
end
subgraph S1["slot 1"]
A1["Source 1"] --> B1["Map 1"]
end
CPU["共享进程 CPU 资源"]
CPU -.-> S0
CPU -.-> S1
end
图 7 省略了网络连接,强调 slot 与执行资源的关系。slot 不提供 CPU 隔离;增加 slot 数也不会产生更多 CPU 核。多个 slot 共用一个 JVM 时,进程退出和部分内存问题会影响其中所有任务。强隔离还需要进程、容器和资源配额配合。
算子链与网络交换
可链接的算子可以组成一个 task,减少线程交接和序列化开销。是否形成 chain 还受并行度、分区方式和 chaining 设置约束;不能把“相邻算子”直接等同于“一条 chain”。keyBy 需要按 key 重分区,通常在链之间引入网络交换。
图 8:算子链内部调用与 keyBy 后的跨任务交换
flowchart TB
subgraph C0["Task 0:同线程算子链"]
S0["Source 0"] --> M0["Map 0"]
end
subgraph C1["Task 1:同线程算子链"]
S1["Source 1"] --> M1["Map 1"]
end
M0 -->|"keyBy"| A0["聚合子任务 0"]
M0 -->|"keyBy"| A1["聚合子任务 1"]
M1 -->|"keyBy"| A0
M1 -->|"keyBy"| A1
图 8 中的两个聚合子任务按 key 接收记录,同一个 key 的状态归属同一个并行实例。算子链与 slot sharing 是两种机制:多个 task 可以共享 slot,但仍保有各自的任务线程。调度与链接规则以 Flink Architecture 为准。
内存预算覆盖整个进程
容器的内存上限需要覆盖 JVM heap、managed memory、network memory、其他堆外内存,以及 JVM 自身的 metaspace 和 overhead。只把 heap 设置成容器上限,会挤掉堆外部分的空间。
图 9:TaskManager 进程内存的预算层次
flowchart TB
P["Total Process Memory"] --> F["Total Flink Memory"]
P --> M["JVM Metaspace"]
P --> O["JVM Overhead"]
F --> H["Framework / Task Heap"]
F --> G["Managed Memory"]
F --> N["Network Memory"]
F --> X["Framework / Task Off-heap"]
图 9 展示包含关系,不代表这些区域的固定比例。RocksDB 的部分 native 内存可纳入 managed memory 管理;用户代码的额外堆外分配也需要预算。配置时可以从总进程内存出发,再根据实际状态量和网络压力调整,避免同时给多组互相约束的总量与分量赋值。TaskManager 内存配置。
运行时状态与恢复副本
State backend 决定工作状态的组织和访问方式;checkpoint storage 决定恢复快照存在哪里。HashMapStateBackend 把工作状态放在 Java heap,EmbeddedRocksDBStateBackend 使用 TaskManager 本地 RocksDB。后者仍消耗内存、磁盘与序列化资源,本地文件不能独立提供节点丢失后的恢复保证。
图 10:运行时状态与持久化 checkpoint 的两条存储路径
flowchart TB
U["算子更新状态"] --> B{"选择 state backend"}
B --> H["HashMap:Java heap"]
B --> R["EmbeddedRocksDB:本地数据库"]
H -->|"生成快照"| C["Checkpoint storage"]
R -->|"生成快照"| C
C --> D["共享持久化文件系统或对象存储"]
D -.->|"失败后恢复"| N["新 TaskManager"]
图 10 只列两种常见 backend。Flink 2.3 还提供面向远端状态的 ForSt,但该版本文档仍将它标为实验性,并列出 savepoint 格式等限制,不能当作本地 RocksDB 的无差别替换。生产方案应同时选择 backend、快照存储、访问权限和保留策略。State Backends,Checkpoints。
Checkpoint 完成的含义
Checkpoint 保存相互匹配的输入位置与算子状态。aligned checkpoint 中,先收到 barrier 的输入需要等待其他输入的同编号 barrier,随后形成一致状态切面;状态持久化可以异步完成。checkpoint 被触发不等于已经可用于恢复,监控需要查看最近一次成功完成的 checkpoint。
图 11:双输入算子的 aligned checkpoint 与完成确认
sequenceDiagram
participant A as 输入 A
participant B as 输入 B
participant O as 双输入算子
participant S as 快照存储
participant J as Checkpoint 协调器
A->>O: barrier n
Note over A,O: 暂停消费 A 在 barrier 后的记录
B->>O: barrier 前的剩余记录
B->>O: barrier n
O->>O: 建立状态切面并向下游传播 barrier
O->>S: 异步持久化快照
Note over A,O: 可恢复消费后续记录
S-->>O: 持久化成功
O-->>J: ACK n
Note over J: 收齐所需 ACK 后完成 checkpoint
图 11 省略了 source 触发和下游算子的独立快照过程。unaligned checkpoint 会把相关在途数据也纳入快照,以减少背压下的对齐等待,代价包括额外 I/O 和快照体积。
Flink 的 exactly-once 状态一致性也不意味着外部副作用天然去重。恢复会重放输入,source 必须支持相应位置的重读,sink 需要匹配的事务提交或幂等语义。向外部 HTTP 服务发出的任意写请求,不会仅因启用 checkpoint 就自动获得端到端 exactly-once。状态恢复机制。
HA、升级与恢复
TaskManager 故障需要重新提供执行资源并恢复状态;JobManager 故障还涉及协调者选举和恢复元数据。Kubernetes 重新创建 Pod 只解决进程存在的问题,不能凭空重建已经丢失的输入位置和状态。
图 12:Kubernetes HA 与作业恢复所需的外部状态
flowchart TB
F["原 JobManager 失效"] --> J["恢复的 JobManager"]
K["Kubernetes HA:选举与指针"] --> J
H["持久化 HA 元数据"] --> J
J -->|"重新部署任务"| T["可用 TaskManagers"]
C["已完成 checkpoint"] -->|"恢复算子状态"| T
S["可重放的数据源"] -->|"从匹配位置继续"| T
Kubernetes HA 依赖 ConfigMap 等 Kubernetes 资源及相应权限;JobManager 恢复所需元数据保存在 high-availability.storageDir,Kubernetes 中保存相关指针。HA 存储和 checkpoint 存储的用途不同,两者都应在新进程所在位置可访问。图 12 也解释了为什么“Pod 已恢复 Running”不足以证明业务恢复。Kubernetes HA Services。
Savepoint 与升级路径
Checkpoint 主要服务运行期故障恢复,savepoint 常用于受控停机、升级和迁移。两者都依赖状态文件,但生命周期、格式和可迁移条件存在区别;保留的 checkpoint 也可以作为恢复入口,不能把“人工恢复”视作 savepoint 独有能力。
图 13:从旧版本状态恢复到新版本的验证路径
flowchart TB
O["旧版本作业"] -->|"生成并确认成功"| S["可用 savepoint"]
S --> C["检查 operator UID、序列化与版本兼容"]
C --> T["隔离环境恢复新版本"]
T --> V{"状态和输出符合预期"}
V -->|"是"| P["执行受控生产切换"]
V -->|"否"| R["修复兼容问题并保留旧版本恢复路径"]
R --> C
图 13 的隔离恢复应使用独立输出位置,避免测试作业与生产作业重复写入。稳定的 operator UID 能帮助状态映射,但不能保证任意类型变更或 Flink 跨版本升级都兼容。保留旧应用包、配置和可读取的状态文件,是准备回退路径的一部分;外部 sink 已提交的数据还需要单独处理。Savepoints,Checkpoints vs. Savepoints。
从背压定位吞吐瓶颈
业务记录向下游流动,背压沿反方向传播。Source 显示高背压,可能是下游 sink 写入慢,也可能是中间算子计算或状态访问慢。只增加 source 并行度,可能继续增加下游积压。
图 14:业务数据与背压的相反传播方向
flowchart TB
S["Source"] -->|"记录"| A["聚合算子"]
A -->|"记录"| K["Sink"]
K -->|"写入"| D["外部存储"]
D -.->|"写入变慢"| K
K -.->|"输出受阻"| A
A -.->|"输出受阻"| S
A --> M["观察 busy、idle、back pressured"]
K --> M
图 14 的定位顺序从下游受阻位置向前检查。Web UI 的聚合指标可能掩盖子任务间差异,出现数据倾斜时需要查看各 subtask 的忙碌程度、输入量和状态大小。checkpoint 持续失败还会让最近成功恢复点越来越旧,即使作业仍显示 RUNNING。背压指标及其定义。
上线验证至少应覆盖 TaskManager 丢失、JobManager 重建、状态存储短暂不可用,以及 sink 变慢后的追赶。每种演练记录恢复耗时、重放范围和输出重复情况;生产 REST/Web UI 入口应放在受控网络和身份边界内。Flink 可以运行提交者的代码,不能把可提交作业的接口当成普通只读监控页暴露。Production Readiness Checklist。
Flink 专题导航
本篇是 Flink 部署专题入口。机制与选型可结合已有系列阅读:
| 阅读位置 | 内容 |
|---|---|
| 本篇 | 生产拓扑、组件职责、资源与恢复边界 |
| 事件时间、水位线与状态恢复 | 分布式系统系列中的时间与一致性机制 |
| Spark 与 Flink:两种计算引擎的设计选择 | Spark 系列中的引擎比较 |
参考资料
以上链接固定在 Apache Flink 2.3 文档分支;部署其他版本时需要核对配置项、connector 版本和恢复兼容性。图 1–14 是依据这些机制绘制的职责示意,不表示已经执行生产故障演练。
- Flink Architecture:进程、JobManager 子组件、slot 与 operator chain。
- Deployment Overview:Session 与 Application 的生命周期。
- Native Kubernetes:资源申请和部署入口。
- State Backends 与 Checkpoints:运行状态与持久化恢复副本。

