【问题标题】:Why all these `HADOOP_HOME` and Winutils errors with Spark on Windows if Hadoop not used?如果不使用 Hadoop,为什么 Windows 上的 Spark 会出现所有这些“HADOOP_HOME”和 Winutils 错误?
【发布时间】:2022-11-11 18:30:10
【问题描述】:

我正在使用 Java 11 在 Windows 10 上运行 Spark 3.3.0。我没有使用 Hadoop。每次我运行某些东西时,都会出现如下错误:

java.lang.RuntimeException: java.io.FileNotFoundException: java.io.FileNotFoundException: HADOOP_HOME and hadoop.home.dir are unset. -see https://wiki.apache.org/hadoop/WindowsProblems
    at org.apache.hadoop.util.Shell.getWinUtilsPath(Shell.java:735)
    at org.apache.hadoop.util.Shell.getSetPermissionCommand(Shell.java:270)
    at org.apache.hadoop.util.Shell.getSetPermissionCommand(Shell.java:286)
    at org.apache.hadoop.fs.RawLocalFileSystem.setPermission(RawLocalFileSystem.java:978)

首先,连错误信息中的链接https://wiki.apache.org/hadoop/WindowsProblems都坏了。更新链接显然是https://cwiki.apache.org/confluence/display/HADOOP2/WindowsProblems,基本上说Hadoop需要Winutils。但我没有使用 Hadoop。我只是使用 Spark 在本地处理一些 CSV 文件。

其次,我希望我的项目使用 Maven 构建并使用纯 Java 运行,而不需要用户安装一些第三方软件。如果需要安装这个 Winutil 东西,它应该包含在一些 Maven 依赖项中。

如果我不使用 Hadoop,为什么需要所有这些 Hadoop/Winutils 的东西,以及如何绕过它,以便我的项目将在 Maven 中构建并像 Java 项目一样使用纯 Java 运行?

【问题讨论】:

  • 一种解决方案是在 Linux/WSL2 而不是 Windows CMD 中运行 Spark。仅在 Windows 上需要 Winutils 的全部原因是因为 Spark 依赖于 Unix 权限集
  • “仅在 Windows 上需要 Winutils 的全部原因是因为 Spark 依赖于 Unix 权限集”。 Java 处理 Unix 权限集就像 Java 7 一样很好。如果您深入研究代码,您会发现真正的问题是 RawLocalFileSystem假设获得它们的唯一方法是使用某些操作系统命令。那么为什么不改进RawLocalFileSystem 在Windows 上使用Java 呢?或者有没有办法告诉 Spark 使用使用纯 Java 的不同 Hadoop FileSystem 实现?创造力,人们!
  • 这是一个开源项目。提出拉取请求或打开 JIRA 而不是抱怨
  • “这是一个开源项目。提出拉取请求或打开 JIRA 而不是抱怨”。当然,我会很高兴。但我首先在这里问它是如何工作的以及选项是什么。唯一符合的是对“就是这样;不要试图研究解决方案”的任何态度,这是在重新打开这个问题之前最初关闭这个问题的态度。
  • 这很痛苦,如果有人想添加降级 hadoop 的 file:// 文件系统以不需要库的功能,我将帮助审查 hadoop PR。同时,为你的 hadoop 二进制文件构建一个 winutils.exe github.com/cdarlint/winutils

标签: java apache-spark hadoop


【解决方案1】:

Spark是一个替代品执行框架对于 mapreduce,而不是“Hadoop 替代品”。

Spark 使用 Hadoop 库进行文件系统访问,包括本地文件系统。如您的错误<strong>org.apache.hadoop</strong>.fs.RawLocalFileSystem所示

它还使用 winutils 作为一种 shim 来实现 Unix(POSIX?) chown/chmod 命令来确定 Windows 目录顶部的文件权限。

告诉 Spark 使用与 RawLocalFileSystem 不同的文件系统实现?

是的,使用与默认 file:// 不同的 URI

例如。 spark.csv("nfs://path/file.csv")

或 s3a 或安装 HDFS,或 GlusterFS 等用于分布式文件系统。毕竟 Spark 是分布式处理引擎;如果您只处理小型本地文件,它不是最好的工具。

【讨论】:

  • “Spark 使用 Hadoop 库来访问文件系统,包括本地文件系统。”那么,我是否可以通过一些 Maven 坐标添加一些最小的 Hadoop 依赖项,以访问文件系统而无需安装 Winutils?
  • 不,我认为 Winutils 是一个 C 库。 Spark 拉取它需要的所有 Java 依赖项
  • 更具体地说,正如 winutils 自述文件中所述,它没有与 Spark(或 Hadoop)一起分发,因为它可能有自己的漏洞,而且许多 Apache 项目根本没有 Windows 构建系统/测试。
  • “是的,使用与默认 file:// 不同的 URI”啊,现在我们到了某个地方。那么nfs: 映射到什么 Hadoop FileSystem 实现?映射在哪里,如果我想通过 Spark 可以更改它们? “……如果你只处理小的本地文件,它不是最好的工具。”好吧,也许我想在我的笔记本电脑上编写一个大型版本的原型,然后再将它部署到云中。
  • 我不确定 NFS,但我知道 http/s 和 s/ftp 在 Hadoop 源代码库 github.com/apache/hadoop/…
