Spark standalone模式在多用户环境下保存结果报错 java.io.ioexception: mkdirs failed to create file

0. 前情提要

在服务器上用standalone模式部署了Spark,使用本地文件系统(没有使用HDFS),在多用户提交Spark application计算任务并保存结果到本地文件系统时出现问题,保存时提示:

java.io.IOException: mkdirs failed to create file

java.io.IOException: Failed to rename DeprecatedRawLocalFileStatus

因为很少有像我这么“奇葩”的使用场景(单机Standalone+本地文件系统+多用户),所以花了两三个小时搜索研究才最终解决。

1. 解决方案

不想看完整问题分析的同学可以直接采用下面的解决方案:

  1. 关闭Spark
  2. 使root用户执行start-master.shstart-worker.sh启动Spark
  3. 在创建SparkSession时,将spark.hadoop.fs.permissions.umask-mode设置为000。可以在Spark安装目录的conf/spark-defaults.conf文件内设置;以pySpark为例,也可以通过这种方式在运行时设置:
spark = SparkSession \
    .builder \
    .master("spark://youraddress:7077") \
    .config("spark.hadoop.fs.permissions.umask-mode", "000") \
    .appName("yourname") \
    .getOrCreate()
  1. 如果你不存在多用户使用的场景,可以忽略第2、3步,使用相同的用户启动Spark和提交Spark任务即可

感兴趣的同学可以接着往下看前因后果,不感兴趣的可以右上角了

2. 系统环境

  • 单台服务器:68 vCPUs + 600GB RAM
  • CentOS 8
  • Spark 3.1.2 (Standalone模式,使用本地文件系统,无HDFS)
  • JupyterHub(支持多用户的Jupyter notebook)
  • pySpark

3. 问题描述

用户可以正常执行Spark计算,而一旦将Spark结果输出到本地文件系统时,就会报错,比如执行df.write.csv('result')。(注意: 这里的df是Spark DataFrame,不是pandas DataFrame,如果Spark driver有足够内存将结果转换为pandas DataFrame再保存,是不会出现这个问题的)

报错分为两种情况:

  • 启动Spark的为非root用户时:java.io.IOException: mkdirs failed to create file
  • 启动Spark的为root时:java.io.IOException: Failed to rename DeprecatedRawLocalFileStatus

会导致结果保存过程中止,无法导出计算结果

4. 问题分析

问题的根本原因在于启动Spark的用户和使用Spark进行计算的用户不同,其他用户创建的文件和文件夹无法被当前用户修改:

  • 在Spark保存结果时,首先会以提交计算任务的用户(driver)创建一个文件夹(比如上文提到的result),该文件夹的权限为755(rwxr-xr-x),其他非权限用户不具有写权限
  • 然后,各个executor会以启动Spark的用户在上述文件夹中写入临时结果,如果启动Spark的用户不是root,就无法写入,报第一个错误
  • 如果启动Spark的用户是root,那么临时结果可以被写入,最后需要由提交计算任务的用户(driver)来重新组织。由于这些临时文件的所有者是root,所以driver没有办法修改,报第二个错误

5. 解决思路

解决问题的关键在于,在启动和提交Spark计算用户不相同的前提下,让双方创建的文件、文件夹都能够被双方修改。问题似乎很简单,设置一下umask或者ACL不就好了吗?于是我开始了一些失败的尝试:

  • 将保存位置的默认ACL文件夹权限设置为所有人可读写
  • 将用户的umask设置为000(所有人可读写)

结果发现,在保存Spark结果的时候,这些设置都没有生效,生成的文件权限依然为755
这说明Spark有自己的一套配置覆盖了上面的默认配置,那么这个配置在哪呢?

答案是spark.hadoop.fs.permissions.umask-mode,其默认值为022,所以生成的文件权限依然为755(感谢这位答主)。

然而,我以为我的使用环境(本地文件系统)不适用这个配置项,所以刚开始看到这个答案的时候不以为然直接忽略了…… 后来又折腾了几个小时,抱着试一试的心态,发现居然成功了

6. 仍存在的问题

  • 首先肯定是安全性的问题,用户的文件理应只有用户自己能修改(或者至少是组成员)。因为这台服务器的用户都是可信的,所以图省事我把umask-mode改成了000,稍微安全一点的做法是改成002,然后建立一个(或多个)新组把Spark启动用户和计算用户加入进去
  • 即使umask-mode改成了000,如果启动Spark的用户不是root,依然会报第一个错误,原因不明
  • 有条件、不怕麻烦的话大家还是上HDFS或者其他文件系统吧,起码出了问题后能搜到的相关内容都更多些

谨以我昨晚的搜索记录纪念一下这次debug:
在这里插入图片描述

参考资料

  1. https://spark.apache.org/docs/latest/configuration.html
  2. https://stackoverflow.com/questions/43077881/spark-how-to-write-files-with-a-given-permission
  3. https://stackoverflow.com/questions/51769375/how-to-force-spark-hive-to-create-task-directories-with-custom-permissions
  4. https://www.mail-archive.com/user@spark.apache.org/msg28820.html
  5. https://stackoverflow.com/a/35987436/6059213
  • 6
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值