flink on yarn的日志无法刷新问题解决

博客讲述了在使用CDH数据平台的Yarn上运行Flink集群时遇到的日志冲突问题。作者尝试了多种解决办法,包括添加slf4j-log4j12桥接器依赖、排除日志相关依赖、使用slf4j+log4j2并调整依赖排除。通过实验发现,尽管存在多个SLF4J绑定,但日志仍能正常记录,这归因于Java类加载器加载jar包的顺序。最终,作者通过本地引入slf4j+log4j2依赖并排除冲突的log4j库,使得日志问题得到解决,尽管CDH环境中的依赖冲突仍然存在。

我使用的是cdh数据平台,在yarn上跑flink集群,操作步骤如下:

一、本地尝试启动
flink用kafka做连接器,本地启动后,发现控制台报错。

SLF4J: Failed to load class “org.slf4j.impl.StaticLoggerBinder”.
SLF4J: Defaulting to no-operation (NOP) logger implementation
SLF4J: See http://www.slf4j.org/codes.html#StaticLoggerBinder for further details.

第一次尝试:
我尝试添加slf4j-log4j12桥接器依赖

<dependency>
 <groupId>org.slf4j</groupId>
 <artifactId>slf4j-log4j12</artifactId>
 <version>1.7.5</version>
</dependency>

log4j.properties

log4j.rootLogger=INFO, console

# Uncomment this if you want to _only_ change Flink's logging
log4j.logger.org.apache.flink=WARN

# The following lines keep the log level of common libraries/connectors on
# log level INFO. The root logger does not override this. You have to manually
# change the log levels here.
log4j.logger.akka=WARN
log4j.logger.org.apache.kafka=INFO
log4j.logger.org.apache.hadoop=WARN
log4j.logger.org.apache.zookeeper=WARN

# Log all infos to the console
log4j.appender.console=org.apache.log4j.ConsoleAppender
log4j.appender.console.layout=org.apache.log4j.PatternLayout
log4j.appender.console.layout.ConversionPattern=%d{yyyy-MM-dd HH:mm:ss,SSS} %-5p %-60c %x - %m%n

# Suppress the irrelevant (wrong) warnings from the Netty channel handler
log4j.logger.org.apache.flink.shaded.akka.org.jboss.netty.channel.DefaultChannelPipeline=ERROR, console

二、环境上启动
将jar包放到yarn上执行,发现flink web ui上无法输出日志,打开log list中的taskmanager.err查看 slf4j 的桥接日志,发现报错了,下面三个桥接器冲突了。log4j-slf4j-impl-2.17.5 、log4j-slf4j-impl-2.17.1 、slf4j-log4j12-1.7.25。

报错信息

SLF4J: Class path contains multiple SLF4J bindings.
SLF4J: Found binding in [jar:file:/data/sdv1/yarn/nm/usercache/root/appcache/application_1652426634423_0034/filecache/28/hycan-flink-hycanvalue-job-1.0-SNAPSHOT1.jar!/org/slf4j/impl/StaticLoggerBinder.class]
SLF4J: Found binding in [jar:file:/data/sdv1/yarn/nm/usercache/root/appcache/application_1652426634423_0034/filecache/11/log4j-slf4j-impl-2.17.1.jar!/org/slf4j/impl/StaticLoggerBinder.class]
SLF4J: Found binding in [jar:file:/opt/cloudera/parcels/CDH-6.2.1-1.cdh6.2.1.p0.1425774/jars/slf4j-log4j12-1.7.25.jar!/org/slf4j/impl/StaticLoggerBinder.class]
SLF4J: See http://www.slf4j.org/codes.html#multiple_bindings for an explanation.
SLF4J: Actual binding is of type [org.slf4j.impl.Log4jLoggerFactory]
log4j:WARN No appenders could be found for logger (org.apache.flink.yarn.YarnTaskExecutorRunner).
log4j:WARN Please initialize the log4j system properly.
log4j:WARN See http://logging.apache.org/log4j/1.2/faq.html#noconfig for more info.