【解决方案2】:

有一个长期存在的 JIRA ......对于在笔记本电脑上独立运行 spark 的任何人,都不需要提供这些 posix 权限。在那儿

LocalFS to support ability to disable permission get/set; remove need for winutils

这与HADOOP-13223 winutils.exe is a bug nexus and should be killed with an axe 有关。 这是只要人们在遇到这个问题的窗户上运行火花,没有人投入工作来解决它。如果有人是,我将帮助审查/培养。

【讨论】:

  • 感谢这些参考资料,史蒂夫。几天后,我将尝试深入研究这一点,并就实际实施的票证本身做出进一步回应。
  • 最容易获取构建的 exe;它只是很难构建它不是通过 ASF 版本完成的。无情的是,linux arm64 二进制文件具有更高的优先级,因为它确实在生产中使用,从而大规模降低了成本。
  • “最容易获取构建的 exe”。但是,为什么最新的技术会迫使我使用降低项目安全性并破坏我的“只使用mvn install build”的工具呢?为什么我们不能把事情做对?我喜欢你关于更好地处理门票的想法,我已经打算研究它们,看看我是否可以做出贡献。让我们不要进一步这些kludges。
  • 为了实现 YARN 安全下载所需的安全性,然后运行工件,它需要 posix 权限; winutlls 提供了这一点,并且在 java native 添加 API 之前就已经这样做了。目前没有人在 hadoop 上部署纱线集群,但一位 MSFT 工程师再次接受了这一点。对于通过 spark 使用 file:// URL,不需要该功能,它应该是可选的,IMO。
  • “……因为在 java native 添加 API 之前……”但是 Java 现在已经添加了 API。此外,这张票的重点不是 Hadoop,而是 Spark。您可能会说“Spark 使用 Hadoop 的 FileSystem API”。但这不是必须的。如果是这样,它就不必使用这个特定的FileSystem 实现,或者以这种方式。
【解决方案3】:

TL;博士

