我在Spark上使用Python时遇到了问题。我的应用程序有一些依赖项,例如numpy,pandas,astropy等。我不能使用virtualenv来创建一个包含所有依赖项的环境,因为除了HDFS之外,集群上的节点没有任何公共mountpoint或文件系统。因此,我坚持使用spark-submit --py-files
。我将站点包的内容打包到ZIP文件中,并像--py-files=dependencies.zip
选项一样提交作业(如Easiest way to install Python dependencies on Spark executor nodes?中所述)。但是,群集上的节点似乎仍然没有看到内部的模块,并且在导入numpy时会抛出ImportError
这样的内容。
File "/path/anonymized/module.py", line 6, in <module>
import numpy
File "/tmp/pip-build-4fjFLQ/numpy/numpy/__init__.py", line 180, in <module>
File "/tmp/pip-build-4fjFLQ/numpy/numpy/add_newdocs.py", line 13, in <module>
File "/tmp/pip-build-4fjFLQ/numpy/numpy/lib/__init__.py", line 8, in <module>
#
File "/tmp/pip-build-4fjFLQ/numpy/numpy/lib/type_check.py", line 11, in <module>
File "/tmp/pip-build-4fjFLQ/numpy/numpy/core/__init__.py", line 14, in <module>
ImportError: cannot import name multiarray
当我切换到virtualenv并使用本地pyspark shell时,一切正常,所以依赖关系都在那里。有谁知道,什么可能导致这个问题以及如何解决它?
谢谢!
答案 0 :(得分:42)
首先,我假设您的依赖项列在requirements.txt
中。要打包和压缩依赖项,请在命令行运行以下命令:
pip install -t dependencies -r requirements.txt
cd dependencies
zip -r ../dependencies.zip .
上面,cd dependencies
命令对于确保模块位于zip文件的顶层是至关重要的。感谢Dan Corin's post提醒。
接下来,通过以下方式提交作业:
spark-submit --py-files dependencies.zip spark_job.py
--py-files
指令将zip文件发送给Spark工作人员,但不将其添加到PYTHONPATH
(对我来说是混乱的来源)。要将依赖项添加到PYTHONPATH
以修复ImportError
,请将以下行添加到Spark作业spark_job.py
:
sc.addPyFile("dependencies.zip")
来自this Cloudera post的警告:
假设任何人使用商品进行分布式计算 硬件必须假设底层硬件是潜在的 异质。在客户端计算机上构建的Python egg将是特定的 因为所需的C而导致客户端的CPU架构 汇编。为一个复杂的编译包分发一个鸡蛋 NumPy,SciPy或pandas是一个可能会失败的脆弱解决方案 大多数集群,至少最终。
虽然上述解决方案不会产生鸡蛋,但适用相同的指南。
答案 1 :(得分:10)
首先,您需要通过 - py-files 或 - 文件
传递您的文件现在,在您的代码中,使用以下命令添加这些zip /文件
sc.addPyFile("your zip/file")
现在使用以下别名导入您的代码中的zip /文件以开始引用它
import zip/file as your-alias
注意:导入时无需使用文件扩展名,例如 .py
希望这很有用。
答案 2 :(得分:4)
要使这种依赖分发方法与已编译的扩展配合使用,我们需要做两件事:
使用以下脚本创建依赖项zip将确保您与系统上已安装的任何软件包隔离。假定已安装virtualenv且当前目录中存在requirements.txt
,并输出一个dependencies.zip
,其中所有相关性都位于根级别。
env_name=temp_env
# create the virtual env
virtualenv --python=$(which python3) --clear /tmp/${env_name}
# activate the virtual env
source /tmp/${env_name}/bin/activate
# download and install dependencies
pip install -r requirements.txt
# package the dependencies in dependencies.zip. the cd magic works around the fact that you can't specify a base dir to zip
(cd /tmp/${env_name}/lib/python*/site-packages/ && zip -r - *) > dependencies.zip
现在可以像这样部署,解压缩依赖关系并将其包含在PYTHONPATH中
spark-submit \
--master yarn \
--deploy-mode cluster \
--conf 'spark.yarn.dist.archives=dependencies.zip#deps' \
--conf 'spark.yarn.appMasterEnv.PYTHONPATH=deps' \
--conf 'spark.executorEnv.PYTHONPATH=deps' \
.
.
.
spark.yarn.dist.archives = dependencies.zip#deps
分发您的zip文件并将其解压缩到名为deps
spark.yarn.appMasterEnv.PYTHONPATH = deps
spark.executorEnv.PYTHONPATH = deps
在PYTHONPATH中将deps
目录包含在master和所有worker的
-部署模式群集
在群集上运行主执行程序,以便它拾取依赖项
答案 3 :(得分:0)
您可以找到所需的所有.pys并相对添加。 有关此解释,请参阅here:
import os, sys, inspect
# realpath() will make your script run, even if you symlink it :)
cmd_folder = os.path.realpath(os.path.abspath(os.path.split(inspect.getfile( inspect.currentframe() ))[0]))
if cmd_folder not in sys.path:
sys.path.insert(0, cmd_folder)
# use this if you want to include modules from a subfolder
cmd_subfolder = os.path.realpath(os.path.abspath(os.path.join(os.path.split(inspect.getfile( inspect.currentframe() ))[0],"subfolder")))
if cmd_subfolder not in sys.path:
sys.path.insert(0, cmd_subfolder)
# Info:
# cmd_folder = os.path.dirname(os.path.abspath(__file__)) # DO NOT USE __file__ !!!
# __file__ fails if script is called in different ways on Windows
# __file__ fails if someone does os.chdir() before
# sys.argv[0] also fails because it doesn't not always contains the path
答案 4 :(得分:0)
Spark也会无法加载使用python zipfile
模块创建的zip存档。必须使用zip实用程序创建Zip存档。
答案 5 :(得分:0)
尝试使用--archives
将anaconda目录存档到每个服务器
并使用--conf spark.yarn.appMasterEnv.PYSPARK_PYTHON=
告诉您的spark服务器在anaconda目录中的python executor路径在哪里。
我们的完整配置是这样的:
--conf spark.yarn.appMasterEnv.PYSPARK_PYTHON=./ANACONDA/anaconda-dependencies/bin/python
--archives <S3-path>/anaconda-dependencies.zip#ANACONDA