观察一下flink官方的二进制运行包中就自带了slf4j和log4j2的jar包:flink-dist带有有关slf4j的jar包,其余是log4j2的。

log4j-1.2-api-2.17.1.jar
log4j-api-2.17.1.jar
log4j-core-2.17.1.jar
log4j-slf4j-impl-2.17.1.jar
flink-dist-1.15.0.jar

也就是说我引入的jar包,flink官方的桥接器、cdh的桥接器是冲突的。因为flink官方使用的是log4j2的日志组件,所以需要移除我自己引入的包中的日志组件和cdh中的日志组件,应该就ok了。因此首先我将jar包中的org.slf4j.*排除。

            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-shade-plugin</artifactId>
                <version>3.2.1</version>
                <executions>
                    <!-- Run shade goal on package phase -->
                    <execution>
                        <phase>package</phase>
                        <goals>
                            <goal>shade</goal>
                        </goals>
                        <configuration>
                            <artifactSet>
                                <excludes>
                                    <exclude>org.slf4j:*</exclude>
                                    <exclude>org.apache.logging.log4j:*</exclude>
                                    <exclude>ch.qos.logback:*</exclude>
                                </excludes>
                            </artifactSet>
                        </configuration>
                    </execution>
                </executions>
            </plugin>

再次打包,生成*****-shaded.jar,执行后发现日志正常记录。再次打开taskmanager.err时,发现有如下报错

报错信息

SLF4J: Class path contains multiple SLF4J bindings.
SLF4J: Found binding in [jar:file:/data/sdv1/yarn/nm/usercache/root/appcache/application_1652426634423_0035/filecache/12/log4j-slf4j-impl-2.17.1.jar!/org/slf4j/impl/StaticLoggerBinder.class]
SLF4J: Found binding in [jar:file:/opt/cloudera/parcels/CDH-6.2.1-1.cdh6.2.1.p0.1425774/jars/slf4j-log4j12-1.7.25.jar!/org/slf4j/impl/StaticLoggerBinder.class]
SLF4J: See http://www.slf4j.org/codes.html#multiple_bindings for an explanation.
SLF4J: Actual binding is of type [org.apache.logging.slf4j.Log4jLoggerFactory]
log4j:WARN No appenders could be found for logger (org.apache.hadoop.metrics2.lib.MutableMetricsFactory).
log4j:WARN Please initialize the log4j system properly.
log4j:WARN See http://logging.apache.org/log4j/1.2/faq.html#noconfig for more info.

对比上面的第一次报错日志,此次减少了我的jar包引入导致的桥接器冲突,并且最终slf4j绑定的变成为org.apache.logging.slf4j.Log4jLoggerFactory

对比分析:

  • 正常的slf4j binding的类:org.apache.logging.slf4j.Log4jLoggerFactory
    不正常的slf4j binding的类:org.slf4j.impl.Log4jLoggerFactory

这明显是slf4j binding包冲突造成的,经过调查发现,前者是log4j2.x用的工厂实现类,而后者是兼容log4j1.x用的工厂类,他们分别位于:
log4j-slf4j-impl-2.17.1.jar
slf4j-log4j12-1.7.5.jar(第一次我自己引入的jar包中的)

根据一些参考文章我有了想法,如果我自己的jar包引入log4j-slf4j-impl桥接器依赖会怎样呢?是否还是会依赖冲突,但是日志正常打印呢?

第二次尝试:
之后我又进行了尝试,本地使用slf4j+log4j2,引入依赖

            <dependency>
                <groupId>org.slf4j</groupId>
                <artifactId>slf4j-api</artifactId>
                <version>${slf4j.version}</version>
            </dependency>
            <dependency>
                <groupId>org.apache.logging.log4j</groupId>
                <artifactId>log4j-api</artifactId>
                <version>${log4j.version}</version>
            </dependency>
            <dependency>
                <groupId>org.apache.logging.log4j</groupId>
                <artifactId>log4j-core</artifactId>
                <version>${log4j.version}</version>
            </dependency>
            <dependency>
                <groupId>org.apache.logging.log4j</groupId>
                <artifactId>log4j-slf4j-impl</artifactId>
                <version>${log4j.version}</version>
            </dependency>