我创建了 Hadoop FileSystem 的本地实现,它绕过了 Windows 上的 Winutils(并且确实应该在任何 Java 平台上工作)。 GlobalMentor Hadoop Bare Naked Local FileSystem 源代码在 GithUb 上可用,可以指定为来自 Maven Central 的依赖项。

  1. 如果您的应用程序需要 Hadoop 本地 FileSystem 支持而不依赖 Winutils,请将最新的 com.globalmentor:hadoop-bare-naked-local-fs 库导入您的项目,例如在 v0.1.0 的 Maven 中:
    <dependency>
      <groupId>com.globalmentor</groupId>
      <artifactId>hadoop-bare-naked-local-fs</artifactId>
      <version>0.1.0</version>
    </dependency>
    
    1. 然后指定您希望将裸本地文件系统实现com.globalmentor.apache.hadoop.fs.BareLocalFileSystem 用于file 方案。 (BareLocalFileSystem 内部使用 NakedLocalFileSystem。)下面的示例在 Java 中为 Spark 执行此操作:
    SparkSession spark = SparkSession.builder().appName("Foo Bar").master("local").getOrCreate();
    spark.sparkContext().hadoopConfiguration().setClass("fs.file.impl", BareLocalFileSystem.class, FileSystem.class);
    

    请注意,您可能仍会收到“HADOOP_HOME 和 hadoop.home.dir 未设置”和“未找到 winutils.exe”的警告。这是因为 Winutils 组件渗透到 Hadoop 代码中并且在低级别进行硬编码,在类加载时静态执行,即使对于与文件访问完全无关的代码也是如此。更多解释可以在project page on GitHub 上找到。另见HADOOP-13223: winutils.exe is a bug nexus and should be killed with an axe.)

    Spark 如何使用 Hadoop FileSystem

    Spark 使用 Hadoop FileSystem API 作为将输出写入磁盘的一种方式,例如用于本地 CSV 或 JSON 输出。它引入了整个 Hadoop 客户端库(当前为 org.apache.hadoop:hadoop-client-api:3.3.2),其中包含各种 FileSystem 实现。这些实现使用 Java service loader framework 为多个方案自动注册多个实现,其中包括:

    • org.apache.hadoop.fs.LocalFileSystem
    • org.apache.hadoop.fs.viewfs.ViewFileSystem
    • org.apache.hadoop.fs.http.HttpFileSystem
    • org.apache.hadoop.fs.http.HttpsFileSystem
    • org.apache.hadoop.hdfs.DistributedFileSystem

    这些文件系统中的每一个都指示它支持哪种方案。特别是org.apache.hadoop.fs.LocalFileSystem 表示它支持file 方案,并且默认使用它来访问本地文件系统。它又在内部使用org.apache.hadoop.fs.RawLocalFileSystem,这是最终负责需要Winutils 的FileSystem 实现。

    但是可以覆盖 Hadoop 配置并指定另一个 FileSystem 实现。 Spark 在 org.apache.spark.sql.internal.SessionState.newHadoopConf(…) 中为 Hadoop 创建了一个特殊的 Configuration,最终结合所有源 core-default.xmlcore-site.xmlmapred-default.xmlmapred-site.xmlyarn-default.xmlyarn-site.xml__spark_hadoop_conf__.xml,如果有的话当下。然后 Hadoop 的 FileSystem.getFileSystemClass(String scheme, Configuration conf) 查找要使用的 FileSystem 实现,方法是查找 fs.${scheme}.impl 形式的方案配置(在本例中为 file)(即在本例中为 fs.file.impl)。

    因此,如果您想指定要使用的另一个本地文件系统实现,您需要以某种方式将fs.file.impl 放入配置中。如果您以编程方式访问 Spark,则无需创建本地配置文件,而是可以通过 Spark 会话进行设置,如简介中所述。

    为什么选择 Winutils

    Hadoop FileSystem API 在很大程度上假设一个 *nix 文件系统。当前 Hadoop 本地 FileSystem 实现使用本机 *nix 库或打开 shell 进程并直接运行 *nix 命令。当前 Windows 的本地 FileSystem 实现与一个巨大的杂物一起出现:一组名为 Winutils 的二进制工件,由 Hadoop 贡献者创建,在 Windows 上提供了一个特殊的后门子系统,Hadoop 可以访问该后门子系统,而不是 *nix 库和外壳命令。 (见HADOOP-13223: winutils.exe is a bug nexus and should be killed with an axe.

    然而,Winutils 的检测和所需支持实际上是在 Hadoop 中的低级硬编码——即使在与文件系统无关的代码中!例如,当 Spark 启动时,即使是 Hadoop 代码中的一个简单的 Configuration 初始化也会调用 StringUtils.equalsIgnoreCase("true", valueString),而 StringUtils 类有一个对 Shell 的静态引用,它有一个静态初始化块,用于查找 Winutils 并生成如果未找到,则发出警告。 ?‍♂️(事实上,这就是警告的来源,而这正是 Stack Overflow 问题的最初动机。)

    在没有 Winutils 的情况下使用 FileSystem 的解决方法

    不管警告如何,更大的问题是让FileSystem 在不需要 Winutils 的情况下工作。自相矛盾的是,这既是一个比最初出现的更简单又复杂得多的项目。一方面,使用更新的 Java API 调用而不是 Winutils 来访问本地文件系统并不难;我已经在GlobalMentor Hadoop Bare Naked Local FileSystem 中做到了。但是完全淘汰 Winutils 更加复杂和困难。当前的LocalFileSystemRawLocalFileSystem 实现是随意发展的,中间实现的功能散布在各处,用于记录不良的极端情况的特殊情况代码,以及渗透到设计本身的实现特定假设。

    上面已经给出了 Configuration 访问 Shell 并尝试在启动期间加载类时引入 Winutils 的示例。在FileSystem 级别,与 Winutils 相关的逻辑不包含在 RawLocalFileSystem 中,这将允许它轻松被覆盖,而是依赖于静态 FileUtil 类,它就像一个单独的文件系统实现,它依赖于Winutils 并且不能修改。例如这里是需要更新的FileUtil 代码,不幸的是独立于FileSystem 实现:

      public static String readLink(File f) {
        /* NB: Use readSymbolicLink in java.nio.file.Path once available. Could
         * use getCanonicalPath in File to get the target of the symlink but that
         * does not indicate if the given path refers to a symlink.
         */
        …
        try {
          return Shell.execCommand(
              Shell.getReadlinkCommand(f.toString())).trim();
        } catch (IOException x) {
          return "";
        }
    

    显然有许多方法的“基于Stat 的新实现”,但RawLocalFileSystem 反而使用了不推荐使用的实现,例如DeprecatedRawLocalFileStatus,它充满了变通方法和特殊情况,是包私有的,所以它不能可以被子类访问,但由于HADOOP-9652 而不能被删除。 useDeprecatedFileStatus 开关是硬编码的,因此它不能被子类修改,从而强制重新实现它所接触的所有内容。换句话说,即使是新的、不那么繁琐的方法在代码中也被关闭了,已经有好几年了,而且似乎没有人在意。

    概括

    总而言之,Winutils 在整个代码中都在低级别进行了硬编码,即使在与文件访问无关的逻辑中也是如此,并且当前的实现是由硬编码标志打开或关闭的已弃用和未弃用代码的大杂烩。当新更改出现错误时到位。这是一团糟,多年来一直如此。没有人真正关心,而是继续在不稳定的沙地上(ViewFs 任何人?)而不是回去修复基础。如果 Hadoop 甚至无法修复大量已弃用的文件访问代码合并在一个地方,您认为他们会修复在低级别渗透到多个类的 Winutils 混杂吗?

    我没有屏住呼吸。相反,我会满足于我编写的解决方法,它通过 Java API 写入文件系统,尽可能绕过 Winutils。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2019-09-17
    • 1970-01-01
    • 1970-01-01
    • 2012-03-30
    • 1970-01-01
    • 2011-08-09
    • 2013-12-30
    • 1970-01-01
    相关资源
    最近更新 更多