1. 为什么要在 CentOS 7 上折腾 Flink 1.18.1
Flink 1.18.1 这个版本在流计算圈子里算是一个相当稳的长期服役版本,1.18 系列修掉了 1.17 里不少状态后端和 Checkpoint 相关的边角问题,同时 API 层面已经趋于稳定,不会像早期版本那样隔三差五给你来个方法废弃警告。而 CentOS 7 虽然已经进入维护末期,但大量企业的生产环境仍然跑在上面,尤其是那些历史包袱比较重的数据平台,短期内不可能整体迁移。所以“Flink 1.18.1 部署与配置在 CentOS 7 上”这个组合,不是怀旧,是实打实的生产需求。
这篇文章面向的是需要在一台或多台 CentOS 7 机器上把 Flink 跑起来的人——可能是刚接手流计算平台运维的工程师,也可能是要搭一套本地开发验证环境的数据开发。我会从整体部署思路讲起,把 Standalone 集群的搭建、核心配置项的取舍、提交作业的完整流程、以及那些文档里不会写的坑,一条一条拆开说。读完之后你应该能独立完成一套可用的 Flink 集群部署,并且知道每个配置项为什么这么填,出了问题往哪个方向排查。
先说清楚一个前提:Flink 的部署模式有好几种,Standalone、Standalone HA、YARN、Kubernetes 等等。这篇文章聚焦Standalone 集群模式,原因很直接——CentOS 7 上的中小规模场景,Standalone 是最快能跑通、依赖最少、排查链路最短的方案。YARN 模式需要额外的 Hadoop 环境,K8s 模式需要容器编排基础设施,对于“先把 Flink 跑起来验证业务”这个目标来说都是过度设计。等你把 Standalone 玩明白了,再往 HA 或者资源调度框架上迁,思路是通的。
2. 部署前的整体设计与环境规划
2.1 集群角色划分与机器规划
Flink 的 Standalone 集群有两个核心角色:JobManager和TaskManager。JobManager 负责作业调度、Checkpoint 协调、资源管理;TaskManager 负责实际的任务执行,提供 Task Slot 给算子跑。小规模场景下,JobManager 和 TaskManager 可以混部在同一台机器上,但生产环境建议至少把 JobManager 独立出来,避免 TaskManager 的 CPU 和内存压力影响调度稳定性。
我一般推荐的机器规划是这样的:三台机器,一台跑 JobManager(同时可以兼一个 TaskManager),另外两台纯跑 TaskManager。这样即使 JobManager 所在机器出问题,后续上 HA 也有基础。如果只是本地验证,单机全角色混部完全够用。
| 角色 | 机器数量(生产建议) | 机器数量(验证环境) | 说明 |
|---|---|---|---|
| JobManager | 1(后续可扩到 2 做 HA) | 1 | 调度与协调核心 |
| TaskManager | 2 及以上 | 1(与 JM 混部) | 任务执行节点 |
| 总机器数 | 3 | 1 | 验证环境单机即可 |
这里有个容易被忽略的点:JobManager 和 TaskManager 的 JVM 堆内存要分开规划。JobManager 的堆主要承载作业图、调度元数据,一般 2G 到 4G 足够;TaskManager 的堆则要承载实际数据处理的算子状态,需要根据作业的数据量和状态大小来定。混部的时候两个进程的堆加起来不能超过机器物理内存的 70%,要给操作系统页缓存和堆外内存留余量。
2.2 版本选型背后的考量
为什么锁定 1.18.1 而不是更新的版本?这里有几个实际考量。第一,1.18.1 是 1.18 系列的第一个 bugfix 版本,修掉了 1.18.0 里几个影响 Standalone 部署的已知问题,稳定性比 .0 版本好很多。第二,1.18 系列对 JDK 的要求是 JDK 8 或 JDK 11,CentOS 7 上默认的 OpenJDK 8 可以直接用,不需要额外折腾 JDK 17 的兼容性。第三,1.18 的配置体系已经比较成熟,flink-conf.yaml里的核心参数在后续版本里变化不大,学会了迁移成本低。
需要特别注意的是,Flink 1.18 开始对JDK 8 的支持是“deprecated”状态,虽然还能跑,但官方建议往 JDK 11 迁。如果你在 CentOS 7 上装的是 OpenJDK 8,跑起来没问题,但日志里可能会有相关提示,不用慌。如果条件允许,装一个 OpenJDK 11 会更省心,CentOS 7 的 yum 源里可以通过java-11-openjdk-devel包安装。
2.3 依赖清单与前置检查
在动手之前,先把这几样东西确认一遍,能省掉后面一半的排查时间:
- JDK:
java -version确认版本,JDK 8 或 11 均可,推荐 11。 - SSH 免密:集群模式下 JobManager 需要通过 SSH 启动各节点上的 TaskManager,必须配置 JobManager 到所有节点(含自身)的免密登录。
- 主机名与 hosts:所有节点的
/etc/hosts里要有彼此的主机名映射,Flink 的配置里用主机名而不是 IP,后续换机器方便。 - 防火墙与端口:Standalone 模式默认用 8081(Web UI)、6123(JobManager RPC)、6121/6122(TaskManager 相关)。CentOS 7 的 firewalld 默认开着,要么关掉,要么把这些端口放行。
- 时钟同步:多节点之间时间偏差过大会导致心跳和 Checkpoint 出问题,用 chrony 或 ntp 保证同步。
提示:CentOS 7 的 firewalld 在验证环境里可以直接
systemctl stop firewalld && systemctl disable firewalld,但生产环境务必用firewall-cmd精确放行端口,不要图省事直接关。
3. 核心配置项拆解与参数计算
3.1 flink-conf.yaml 里真正需要改的那几项
Flink 的配置文件conf/flink-conf.yaml有上百个配置项,但 Standalone 部署真正需要你手动改的其实就那么几个。剩下的保持默认即可,改多了反而容易互相干扰。下面是我每次部署必改的清单:
# JobManager 的 RPC 地址,填 JobManager 所在机器的主机名 jobmanager.rpc.address: flink-master # JobManager 的 RPC 端口,默认 6123,一般不动 jobmanager.rpc.port: 6123 # JobManager 堆内存,验证环境 1g 够用,生产建议 2g-4g jobmanager.memory.process.size: 2048m # TaskManager 总内存(进程内存),这个是最关键的参数 taskmanager.memory.process.size: 4096m # 每个 TaskManager 提供的 slot 数量 taskmanager.numberOfTaskSlots: 4 # 集群默认并行度 parallelism.default: 4这里重点说taskmanager.memory.process.size和taskmanager.numberOfTaskSlots这两个参数,因为它们直接决定集群能扛多少活,也是最容易配错的地方。
taskmanager.memory.process.size是 TaskManager 进程的总内存,Flink 会在这个总内存里再切分出堆内存、堆外内存、网络缓冲、Metaspace 等区域。1.18 的内存模型是“总进程内存减去 JVM 开销(Metaspace、JVM 自身)后,剩余部分再按比例分给 task heap、managed memory、network 等”。如果你只配了 4096m,实际能用于算子计算的堆可能只有 1.5G 左右,剩下的被框架开销吃掉了。所以这个值不能按“我想给算子多少内存”来填,而要按“算子需要多少 + 框架开销”来估。
一个粗略的估算公式是:process.size ≈ 算子所需堆内存 / 0.5。也就是说如果你希望算子有 2G 堆可用,process.size 至少要填 4G。这个 0.5 的系数是经验值,实际取决于网络缓冲和 managed memory 的配置比例。
taskmanager.numberOfTaskSlots决定一个 TaskManager 能并行跑多少个 subtask。slot 数不是越多越好——每个 slot 会分到一份内存,slot 太多会导致单 slot 内存不足,作业容易 OOM。经验法则是:slot 数 = TaskManager 所在机器的 CPU 核数,或者略少于核数。比如 8 核机器配 4 到 8 个 slot 都合理,4 核机器配 2 到 4 个。
3.2 内存模型的参数联动关系
Flink 1.18 的内存配置是一套联动体系,改一个参数可能影响另一个。核心的几个参数之间的关系是这样的:
| 参数 | 作用 | 默认值 | 调整建议 |
|---|---|---|---|
taskmanager.memory.process.size | 进程总内存 | 无默认,必须配 | 按算子需求 × 2 估算 |
taskmanager.memory.task.heap.size | 算子堆内存 | 由总内存推导 | 一般不用手动配 |
taskmanager.memory.managed.size | 托管内存(RocksDB 等) | 由总内存推导 | 用 RocksDB 状态后端时关注 |
taskmanager.memory.network.min/max | 网络缓冲 | 由总内存推导 | 数据 shuffle 大时调大 |
taskmanager.memory.jvm-metaspace.size | Metaspace | 256m | 用大量 UDF 时调大 |
如果你用了RocksDB 作为状态后端(这是生产环境大状态场景的标配),managed memory 的大小就很重要。RocksDB 的写缓冲和块缓存都从 managed memory 里分配,managed memory 太小会导致频繁刷盘、性能下降。默认情况下 managed memory 占总内存的一定比例,如果状态很大,可以显式设置taskmanager.memory.managed.size或者用taskmanager.memory.managed.fraction调整比例。
注意:不要同时手动设置
taskmanager.memory.managed.size和taskmanager.memory.managed.fraction,两者只能取其一,同时设置会报配置冲突。
3.3 网络与 Checkpoint 相关配置
Standalone 集群跑起来之后,真正影响稳定性的往往是网络缓冲和 Checkpoint 配置。网络缓冲不够,数据 shuffle 时会频繁反压;Checkpoint 配置不当,作业恢复时间会很长。
# 网络缓冲内存,数据量大时适当调大 taskmanager.memory.network.min: 64m taskmanager.memory.network.max: 1g # Checkpoint 间隔,根据业务容忍的恢复时间定 execution.checkpointing.interval: 60000 # Checkpoint 超时时间 execution.checkpointing.timeout: 600000 # Checkpoint 存储位置,Standalone 下用文件系统 state.checkpoints.dir: file:///data/flink/checkpoints # 状态后端,大状态用 rocksdb state.backend: rocksdb state.backend.incremental: trueexecution.checkpointing.interval设成 60 秒是个折中值。设太短(比如 10 秒)会给集群带来持续的 Checkpoint 压力,尤其是状态大的作业;设太长(比如 10 分钟)则故障恢复时要重放更多数据。60 秒到 3 分钟是比较常见的区间,具体看业务对数据重复的容忍度。
state.backend.incremental: true这个配置在 RocksDB 下强烈建议打开,它让 Checkpoint 只上传增量部分,大幅减少 Checkpoint 的数据量和耗时。代价是恢复时需要合并多个增量文件,恢复时间会略长,但整体收益远大于代价。
4. 完整部署实操流程
4.1 环境准备与 JDK 安装
先在每台机器上把基础环境铺好。以 OpenJDK 11 为例:
# 安装 OpenJDK 11 yum install -y java-11-openjdk-devel # 确认版本 java -version如果机器上已经有 JDK 8 且不想动,也可以直接用,Flink 1.18 兼容。但要确认JAVA_HOME环境变量指向正确的 JDK:
# 写入 /etc/profile.d/java.sh export JAVA_HOME=/usr/lib/jvm/java-11-openjdk export PATH=$JAVA_HOME/bin:$PATH然后配置主机名和 hosts。假设三台机器的主机名分别是flink-master、flink-worker1、flink-worker2,在每台的/etc/hosts里加上:
192.168.x.10 flink-master 192.168.x.11 flink-worker1 192.168.x.12 flink-worker2接着配置 SSH 免密。在flink-master上生成密钥对,然后把公钥分发到所有节点(包括自己):
ssh-keygen -t rsa -N "" -f ~/.ssh/id_rsa ssh-copy-id flink-master ssh-copy-id flink-worker1 ssh-copy-id flink-worker2验证一下免密是否生效:ssh flink-worker1 hostname应该直接返回主机名而不提示输密码。这一步没做好的话,后面start-cluster.sh会卡在启动 TaskManager 的环节,而且报错信息不明显,很容易误判成别的问题。
4.2 Flink 安装包解压与目录规划
从官方渠道下载flink-1.18.1-bin-scala_2.12.tgz,注意 Scala 版本要和后续提交的作业匹配。解压到一个统一的目录,我习惯放在/opt/flink:
tar -xzf flink-1.18.1-bin-scala_2.12.tgz -C /opt mv /opt/flink-1.18.1 /opt/flink目录结构里几个关键位置要心里有数:conf/放配置文件,bin/放启动脚本,lib/放依赖 jar,log/放日志。数据目录(Checkpoint、Savepoint)建议单独规划,不要放在 Flink 安装目录下,避免升级时误删。我一般用/data/flink/checkpoints和/data/flink/savepoints。
如果集群是多节点,把解压好的目录同步到其他节点:
scp -r /opt/flink flink-worker1:/opt/ scp -r /opt/flink flink-worker2:/opt/4.3 配置文件修改与节点清单
修改conf/flink-conf.yaml,按前面 3.1 节的清单填入配置。然后编辑conf/workers文件,列出所有 TaskManager 节点的主机名:
flink-worker1 flink-worker2如果 JobManager 所在机器也兼跑 TaskManager,把flink-master也加进去。conf/masters文件里填 JobManager 的主机名和端口:
flink-master:8081这里的 8081 是 Web UI 端口,不是 RPC 端口,别搞混。RPC 端口在flink-conf.yaml的jobmanager.rpc.port里配,默认 6123。
4.4 启动集群与验证
在flink-master上执行启动脚本:
/opt/flink/bin/start-cluster.sh脚本会先启动本机的 JobManager,然后 SSH 到workers文件里列出的每台机器启动 TaskManager。启动完成后,用jps检查进程:
# 在 master 上应该看到 StandaloneSessionClusterEntrypoint jps # 在 worker 上应该看到 TaskManagerRunner ssh flink-worker1 jps然后打开浏览器访问http://flink-master:8081,能看到 Flink Web UI 就说明集群起来了。在 UI 的 Task Managers 页面应该能看到所有 worker 节点,每个节点的 Slots 数量和你配的numberOfTaskSlots一致。
如果 Web UI 打不开,先检查 firewalld 是否放行了 8081 端口,再检查 JobManager 进程是否真的起来了。如果进程在但 UI 打不开,看log/flink-*-standalonesessionclusterentrypoint-*.log里的报错。
4.5 提交第一个作业验证集群
集群起来之后,用自带的示例 jar 提交一个 WordCount 作业,验证整条链路:
/opt/flink/bin/flink run \ /opt/flink/examples/streaming/WordCount.jar提交后到 Web UI 的 Running Jobs 页面看作业状态,应该是 RUNNING。如果作业卡在 CREATED 或者 FAILED,看 TaskManager 的日志。常见的失败原因是 slot 不够(并行度大于总 slot 数)或者内存不足(process.size 配太小)。
验证完之后可以取消作业:
/opt/flink/bin/flink list /opt/flink/bin/flink cancel <job-id>5. 常见问题与排查技巧实录
5.1 启动脚本卡住或 TaskManager 起不来
这是 Standalone 部署最高频的问题,九成以上是 SSH 免密没配好。start-cluster.sh在启动 TaskManager 时是通过 SSH 远程执行的,如果免密不通,脚本会卡在密码提示或者直接失败。排查方法很直接:手动ssh flink-worker1看是否免密登录,不通就重新分发公钥。
还有一种情况是workers文件里的主机名和/etc/hosts里的映射对不上,SSH 解析不到主机。用ping flink-worker1确认主机名能解析到正确 IP。
5.2 Web UI 能打开但作业提交失败
作业提交失败但集群本身正常,通常是资源问题。先看 Web UI 的 Task Managers 页面,确认有可用的 slot。如果 slot 数为 0,说明 TaskManager 没注册上,回到 5.1 排查。如果 slot 有但作业还是失败,看 JobManager 日志里的具体异常,常见的是NoResourceAvailableException,意思是当前 slot 不够跑这个作业的并行度,要么调低作业并行度,要么加 TaskManager 或 slot 数。
5.3 Checkpoint 一直失败
Checkpoint 失败的原因比较多,按这个顺序排查效率最高:
| 现象 | 可能原因 | 排查方向 |
|---|---|---|
| Checkpoint 超时 | 状态太大或网络慢 | 调大execution.checkpointing.timeout,检查存储目录 IO |
| Checkpoint 目录写入失败 | 目录权限或磁盘满 | 检查state.checkpoints.dir权限和磁盘空间 |
| Checkpoint 被取消 | 作业反压严重 | 看 Web UI 的反压指标,排查慢算子 |
| 增量 Checkpoint 报错 | RocksDB 配置问题 | 检查 managed memory 是否足够 |
Checkpoint 目录的权限问题特别隐蔽——Flink 进程以某个用户身份运行,如果 Checkpoint 目录属于另一个用户且没有写权限,Checkpoint 会静默失败。部署时统一用同一个用户跑 Flink,目录权限设成该用户可写。
5.4 内存相关报错的处理思路
java.lang.OutOfMemoryError: Java heap space是最常见的报错,说明算子堆内存不够。处理方式有两种:调大taskmanager.memory.process.size,或者调小numberOfTaskSlots让每个 slot 分到更多内存。前者需要机器有富余物理内存,后者会降低并行能力,根据实际情况选。
如果是Metaspace溢出,说明加载的类太多,通常是 UDF 或者依赖 jar 过多导致的,调大taskmanager.memory.jvm-metaspace.size即可。还有一种Direct buffer memory溢出,和网络缓冲或堆外内存有关,检查taskmanager.memory.network.max和taskmanager.memory.task.off-heap.size的配置。
实操心得:内存问题不要一上来就无脑调大总内存,先看日志确认是哪个区域溢出。堆溢出、Metaspace 溢出、直接内存溢出的处理方式完全不同,调错方向不仅解决不了问题,还会浪费机器资源。
5.5 日志位置与快速定位
Flink 的日志分散在几个地方,知道去哪找能省很多时间。JobManager 的日志在log/flink-<user>-standalonesessionclusterentrypoint-<hostname>.log,TaskManager 的日志在log/flink-<user>-taskexecutor-<hostname>.log。Web UI 上每个作业的 Exceptions 标签页也能看到该作业相关的异常堆栈,比翻日志快。
如果日志里出现Connection refused到某个端口,先确认对应进程是否在跑,再确认端口是否被防火墙拦了。CentOS 7 上ss -tlnp | grep 6123能快速确认端口监听状态。
6. 部署后的调优与运维建议
6.1 日志轮转与磁盘管理
Flink 默认的日志配置会持续往文件里写,长时间运行后日志文件会撑爆磁盘。conf/log4j.properties里可以配置轮转策略,把log4j.appender.file的MaxFileSize和MaxBackupIndex设上,比如单文件 100MB、保留 10 个备份。Checkpoint 目录也要定期清理,尤其是作业频繁重启的场景,旧的 Checkpoint 会越积越多。可以配一个定时任务清理超过一定天数的 Checkpoint 文件。
6.2 监控指标的接入
Standalone 集群默认把指标暴露在 Web UI 上,但生产环境需要接入统一的监控系统。Flink 支持通过metrics.reporter配置把指标推送到 Prometheus、InfluxDB 等。以 Prometheus 为例,在flink-conf.yaml里加上:
metrics.reporter.prom.factory.class: org.apache.flink.metrics.prometheus.PrometheusReporterFactory metrics.reporter.prom.port: 9249然后在 Prometheus 的抓取配置里加上各节点的 9249 端口。重点关注的指标包括 TaskManager 的堆使用率、Checkpoint 的持续时间和大小、作业的反压比例。这几个指标能覆盖大部分稳定性问题。
6.3 升级与回滚的注意事项
从 1.18.1 往上升级时,配置文件格式可能有细微变化,升级前先对比新旧版本的flink-conf.yaml默认配置。作业的 Savepoint 是跨版本兼容的,升级前先打一个 Savepoint,升级后用 Savepoint 恢复作业。回滚时同样用 Savepoint,不要依赖 Checkpoint,因为 Checkpoint 的格式和版本绑定更紧。
升级的顺序是先停作业、打 Savepoint、停集群、替换安装包、改配置、启集群、从 Savepoint 恢复作业。每一步都验证通过再走下一步,别图快一次性全做完,出了问题不好定位是哪一步引入的。
6.4 单机验证环境的最小化配置
如果只是本地验证,不需要三台机器,单机全角色混部就行。这时候workers文件里只填本机主机名,taskmanager.memory.process.size可以适当调小到 2048m,numberOfTaskSlots设成 2 到 4。单机环境下最容易踩的坑是内存超配——JobManager 和 TaskManager 两个 JVM 加起来超过物理内存,机器直接卡死。验证环境把两个进程的内存加起来控制在物理内存的 60% 以内比较安全。
我在实际部署中反复验证过的一点是:配置文件的每一行改动都要有理由。Flink 的默认配置是经过大量场景验证的,盲目调参往往适得其反。先把最小配置跑通,再根据实际监控数据有针对性地调,这个顺序不能反。踩过几次“调了一堆参数结果问题出在 SSH 免密”的坑之后,我现在部署任何集群都是先跑通最小链路,再谈优化。