log4j2.xml

<?xml version="1.0" encoding="UTF-8" ?>

<Configuration status="WARN" monitorInterval="600">
    <Appenders>
        <Console name="console_out_appender" target="SYSTEM_OUT">
            <!-- 输出日志的格式 -->
            <PatternLayout pattern="[%t] %d{MM/dd-HH:mm:ss,SSS} [%-5p] : %m (%F:%L)%n"/>
        </Console>
    </Appenders>
    <Loggers>
        <!-- 配置日志的根节点,打印trace等级及其以上等级的日志 -->
        <root level="info">
            <appender-ref ref="console_out_appender"/>
        </root>
    </Loggers>
</Configuration>

再次打包,生成*****-.jar,执行后发现日志正常记录。但是日志的报错变化了

报错信息

SLF4J: Class path contains multiple SLF4J bindings.
SLF4J: Found binding in [jar:file:/data/sdv1/yarn/nm/usercache/root/appcache/application_1652426634423_0034/filecache/28/hycan-flink-hycanvalue-job-1.0-SNAPSHOT.jar!/org/slf4j/impl/StaticLoggerBinder.class]
SLF4J: Found binding in [jar:file:/data/sdv1/yarn/nm/usercache/root/appcache/application_1652426634423_0035/filecache/12/log4j-slf4j-impl-2.17.1.jar!/org/slf4j/impl/StaticLoggerBinder.class]
SLF4J: Found binding in [jar:file:/opt/cloudera/parcels/CDH-6.2.1-1.cdh6.2.1.p0.1425774/jars/slf4j-log4j12-1.7.25.jar!/org/slf4j/impl/StaticLoggerBinder.class]
SLF4J: See http://www.slf4j.org/codes.html#multiple_bindings for an explanation.
SLF4J: Actual binding is of type [org.apache.logging.slf4j.Log4jLoggerFactory]
log4j:WARN No appenders could be found for logger (org.apache.hadoop.metrics2.lib.MutableMetricsFactory).
log4j:WARN Please initialize the log4j system properly.
log4j:WARN See http://logging.apache.org/log4j/1.2/faq.html#noconfig for more info.

果然,产生了三个jar包的冲突,因为我自己的jar包引入的桥接器是log4j-slf4j-impl的版本与flink官方的不同。最终slf4j绑定的类仍然是org.apache.logging.slf4j.Log4jLoggerFactory,所以本次依然能正常打印日志。

虽然大概了解了原因,但是仍然有一些疑问,为什么出现了jar包的冲突仍然能够正常打印日志呢?

其实,slf4j在运行时会打印所有在classpath里面发现的所有日志实现类,然后会选择第一个被类加载器加载的实现类作为底层的真正的日志组件,之后其他的实现类会被忽略,因为Java类加载器在加载多个同包名同类名的class的时候,只有第一个会成功,后面的不会被加载,这也是双亲委派模型的经典之处。

这就引出了,jvm加载jar的顺序问题,文章上是这么说的:

Java类加载器加载同一个目录下的jar包的顺序是随机的,会受操作系统的文件系统影响。
在包冲突的情况下(包括同jar不同版本和不同jar但存在同包名同类名的class),如果类加载器按照正常的顺序加载,是没有问题的,但如果恰好冲突的jar包,加载的顺序发生了颠倒,那么就极有可能引发莫名其妙的问题

jdk官网:同一个目录下,jvm加载jar包顺序是无法保证的,每个系统的都不一样,甚至同一个系统不同的时刻加载都不一样。良好设计的系统不应该依赖任何特定的加载顺序。

