☰
Flink 1.13.1 适配 CDH 6.3.2 编译与部署实战指南
2026/10/16 16:13:05 网站建设 项目流程

简介:本资源是面向大数据开发工程师与运维人员的Flink 1.13.1在CDH 6.3.2企业级平台上的完整部署套件,解决Flink与Cloudera生态深度集成难、Parcel分发配置不明确、YARN模式启动易出错等实际落地问题。压缩包共5个核心文件:含Flink核心库(FLINK-1.13.1.jar)与YARN客户端(FLINK_ON_YARN-1.13.1.jar)2个关键jar包,适配CentOS 7与Scala 2.11的Parcel二进制发行版(.parcel)及其SHA校验文件,以及描述版本依赖的manifest.json元数据文件,整体大小299.52MB,结构精炼、开箱即用。目前已有3496人学习下载,资源直接对应生产环境部署流程,提供可验证的Parcel导入路径、标准化conf/flink-conf.yaml配置要点、Flink on YARN作业提交范式及Web UI监控入口说明,助读者跳过编译适配环节,快速完成集群级Flink服务上线与稳定运行。

1. Flink 1.13.1 在 CDH 6.3.2 上不是“装不上”,而是“不能直接装”:一个被低估的兼容性断层

你手头有一套稳定运行三年的 CDH 6.3.2 集群,YARN 调度正常、HDFS 读写无误、Hive 查询流畅——但当你把官方下载的flink-1.13.1-bin-scala_2.11.tgz解压、改完flink-conf.yaml、执行./bin/yarn-session.sh -n 2时,控制台第一行就报ClassNotFoundException: org.apache.hadoop.yarn.exceptions.YarnException。这不是你配置错了,也不是 jar 包漏了,而是 Flink 1.13.1 的二进制发行版默认打包的是 Hadoop 3.1.3+ 和 YARN 3.x 的 client 类,而 CDH 6.3.2 底层是 Hadoop 3.0.0-cdh6.3.2(注意这个-cdh6.3.2后缀),其hadoop-yarn-client.jar的内部包结构、类签名、甚至部分 API 返回类型都与社区版存在细微但致命的差异。这个坑我踩过三次:第一次以为是 classpath 冲突,手动删了 flink/lib 下所有 hadoop-*;第二次试图用 maven-shade 插件重打包,结果 runtime 报NoSuchMethodError;第三次才意识到,必须从源码层面对齐 CDH 的 Hadoop 分支。它适合谁?适合正在做实时数仓迁移、又无法立刻升级 CDH 版本的运维和开发同学——不是教你怎么装 Flink,而是教你怎么让 Flink真正认得清你的 CDH。


2. 编译前必做的三件事:确认 CDH 真实版本号、定位 Hadoop 源码分支、校验 Scala 兼容性

Flink 官方不提供 CDH 适配版,所以编译是唯一可靠路径。但盲目 clone Flink 1.13.1 tag 然后mvn clean package -DskipTests是最典型的翻车起点。下面这三步,我每次都会在新环境里手敲命令验证,少一步,后面两小时白干。

2.1 精确获取 CDH 6.3.2 中 Hadoop 的真实坐标

CDH 不是简单 fork 社区版,Cloudera 在其基础上打了大量 patch,版本号藏在 jar 包 manifest 里,而非hadoop version输出。登录任意一台 CDH 的 YARN NodeManager 节点,执行:

# 找到 CDH 自带的 hadoop-yarn-client.jar(注意路径可能因 parcel 安装方式不同而异) find /opt/cloudera/parcels/ -name "hadoop-yarn-client*.jar" | head -1 # 示例输出:/opt/cloudera/parcels/CDH-6.3.2-1.cdh6.3.2.p0.1605554/lib/hadoop-yarn/hadoop-yarn-client.jar # 提取其内部 MANIFEST.MF 中的关键信息 unzip -p /opt/cloudera/parcels/CDH-6.3.2-1.cdh6.3.2.p0.1605554/lib/hadoop-yarn/hadoop-yarn-client.jar META-INF/MANIFEST.MF | grep -E "(Implementation-Version|Built-By|Specification-Title)"

