pyspark中的--files选项不起作用

gok*_*oks 11 hadoop-yarn apache-spark pyspark

我尝试了命令行中的sc.addFile选项(没有任何问题)和--files选项(失败).

运行1:spark_distro.py

from pyspark import SparkContext, SparkConf
from pyspark import SparkFiles

def import_my_special_package(x):
    from external_package import external
    ext = external()
    return ext.fun(x)

conf = SparkConf().setAppName("Using External Library")
sc = SparkContext(conf=conf)
sc.addFile("/local-path/readme.txt")
with open(SparkFiles.get('readme.txt')) as test_file:
    lines = [line.strip() for line in test_file]
print(lines)
int_rdd = sc.parallelize([1, 2, 4, 3])
mod_rdd = sorted(int_rdd.filter(lambda z: z%2 == 1).map(lambda x:import_my_special_package(x)))
Run Code Online (Sandbox Code Playgroud)

外部包:external_package.py

class external(object):
    def __init__(self):
        pass
    def fun(self,input):
        return input*2
Run Code Online (Sandbox Code Playgroud)

readme.txt文件

MY TEXT HERE
Run Code Online (Sandbox Code Playgroud)

spark-submit命令

spark-submit \
  --master yarn-client \
  --py-files /path to local codelib/external_package.py  \
  /local-pgm-path/spark_distro.py  \
  1000
Run Code Online (Sandbox Code Playgroud)

输出:按预期工作

['MY TEXT HERE']
Run Code Online (Sandbox Code Playgroud)

但是,如果我尝试使用--files(而不是sc.addFile)选项从命令行传递文件(readme.txt),则它会失败.如下.

运行2:spark_distro.py

from pyspark import SparkContext, SparkConf
from pyspark import SparkFiles

def import_my_special_package(x):
    from external_package import external
    ext = external()
    return ext.fun(x)

conf = SparkConf().setAppName("Using External Library")
sc = SparkContext(conf=conf)
with open(SparkFiles.get('readme.txt')) as test_file:
    lines = [line.strip() for line in test_file]
print(lines)
int_rdd = sc.parallelize([1, 2, 4, 3])
mod_rdd = sorted(int_rdd.filter(lambda z: z%2 == 1).map(lambda x: import_my_special_package(x)))
Run Code Online (Sandbox Code Playgroud)

external_package.py与上面相同

火花提交

spark-submit \
  --master yarn-client \
  --py-files /path to local codelib/external_package.py  \
  --files /local-path/readme.txt#readme.txt  \
  /local-pgm-path/spark_distro.py  \
  1000
Run Code Online (Sandbox Code Playgroud)

输出:

Traceback (most recent call last):
  File "/local-pgm-path/spark_distro.py", line 31, in <module>
    with open(SparkFiles.get('readme.txt')) as test_file:
IOError: [Errno 2] No such file or directory: u'/tmp/spark-42dff0d7-c52f-46a8-8323-08bccb412cd6/userFiles-8bd16297-1291-4a37-b080-bbc3836cb512/readme.txt'
Run Code Online (Sandbox Code Playgroud)

sc.addFile--file用于相同的目的?有人可以分享你的想法.

des*_*aut 10

我终于找到了问题,这确实是一个非常微妙的问题.

如所怀疑的那样,这两个选项(sc.addFile--files)并不相同,而且(无可否认)非常巧妙地暗示了文档(强调增加):

addFile(path,recursive = False)
在每个节点上添加要使用此Spark作业下载的文件.

--filesFILES以
逗号分隔的文件列表,放在每个执行程序的工作目录中.

用简单的英语,虽然添加的文件sc.addFile对执行程序和驱动程序都可用,但添加的文件--files只对执行程序可用; 因此,当试图从驱动程序访问它们时(如OP中的情况),我们得到一个No such file or directory错误.

让我们证实了这一点(摆脱所有不相干的--py-files,并1000在OP的东西):

test_fail.py:

from pyspark import SparkContext, SparkConf
from pyspark import SparkFiles

conf = SparkConf().setAppName("Use External File")
sc = SparkContext(conf=conf)
with open(SparkFiles.get('readme.txt')) as test_file:  
    lines = [line.strip() for line in test_file]
print(lines)
Run Code Online (Sandbox Code Playgroud)

测试:

spark-submit --master yarn \
             --deploy-mode client \
             --files /home/ctsats/readme.txt \
             /home/ctsats/scripts/SO/test_fail.py
Run Code Online (Sandbox Code Playgroud)

结果:

[...]
17/11/10 15:05:39 INFO yarn.Client: Uploading resource file:/home/ctsats/readme.txt -> hdfs://host-hd-01.corp.nodalpoint.com:8020/user/ctsats/.sparkStaging/application_1507295423401_0047/readme.txt
[...]
Traceback (most recent call last):
  File "/home/ctsats/scripts/SO/test_fail.py", line 6, in <module>
    with open(SparkFiles.get('readme.txt')) as test_file:
IOError: [Errno 2] No such file or directory: u'/tmp/spark-8715b4d9-a23b-4002-a1f0-63a1e9d3e00e/userFiles-60053a41-472e-4844-a587-6d10ed769e1a/readme.txt'
Run Code Online (Sandbox Code Playgroud)

在上面的脚本中test_fail.py,是请求访问该文件的驱动程序readme.txt; 让我们更改脚本,以便为执行者(test_success.py)请求访问:

from pyspark import SparkContext, SparkConf

conf = SparkConf().setAppName("Use External File")
sc = SparkContext(conf=conf)

lines = sc.textFile("readme.txt") # run in the executors
print(lines.collect())
Run Code Online (Sandbox Code Playgroud)

测试:

spark-submit --master yarn \
             --deploy-mode client \
             --files /home/ctsats/readme.txt \
             /home/ctsats/scripts/SO/test_success.py
Run Code Online (Sandbox Code Playgroud)

结果:

[...]
17/11/10 15:16:05 INFO yarn.Client: Uploading resource file:/home/ctsats/readme.txt -> hdfs://host-hd-01.corp.nodalpoint.com:8020/user/ctsats/.sparkStaging/application_1507295423401_0049/readme.txt
[...]
[u'MY TEXT HERE']
Run Code Online (Sandbox Code Playgroud)

另请注意,这里我们不需要SparkFiles.get- 文件易于访问.

如上所述,sc.addFile在两种情况下都可以工作,即当驱动程序或执行程序请求访问时(已测试但未在此处显示).

关于命令行选项的顺序:正如我在其他地方所论述的那样,所有与Spark相关的参数必须在要执行的脚本之前; 可以说,相对顺序--files--py-files无关(将其作为练习).

使用Spark 1.6.02.2.0进行测试.

更新(评论后):似乎我的fs.defaultFS设置也指向HDFS:

$ hdfs getconf -confKey fs.defaultFS
hdfs://host-hd-01.corp.nodalpoint.com:8020
Run Code Online (Sandbox Code Playgroud)

但是让我关注这里的森林(而不是树木),并解释为什么整个讨论只有学术兴趣:

使用标志传递要处理的文件--files是不好的做法; 事后来看,我现在可以看到为什么我几乎找不到在线使用的参考资料 - 可能没有人在实践中使用它,而且有充分的理由.

(请注意,我不是在说--py-files,这是一个不同的合法角色.)

由于Spark是一个分布式处理框架,运行在集群和分布式文件系统(HDFS)上,因此最好的办法是将所有文件都处理到HDFS中 - 期间.Spark处理文件的"自然"位置是HDFS,而不是本地FS - 尽管有一些玩具示例仅使用本地FS进行演示.更重要的是,如果您希望将来有一些时间将部署模式更改为cluster,您将发现默认情况下,群集对本地路径和文件一无所知,而且理所当然......

  • 太棒了 :) (2认同)