第三次尝试:
最终决定本地引入slf4j+log4j2依赖(第二次尝试中的方式),并且将有关slf4j和log4j的依赖排除:

            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-shade-plugin</artifactId>
                <version>3.2.1</version>
                <executions>
                    <!-- Run shade goal on package phase -->
                    <execution>
                        <phase>package</phase>
                        <goals>
                            <goal>shade</goal>
                        </goals>
                        <configuration>
                            <artifactSet>
                                <excludes>
                                    <exclude>org.slf4j:*</exclude>
                                    <exclude>org.apache.logging.log4j:*</exclude>
                                    <exclude>ch.qos.logback:*</exclude>
                                </excludes>
                            </artifactSet>
                        </configuration>
                    </execution>
                </executions>
            </plugin>

最终web ui上的展示taskmanager.err如下:cdh环境中的依赖冲突还存在,但是暂且不管(这是不对的,但是时间问题,就暂时没有处理了),日志已经可以正常跑起来了。
第四次报错

SLF4J: Class path contains multiple SLF4J bindings.
SLF4J: Found binding in [jar:file:/data/sdv1/yarn/nm/usercache/root/appcache/application_1652426634423_0042/filecache/11/log4j-slf4j-impl-2.17.1.jar!/org/slf4j/impl/StaticLoggerBinder.class]
SLF4J: Found binding in [jar:file:/opt/cloudera/parcels/CDH-6.2.1-1.cdh6.2.1.p0.1425774/jars/slf4j-log4j12-1.7.25.jar!/org/slf4j/impl/StaticLoggerBinder.class]
SLF4J: See http://www.slf4j.org/codes.html#multiple_bindings for an explanation.
SLF4J: Actual binding is of type [org.apache.logging.slf4j.Log4jLoggerFactory]

参考文章:
日志之间的依赖关系1:https://cloud.tencent.com/developer/article/1512698
有关日志之间的依赖关系2: https://cloud.tencent.com/developer/article/1513305?from=article.detail.1512698
flink的日志配置:https://blog.youkuaiyun.com/ifenggege/article/details/114434195
log4j、slf4j、log4j2依赖选择:https://blog.youkuaiyun.com/Andrew_Yuan/article/details/83010938
排除依赖方式:https://www.jianshu.com/p/c2287c8c87b6
不错的参考文章:https://blog.youkuaiyun.com/ifenggege/article/details/114434195