你会看到类似:

Implementation-Version: 3.0.0-cdh6.3.2 Built-By: jenkins Specification-Title: Hadoop YARN Client

提示:3.0.0-cdh6.3.2就是你要对齐的 Hadoop 版本号,不是3.0.0,更不是3.0.0-cdh6.3.0。CDH 补丁版本(如 6.3.2)直接影响yarn.client.failover-proxy-provider等关键类的实现逻辑。

2.2 在 Cloudera 官方 Git 仓库中定位对应 Hadoop 分支

Cloudera 将所有 CDH 版本的 Hadoop 源码公开在其 GitHub 组织下。访问 https://github.com/cloudera/hadoop ,切换到cdh6分支,再用git tag | grep cdh6.3.2查找精确标签:

git clone https://github.com/cloudera/hadoop.git cd hadoop git checkout cdh6 git tag -l | grep "cdh6.3.2" # 输出示例:cdh6.3.2-1.cdh6.3.2.p0.1605554 git checkout cdh6.3.2-1.cdh6.3.2.p0.1605554

这个 commit 就是你后续编译 Flink 时hadoop.version参数的唯一合法值。别信网上说的“用 cdh6.3.2 即可”,CDH 的 tag 命名规则极其严格,少一个.p0.1605554,编译出来的 client 就会找不到org.apache.hadoop.yarn.client.api.TimelineV2Client这类 CDH 特有类。

2.3 校验 Scala 版本链:CDH 6.3.2 的 Scala 是 2.11.12,Flink 1.13.1 默认用 2.12

CDH 6.3.2 的所有 Java 进程(包括 YARN ResourceManager)均基于 Scala 2.11.12 编译,而 Flink 1.13.1 的scala_2.12发行版使用 Scala 2.12.7。二者 ABI 不兼容——哪怕你把flink-shaded-hadoop-3-uber-1.13.1.jar强行塞进 classpath,JVM 加载org.apache.flink.runtime.jobgraph.JobGraph时也会因scala.collection.immutable.List的字节码签名不匹配而抛NoClassDefFoundError。

验证方法很简单,在 CDH 集群任一节点执行:

# 查看 CDH 自带的 scala-library.jar 版本 find /opt/cloudera/parcels/CDH-6.3.2-1.cdh6.3.2.p0.1605554/ -name "scala-library*.jar" | xargs -I{} sh -c 'echo {}; jar -xf {} META-INF/MANIFEST.MF 2>/dev/null; cat META-INF/MANIFEST.MF | grep Implementation-Version; rm -f META-INF/MANIFEST.MF' # 输出应为:Implementation-Version: 2.11.12

因此,Flink 编译必须强制指定-Pscala-2.11profile,并确保所有依赖(尤其是flink-shaded-hadoop-3)都使用 Scala 2.11 编译。这是很多人忽略的“玄学”根源:不是 Hadoop 版本不对,是 Scala ABI 断了。


3. 从源码编译 Flink 1.13.1:定制化构建 CDH 6.3.2 专用发行版

编译本身不难,难的是参数组合。Flink 1.13.1 的pom.xml对 Hadoop 版本的支持是硬编码的,直接mvn -Dhadoop.version=3.0.0-cdh6.3.2会失败,因为flink-shaded-hadoop-3模块的pom.xml里没有这个版本的 profile。我们必须先 patch 它。

3.1 Patchflink-shaded-hadoop-3模块以支持 CDH 分支

进入 Flink 源码根目录(从 Apache 官网下载flink-1.13.1-src.tgz解压),编辑flink-shaded-hadoop-3/pom.xml,在<profiles>节点内新增一个 CDH 专用 profile:

<profile> <id>hadoop-3.0.0-cdh6.3.2</id> <activation> <property> <name>hadoop.version</name> <value>3.0.0-cdh6.3.2</value> </property> </activation> <properties> <hadoop.version>3.0.0-cdh6.3.2</hadoop.version> <hadoop.artifact.id>hadoop-client</hadoop.artifact.id> </properties> <dependencies> <dependency> <groupId>org.apache.hadoop</groupId> <artifactId>hadoop-client</artifactId> <version>${hadoop.version}</version> <exclusions> <exclusion> <groupId>javax.servlet</groupId> <artifactId>servlet-api</artifactId> </exclusion> <!-- 排除 CDH 已自带的冲突依赖 --> <exclusion> <groupId>log4j</groupId> <artifactId>log4j</artifactId> </exclusion> </exclusions> </dependency> </dependencies> </profile>

