(一)场景问题
1)我在本地pycharm项目分支下运行文件,运行方式是:先cd到项目根目录,然后再运行本地提交命令;现在把该部分代码打包上传到线上,直接在命令行运行,就会报no module named XXX错误;
本地目录:
解决思路1
因为考虑到之前,spark-submit提交的时候,运行的py文件是直接在根目录下,并没有像这样嵌套在多级子目录里面;因此考虑的解决方式是:在根目录下面建一个调度文件,类似于main.py文件一样;看能否解决问题,但其实发现不能解决;但这种思路能解决另一种导入问题(见后续讨论-2019-3-20的Python包导入问题);调试了一天,将clean_utils_gz.py移到根目录下,或者将运行的py文件里面import clean_utils_gz as cl改成from biz.t14.sub.clean_data.data_clean import clean_utils_gz as cl;还有尝试将文件打包成zip包,添加进–py-files参数;还有尝试添加进sc.addfiles()里面,都没有解决,直到下方思路2才解决;
解决思路2
用以下提交方式完美运行:
spark-submit --master spark://192.168.31.10:7077 --executor-memory 120G --total-executor-cores 40 --py-files /data3/test/gd_data_code/clean_utils_gz.py /data3/test/gd_data_code/biz/t14/sub/clean_data/data_clean/clean_saic_part9_com_releated.py
注意两点:
spark-submit --master spark://192.168.31.10:7077 --executor-memory 120G --total-executor-cores 40 --py-files /data3/test/gd_data_code/clean_utils_gz.py /data3/test/gd_data_code/t14_main_update.py
还是会报导入包错误:
ImportError: ('No module named biz.t14.sub.clean_data.data_clean.clean_utils_gz', , ('biz.t14.sub.clean_data.data_clean.clean_utils_gz',))
思路2解决的具体原因有待再详究
(2)pyspark里面spark-submit提交任务各种包依赖的情况
在spark上运行Python脚本遇到“ImportError: No module name xxxx”
这是因为运行Python脚本的集群上的Python环境里缺乏脚本运行需要的依赖。
根据所需依赖的不同性质可以分为3类:
(1)单个的*.py或者*.py[co]
(2)自建模块包依赖
(3)自包含的依赖项
(4)复杂依赖
【1】只依赖于单个文件(only depend on a sigle file)
/opt/spark/bin/spark-submit --master yarn-cluster --py-files dependency.py my_script.py
sc = SparkContext(master=”yarn-cluster”,appName=”myApp”)
sc.addPyFile(file_path)
【2】自建模块包依赖
比如在Python脚本中使用了:
from model.file import
那么就需要将model文件夹进行打包。注意打包指令要在文件所在的父目录下进行。比如文件夹model的路径为/home/workspace/model,那么就要保证打包命令是在/home/workspace/下进行的,但这样打包会将workspace文件夹下的文件全部打包。
zip -r ../my_dependencies.zip .
备注:这样操作的原因是,要确保所需文件在*.zip的顶层(ensure that the modules are the in the top level of the zip file),即当解压缩deps.zip时,文件和文件夹必须出现在顶层。当在spark中执行任务时,相关的依赖包会以如下方式进行查找:
/hadoopdir3/yarn/…/my_dependencies.zip/model/file/__init__.py
###备注;文件和文件夹必须出现在顶层的含义:/my_dependencies.zip/解压后即为根目录my_dependencies;model路径就在.../my_dependencies/model下;
两种打包命令的区别:
正确的:
$ zip -r ../deps.zip .
adding: __init__.py (stored 0%)
adding: _markerlib/ (stored 0%)
adding: _markerlib/__init__.py (deflated 55%)
adding: _markerlib/__init__.pyc (deflated 60%)
adding: _markerlib/markers.py (deflated 63%)
adding: _markerlib/markers.pyc (deflated 61%)
adding: aniso8601/ (stored 0%)
...
错误的:
$ zip -r deps.zip folder_name/*
adding: folder_name/__init__.py (stored 0%)
adding: folder_name/_markerlib/ (stored 0%)
adding: folder_name/_markerlib/__init__.py (deflated 55%)
adding: folder_name/_markerlib/__init__.pyc (deflated 60%)
adding: folder_name/_markerlib/markers.py (deflated 63%)
adding: folder_name/_markerlib/markers.pyc (deflated 61%)
adding: folder_name/aniso8601/ (stored 0%)
...
打包好之后,就可以按照【1】中分发单个文件给集群的方法将.zip文件分发给集群。
/opt/spark/bin/spark-submit --master yarn-cluster --py-files my_dependency.zip my_script.py
或者
sc = SparkContext(master=”yarn-cluster”,appName=”myApp”)
sc.addPyFile(file_path)
【3】自包含的依赖项(a self contained module (meaning a module with no other dependencies))
如果所需依赖是Python的第三方模块,可以通过virtualenv创建一个独立的Python运行环境,然后在这个Python运行环境里安装各种第三方包。然后将安装的第三方包打包成*.zip或者*.egg。
详细步骤如下:
第一步,用pip安装virtualenv
#Python2版本
pip2 install virtualenv
或者 Python3
pip3 install virtualenv
第二步,创建一个独立的Python运行环境,命名为pyspark-env
virtual –no-site-packages pyspark-env
#–no-site-packages,可以使已经安装的系统Python环境中的所有第三方包不会复制过来,这样可以得到一个不带任何第三方包的“干净的”的Python运行环境。
第三步,用source进入该环境
source pyspark-env/bin/activate
#使用deactivate 命令退出当前env环境
deactivate
可以发现,命令提示符变了,有个pyspark-env前缀,表示当前环境使一个名为pyspark-env的Python环境。
第四步,正常安装各种第三方包
pip install numpy
在pyspark-env环境下,用pip安装的包都被安装到pyspark-env这个环境下,系统Python环境不受任何影响。
第五步,打包安装的第三方包
所有的第三方的包都会被pip安装到lib/python2/site-packages目录下面。
进入site-packages目录下面,
cd ./lib/python2.7/site-packages
zip -r /zip-path/my_dependencies.zip
同样也要注意,必须在所需包的父目录下进行打包,使文件和文件夹作为*.zip的顶层文件。
打包好之后,就可以通过
spark-submit –master yarn-cluster –py-files my_dependency.zip my_script.py
或者通过在脚本中sparkcontext的属性添加
sc.addfiles(“path/my_dependency.zip”)
将依赖库分发到各节点。
警告:如果您的软件包依赖于已编译的代码,并且您的集群中的计算机具有与您编译egg的代码不同的CPU体系结构,则这将不起作用。
比如Numpy,通过这种方式没有解决依赖的问题,最后是让集群管理员直接在集群上安装了Numpy。
主要原因是Python不允许动态导入.so文件,而Numpy由于是C编译的,存在*.so文件。
4】复杂依赖(Complex Dependency)
要用的包依赖于其他的依赖项
比如Pandas依赖于NumPy和SciPy,以及其他包。
理论上,我们可以为所需的依赖包创建一个*.egg文件,然后通过命令行的—py-files选项,或者sc.addPyFiles()方法 将依赖文件传送给执行器。
但是对于复杂依赖,这种方法很脆弱:
A Python egg built on a client machine will be specific to the client’s CPU architecture because of the required C compilation. Distributing an egg for a complex, compiled package like NumPy, SciPy, or pandas is a brittle solution that is likely to fail on most clusters, at least eventually.
参考:
https://stackoverflow.com/questions/29495435/easiest-way-to-install-python-dependencies-on-spark-executor-nodes
https://stackoverflow.com/questions/35214231/importerror-no-module-named-numpy-on-spark-workers
https://www.cloudera.com/documentation/enterprise/5-5-x/topics/spark_python.html
https://blog.cloudera.com/blog/2015/09/how-to-prepare-your-apache-hadoop-cluster-for-pyspark-jobs/
https://stackoverflow.com/questions/36461054/i-cant-seem-to-get-py-files-on-spark-to-work
知识补充这部分转载自:https://blog.csdn.net/wangxiao7474/article/details/81391300