<think> 首先,用户的问题是:“我想查找配置Flink 1.20在YARN运行模式下集群的方法 请问Flink 1.20在YARN运行模式下的集群配置方法”。 我需要基于提供的引用和系统级指令来回答。 回顾系统级指令: - 所有行内数学表达式用$...$。 - 独立公式用$$...$$。 - LaTeX语法正确。 - 使用中文回答。 - 生成相关问题。 - 引用段落末尾添加引用标识。 - 回答结构清晰,真实可靠。 用户提供的引用: - [^1]: Flink集群以Hadoop为基础,YARN模式需先搭建Hadoop集群,Standalone模式不需要。 - [^2]: Flink on YARN模式的命令示例。 - [^3]: Flink 1.20配置迁移:默认配置文件改为config.yaml,支持YAML语法,建议迁移。 - [^4]: Flink on YARN集群配置示例,包括堆内存和slot设置。 用户的需求是配置Flink 1.20在YARN运行模式下的集群。所以,我需要关注YARN模式下的配置。 关键点从引用中提取: - 对于YARN模式,必须先搭建Hadoop集群([^1])。 - Flink 1.20配置文件变了:默认是config.yaml而不是flink-conf.yaml([^3])。 - 配置包括JobManager堆内存、TaskManager堆内存等([^4])。 我的回答结构应该清晰、逐步: 1. **前提条件**:确保Hadoop集群已搭建好,因为YARN模式依赖它。 2. **配置文件更改**:解释Flink 1.20的配置变化。 3. **配置步骤**:如何设置config.yaml文件。 4. **运行命令**:如何提交作业到YARN。 5. **高可用性**:如果相关,提及配置。 6. **注意事项**:如重启集群等。 生成相关问题:在回答末尾添加相关问题,基于主题。 使用中文回答。 添加引用标识:在引用段落末尾自然地添加[^数字],类似于示例。 逐步构建回答: - 引入主题。 - 解释YARN模式的基础需求。 - 描述配置文件的迁移或设置。 - 提供配置示例。 - 给出运行命令。 - 结尾时添加相关问题。 确保LaTeX正确:用户问题中没有数学表达式,因此可能不需要,但如果提到任何公式,就用指定格式。 最终输出格式: - 清晰段落。 - 代码块用于配置文件示例或命令。 - 引用标识。 - 相关问题。 相关问题想法: 1. 如何迁移旧配置到新格式? 2. Flink on YARN的最佳实践是什么? 3. 如何配置高可用性? 4. FlinkYARN上的常见问题如何解决? </think> 以下是针对配置 Apache Flink 1.20 在 YARN 运行模式下集群的详细方法。Flink on YARN 模式允许 Flink 应用程序在 Hadoop YARN 资源管理框架上运行,利用 YARN 的资源调度能力。本指南基于 Flink 1.20 的最新特性(如配置文件变更),并结合引用内容逐步说明。配置过程分为四个步骤:前提条件、集群配置文件设置、作业提交命令和相关注意事项。 ### 步骤 1: 准备前提条件 在配置 Flink on YARN 前,必须先搭建 Hadoop 集群(包括 HDFS 和 YARN 服务),因为 YARN 模式依赖 Hadoop 的基础设施。如果是全新安装,请参考 Hadoop 官方文档或相关教程(如 [^1] 提到的 "hadoop集群搭建")。确保以下条件: - Hadoop 版本兼容:Flink 1.20 支持 Hadoop 3.x,建议使用 Hadoop 3.3.0 或更高。 - 环境变量设置:在 Flink 节点上配置 `HADOOP_HOME` 和 `HADOOP_CONF_DIR` 环境变量,指向 Hadoop 的安装目录和配置文件路径(如 `export HADOOP_CONF_DIR=/path/to/hadoop/etc/hadoop`)。 - Flink 安装:下载 Flink 1.20 二进制包(从 Apache 官网),解压到集群所有节点(如 JobManager 和 TaskManager 节点)。引用提到:"flink集群是以hadoop集群为基础的,如果是yarn模式,先要搭建hadoop集群" [^1]。 ### 步骤 2: 配置 Flink 集群文件(核心步骤) Flink 1.20 引入了新的配置文件格式(`config.yaml`),取代了旧的 `flink-conf.yaml`,以支持更灵活的 YAML 语法(如嵌套结构)。引用指出:"在 Flink 1.20 版本中,默认的配置文件已从 flink-conf.yaml 改为 conf/config.yaml",建议优先使用 `config.yaml` 以避免兼容性问题 [^3]。如果已有旧配置,需先迁移: - **迁移旧配置(可选)**:如果节点上存在 `conf/flink-conf.yaml`,运行内置迁移脚本自动生成 `config.yaml`: ```bash bin/migrate-config-file.sh # 自动迁移旧配置,生成 config.yaml ``` 迁移后删除旧文件以避免冲突。 - **编辑 `config.yaml` 文件**:在 Flink 安装目录的 `conf/` 子目录下,创建或修改 `config.yaml`。Flink on YARN 模式无需指定 JobManager 和 TaskManager 的主机地址(由 YARN 动态分配),但需设置资源参数。以下是关键配置示例(基于引用 [^3] 和 [^4] 的推荐设置)。使用 YAML 嵌套格式或扁平格式(推荐嵌套以提高可读性): ```yaml # 示例:config.yaml 文件内容(嵌套格式) jobmanager: rpc: # 地址可选指定集群名,YARN 模式下通常留空或设为 localhost address: localhost heap: # JobManager 堆内存大小(单位 MB) size: 1024mb # 值来自引用 [^4]: "jobmanager.heap.mb: 1024" taskmanager: heap: # TaskManager 堆内存大小 size: 2048mb # 值来自引用 [^4]: "taskmanager.heap.mb: 2048" numberOfTaskSlots: 4 # 每个 TaskManager 的 slot 数,引用 [^4] 建议默认 4 # YARN 特定配置 yarn: application: # 应用名称(可选) name: "Flink on YARN Cluster" # 资源队列(可选) queue: "default" # 默认并行度 parallelism: default: 12 # 全局并行度设置,引用 [^4]: "parallelism.default: 12" # 可选:高可用性配置(如需 HA,添加 ZooKeeper 设置) high-availability: storageDir: "hdfs:///flink/ha" # HDFS 路径用于存储状态 zookeeper: quorum: "zk-host1:2181,zk-host2:2181" # ZooKeeper 集群地址 ``` **关键说明**: - **资源参数**:`jobmanager.heap.size` 和 `taskmanager.heap.size` 控制内存分配,单位应为 `mb` (如 `1024mb`),而非旧格式的 `mb` 后缀键名(引用 [^4] 的旧键名已更新为 YAML 格式)。 - **配置文件选择**:如果 `conf/flink-conf.yaml` 存在,Flink 1.20 会优先使用它,但强烈建议迁移到 `config.yaml` 以利用 YAML 优势(如类型安全和多级嵌套)。引用强调:"修改任一配置文件后需重启集群"[^3]。 - **高可用性**:若需配置 HA(如防止 JobManager 单点故障),添加 `high-availability` 部分(如上示例),并确保 HDFS 路径有效。 ### 步骤 3: 启动集群和提交作业 配置完成后,启动 Flink 集群无需手动启动 JobManager/TaskManager(由 YARN 管理)。提交作业使用 Flink 命令行工具: - **提交作业到 YARN**:在 Flink 安装目录下,运行以下命令。引用 [^2] 提供了一个示例命令: ```bash ./bin/flink run-application -t yarn-application -c <main-class> <jar-file> [args] ``` 例如: ```bash ./bin/flink run-application -t yarn-application -c com.example.MyFlinkJob flink-app-1.0.jar ``` 参数说明: - `-t yarn-application`: 指定 YARN 应用模式(支持 session 或 per-job 模式)。 - `-c <main-class>`: 作业的主类全名。 - `<jar-file>`: 应用程序 JAR 文件路径。 - **监控作业**:作业提交后,访问 YARN ResourceManager Web UI(如 `http://<yarn-rm-host>:8088`)查看 Flink 应用状态。 ### 步骤 4: 注意事项和验证 - **配置文件生效**:修改 `config.yaml` 后必须重启 Flink 相关进程(如果已运行),或重新提交作业。引用警告:"重启生效:修改任一配置文件后需重启集群"[^3]。 - **错误处理**:`config.yaml` 对重复键或非法配置会直接报错,确保语法正确(可使用 YAML lint 工具检查)。 - **性能调优**:根据集群规模调整资源参数。例如,增大 `taskmanager.heap.size` 或 `taskmanager.numberOfTaskSlots` 来处理高负载任务(引用 [^4] 建议起始值)。 - **验证方法**: 1. 运行测试作业:例如,提交 Flink 自带示例(如 `./bin/flink run-application -t yarn-application examples/streaming/WordCount.jar`)。 2. 检查日志:查看 YARN 应用日志(`yarn logs -applicationId <app-id>`)或 Flink Web UI(通过 YARN UI 获取 URL)。 ### 总结 配置 Flink 1.20 on YARN 的核心是:1)确保 Hadoop 集群就绪;2)使用 `config.yaml` 设置资源参数;3)通过 `flink run-application` 提交作业。新配置文件格式(`config.yaml`)提升了灵活性,但需注意迁移和语法细节。建议参考 Flink 官方文档进行扩展配置 [^1][^2][^3][^4]。
评论 1
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值