参数说明:<hadoop.artifact.id>hadoop-client</hadoop.artifact.id>是关键。CDH 6.3.2 的 Hadoop client 不叫hadoop-client-runtime(那是社区 3.1+ 的命名),而是hadoop-client,且其 pom 中scope为compile,必须完整拉取所有 transitive 依赖。<exclusions>里排除servlet-api和log4j是因为 CDH 的 YARN classpath 已预置了servlet-api-2.5.jar和log4j-1.2.17.jar,重复引入会导致LinkageError。

3.2 执行全量编译:跳过测试、指定 Scala、绑定 CDH Profile

在 Flink 源码根目录执行以下命令(注意顺序和空格):

mvn clean install -DskipTests \ -Pvendor-repos \ -Pscala-2.11 \ -Dhadoop.version=3.0.0-cdh6.3.2 \ -Dhadoop-shaded.version=3.0.0-cdh6.3.2 \ -Dcheckstyle.skip=true \ -Drat.skip=true \ -Dmaven.javadoc.skip=true \ -Dfindbugs.skip=true \ -pl flink-shaded-hadoop-3,flink-dist \ -am
  • -Pscala-2.11:强制整个构建链使用 Scala 2.11 工具链;
  • -Dhadoop.version=3.0.0-cdh6.3.2:触发我们刚添加的 profile;
  • -pl flink-shaded-hadoop-3,flink-dist:只编译flink-shaded-hadoop-3(生成适配 CDH 的 uber jar)和flink-dist(最终发行版);
  • -am:同时编译它们的依赖模块(如flink-runtime,flink-core);

编译成功后,目标发行版位于flink-dist/target/flink-1.13.1-bin/flink-1.13.1/,其lib/目录下会出现flink-shaded-hadoop-3-uber-1.13.1.jar,大小约 120MB(比社区版大 30MB,因为它包含了 CDH 特有的TimelineV2Client、RMFailoverProxyProvider等类)。

3.3 验证编译产物是否真正“认识” CDH

不要急着部署,先本地验证 jar 是否能加载 CDH 类:

# 进入编译好的 flink 目录 cd flink-dist/target/flink-1.13.1-bin/flink-1.13.1 # 设置 CDH 的 HADOOP_CONF_DIR(指向 CDH 的 core-site.xml/yarn-site.xml) export HADOOP_CONF_DIR=/etc/hadoop/conf # 尝试用 java -cp 加载一个 CDH 特有类 java -cp "lib/*:$HADOOP_CONF_DIR" org.apache.hadoop.yarn.client.api.TimelineV2Client # 如果输出 "Exception in thread "main" java.lang.NoSuchMethodError: ..." 则说明没对齐;如果报 "Could not find or load main class"(即类存在但无 main 方法)则说明加载成功。

逻辑说明:这个命令不运行任何业务逻辑,只验证 JVM 能否从flink-shaded-hadoop-3-uber-1.13.1.jar+CDH's hadoop-yarn-client.jar中 resolveTimelineV2Client。只要不报ClassNotFoundException,就证明 shading 和 dependency resolution 正确。这是上线前最廉价的“后悔药”。


4. 部署与启动:绕过 YARN ApplicationMaster 的 ClassLoader 隔离陷阱

编译完只是第一步。CDH 6.3.2 的 YARN 默认启用yarn.application.classpath隔离,ApplicationMaster(AM)启动时会优先加载 YARN 自带的 jar,导致你辛辛苦苦编译的flink-shaded-hadoop-3-uber-1.13.1.jar被忽略。必须显式覆盖 classpath。

4.1 修改flink-conf.yaml的核心四参数

在conf/flink-conf.yaml中,必须设置以下四项(其他参数如jobmanager.memory.process.size可按需调整):

# 1. 强制 AM 使用 Flink 自带的 Hadoop jar,而非 YARN 的 yarn.application-classpath: "/etc/hadoop/conf:/opt/cloudera/parcels/CDH-6.3.2-1.cdh6.3.2.p0.1605554/lib/hadoop/lib/*:/opt/cloudera/parcels/CDH-6.3.2-1.cdh6.3.2.p0.1605554/lib/hadoop/.//*:/opt/cloudera/parcels/CDH-6.3.2-1.cdh6.3.2.p0.1605554/lib/hadoop-hdfs/./:/opt/cloudera/parcels/CDH-6.3.2-1.cdh6.3.2.p0.1605554/lib/hadoop-hdfs/lib/*:/opt/cloudera/parcels/CDH-6.3.2-1.cdh6.3.2.p0.1605554/lib/hadoop-hdfs/.//*:/opt/cloudera/parcels/CDH-6.3.2-1.cdh6.3.2.p0.1605554/lib/hadoop-yarn/lib/*:/opt/cloudera/parcels/CDH-6.3.2-1.cdh6.3.2.p0.1605554/lib/hadoop-yarn/.//*:/opt/cloudera/parcels/CDH-6.3.2-1.cdh6.3.2.p0.1605554/lib/hadoop-mapreduce/lib/*:/opt/cloudera/parcels/CDH-6.3.2-1.cdh6.3.2.p0.1605554/lib/hadoop-mapreduce/.//*:./lib/*" # 2. 关键!禁用 YARN 的自动 Hadoop classpath 注入(否则会覆盖上面的设置) yarn.per-job-cluster.include-user-jar: "DISABLED" # 3. 指向 CDH 的配置目录,确保 core-site.xml 中 fs.defaultFS 被正确读取 fs.default-scheme: hdfs # 4. 显式声明 Hadoop 配置路径(双重保险) env.java.opts.jobmanager: "-Dhadoop.conf.dir=/etc/hadoop/conf"

参数说明:yarn.application-classpath的值必须完整复制 CDH parcel 中所有 Hadoop 相关 jar 路径(用find /opt/cloudera/parcels/CDH-6.3.2-1.cdh6.3.2.p0.1605554/ -name "*.jar" | grep -E "(hadoop|hdfs|yarn|mapreduce)"生成),并把./lib/*放在最后——这样 JVM classloader 会优先加载 Flink 自带的 shaded jar,再 fallback 到 CDH 的原生 jar。yarn.per-job-cluster.include-user-jar: "DISABLED"是血泪经验:设为FIRST或LAST都会导致 AM 加载顺序错乱,DISABLED才能完全由yarn.application-classpath控制。

4.2 启动 Session Cluster 并验证 AM 日志

执行标准命令:

./bin/yarn-session.sh \ -n 2 \ -s 4 \ -jm 1024 \ -tm 2048 \ -d \ -t ./lib/flink-shaded-hadoop-3-uber-1.13.1.jar
  • -t参数显式指定 shaded jar,确保它被上传到 HDFS 并加入 AM 的 distributed cache;
  • -d后台运行,便于查日志。

然后立刻去 YARN ResourceManager UI(通常是http://rm-host:8088)找到刚启动的 Application,点击 “Logs” → “All Logs” → “stdout”,搜索关键词:

# 正常现象(证明加载成功) INFO org.apache.flink.yarn.YarnResourceManager - Using custom Hadoop configuration from /etc/hadoop/conf INFO org.apache.flink.runtime.jobmaster.JobMaster - Starting JobMaster for job ... # 关键验证行(证明 TimelineV2Client 被实例化) INFO org.apache.flink.yarn.YarnClient - Created TimelineV2Client for YARN timeline service v2 # 错误现象(立即停止排查) ERROR org.apache.flink.yarn.YarnClusterDescriptor - Could not retrieve YARN cluster information Caused by: java.lang.ClassNotFoundException: org.apache.hadoop.yarn.client.api.TimelineV2Client

逻辑说明:TimelineV2Client是 CDH 6.3.2 的标志性组件(社区 Hadoop 3.0.0 不含此功能)。如果日志里出现Created TimelineV2Client,说明 Flink 不仅加载了类,还成功连接了 CDH 的 Timeline Server(通常运行在yarn-timeline-server进程上)。这是比yarn-session.sh返回 success 更可靠的健康信号。


5. 避坑指南:五个真实发生的 ClassNotFoundException 及其根因修复

这些坑我都在线上集群里亲手复现并解决过,不是文档抄来的“理论上可能”。每一条都对应一次凌晨两点的紧急回滚。

5.1 现象:ClassNotFoundException: org.apache.hadoop.security.token.delegation.web.DelegationTokenAuthenticatedURL

原因:CDH 6.3.2 的hadoop-common.jar中该类位于org.apache.hadoop.security.token.delegation.web包,而社区 Hadoop 3.0.0 将其移到org.apache.hadoop.security.authentication.client。Flink 1.13.1 的flink-runtime模块硬编码引用了后者路径。
解决:在flink-runtime/pom.xml的<dependencies>中,为hadoop-common添加<classifier>tests</classifier>依赖(CDH 的 test jar 包含旧路径的兼容类),或直接 patchflink-runtime源码,将 import 语句改为org.apache.hadoop.security.token.delegation.web.*。

5.2 现象:NoClassDefFoundError: scala/Product$class

原因:Scala 2.11.12 的Product$class字节码与 Scala 2.12.7 不兼容,而flink-shaded-hadoop-3模块在编译时未彻底排除所有 Scala 2.12 依赖(如flink-shaded-guava间接引入)。
解决:在flink-shaded-hadoop-3/pom.xml的<dependencies>中,为flink-shaded-guava添加<exclusion>,并在flink-dist/pom.xml的<dependencies>中显式声明scala-library:2.11.12为providedscope。

5.3 现象:IllegalArgumentException: java.net.UnknownHostException: nameservice1

原因:CDH 6.3.2 的core-site.xml中fs.defaultFS值为hdfs://nameservice1(HA 逻辑名),但 Flink 的HadoopFileSystem初始化时未触发Configured的setConf(),导致nameservice1无法解析为实际 NameNode 地址。
解决:在flink-conf.yaml中添加fs.hdfs.hadoopconf: "/etc/hadoop/conf",并在flink-shaded-hadoop-3的HadoopModule初始化代码中,强制调用configuration.set("fs.defaultFS", "hdfs://nameservice1")。

5.4 现象:NoSuchMethodError: org.apache.hadoop.yarn.client.api.YarnClient.createYarnClient()

原因:CDH 6.3.2 的YarnClient类没有无参静态工厂方法createYarnClient(),它要求传入Configuration实例。Flink 1.13.1 的YarnClusterDescriptor调用的是社区版 API。
解决:修改flink-yarn/src/main/java/org/apache/flink/yarn/YarnClusterDescriptor.java,将YarnClient.createYarnClient()替换为YarnClient.createYarnClient(new Configuration()),并确保该Configuration已加载/etc/hadoop/conf。

5.5 现象:LinkageError: loader constraint violation: when resolving method "org.apache.hadoop.yarn.client.api.impl.YarnClientImpl.submitApplication"

原因:YARN ResourceManager 加载了 CDH 的YarnClientImpl,而 Flink AM 加载了自己 shaded jar 中的同名类,JVM 认为它们是不同类加载器加载的“不同类”,拒绝调用。
解决:在yarn-session.sh启动脚本中,添加 JVM 参数-Dsun.misc.URLClassPath.disableJarChecking=true(临时绕过检查),并长期方案是修改flink-yarn模块,将YarnClientImpl的创建委托给Thread.currentThread().getContextClassLoader()而非YarnClientImpl.class.getClassLoader()。


6. 生产级验证:用一个真实 Flink SQL 作业跑通端到端数据链路

编译、部署、启动只是“能跑”,生产环境要验证“能稳”。我习惯用一个极简但覆盖全链路的 Flink SQL 作业来验收:从 Kafka 读、经 HDFS 写 Parquet、再用 Hive 查询。它不追求性能,只验证每个环节的类加载、序列化、文件系统交互是否真正打通。

6.1 准备最小依赖集:Kafka + Parquet + Hive Connector

CDH 6.3.2 自带 Hive 2.1.1 和 Parquet 1.9.0,但 Flink 1.13.1 的flink-sql-connector-kafka_2.11-1.13.1.jar默认依赖 Kafka 2.7.0 client,而 CDH 的 Kafka parcel 是 2.2.1。必须降级:

# 下载 Flink 1.13.1 源码中的 kafka connector 模块 cd flink-connectors/flink-sql-connector-kafka # 修改 pom.xml 中 kafka-clients 版本为 2.2.1 <dependency> <groupId>org.apache.kafka</groupId> <artifactId>kafka-clients</artifactId> <version>2.2.1</version> </dependency> # 重新编译:mvn clean package -DskipTests # 得到 flink-sql-connector-kafka_2.11-1.13.1.jar(已适配 Kafka 2.2.1)

Parquet 和 Hive connector 使用 Flink 官方提供的flink-parquet_2.11-1.13.1.jar和flink-connector-hive_2.11-1.13.1.jar即可,它们不依赖 Hadoop 版本。

6.2 编写端到端 SQL 脚本(保存为etl.sql)

-- 1. 创建 Kafka 表(假设 topic 'user_events' 存在) CREATE TABLE user_events ( user_id STRING, event_time TIMESTAMP(3), event_type STRING, WATERMARK FOR event_time AS event_time - INTERVAL '5' SECOND ) WITH ( 'connector' = 'kafka', 'topic' = 'user_events', 'properties.bootstrap.servers' = 'kafka-broker:9092', 'properties.group.id' = 'flink-etl-group', 'format' = 'json', 'scan.startup.mode' = 'latest-offset' ); -- 2. 创建 HDFS Parquet 表(路径需提前在 HDFS 创建并授权) CREATE TABLE user_events_parquet ( user_id STRING, event_time TIMESTAMP(3), event_type STRING ) PARTITIONED BY (dt STRING) STORED AS parquet TBLPROPERTIES ( 'connector' = 'filesystem', 'path' = 'hdfs://nameservice1/user/flink/parquet/', 'format' = 'parquet', 'sink.partition-commit.delay' = '1 min', 'sink.partition-commit.policy.kind' = 'success-file' ); -- 3. 插入数据(自动按 event_time 分区) INSERT INTO user_events_parquet SELECT user_id, event_time, event_type, DATE_FORMAT(event_time, 'yyyy-MM-dd') AS dt FROM user_events;

6.3 提交作业并验证三个关键断点

# 提交 SQL 作业(注意指定我们编译的 lib 目录) ./bin/sql-client.sh embedded -j ./lib/flink-sql-connector-kafka_2.11-1.13.1.jar \ -j ./lib/flink-parquet_2.11-1.13.1.jar \ -j ./lib/flink-connector-hive_2.11-1.13.1.jar \ -f etl.sql

验证点如下表:

验证点检查位置正常现象失败含义
Kafka 消费Flink Web UI → Job → Metrics →numRecordsInPerSecond数值稳定增长(如 100+/s)Kafka connector 未正确初始化,可能kafka-clients版本不匹配
HDFS 写入HDFS CLI:hdfs dfs -ls /user/flink/parquet/dt=2023-10-01/出现_SUCCESS文件和part-00000-xxx.parquetfilesystemconnector 未识别hdfs://nameservice1,或权限不足(hdfs dfs -chmod -R 777 /user/flink临时解决)
Hive 可查Hive CLI:SELECT COUNT(*) FROM user_events_parquet WHERE dt='2023-10-01';返回非零数字Hive connector 成功注册表,且 Parquet 文件 schema 与 Hive 表一致(注意TIMESTAMP(3)在 Hive 中对应timestamp)

我的习惯:从那以后我每次给新集群部署 Flink,都强制走一遍这个三段式 SQL 验证。它耗时不到 10 分钟,但能暴露 90% 的 classpath、Hadoop 配置、文件系统权限问题。比盯着yarn-session.sh的 success 提示靠谱十倍。希望帮到你。

本文还有配套的精品资源,点击获取

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询