06. PySpark Python版本和依赖支持 - aliyun/MaxCompute-Spark GitHub Wiki
很多情况下pyspark依赖其他python库/插件/项目,由于odps集群无法自由安装python库,需在本地打包后通过spark-submit上传。对于特定依赖,打包环境需跟线上环境保持一致。我们提供了以下多种打包方式:
- 方案一:不打包直接采用公共资源(无需上传额外资源,只能使用默认提供的python环境)
- 方案二:上传单个wheel包(适用于需要的额外python依赖数量较少、较为简单)
- 方案三:利用脚本一键生成python环境(基于docker提供当前流行的若干版本的python环境,结合用户提供的requirements文件,利用脚本一键生成完整python包)
- 方案四:利用docker容器打包Python环境(可以任意选择python版本,docker容器只是提供了一个linux环境,最终需要把python环境上传到Maxcompute的资源中)
spark.hadoop.odps.cupid.resources = public.python-2.7.13-ucs4.tar.gz
spark.pyspark.python = ./public.python-2.7.13-ucs4.tar.gz/python-2.7.13-ucs4/bin/python
三方库List: Here
spark.hadoop.odps.cupid.resources = public.python-3.6.12.tar.gz
spark.pyspark.python = ./public.python-3.6.12.tar.gz/python-3.6.12/bin/python3
spark.hadoop.odps.cupid.resources = public.python-3.7.9-ucs4.tar.gz
spark.pyspark.python = ./public.python-3.7.9-ucs4.tar.gz/python-3.7.9-ucs4/bin/python3
三方库List: Here
如果依赖较为简单,则可以只上传单个wheel包,通常需要选用manylinux版本,wheel包下载地址:https://pypi.org/
(1)需要将wheel包重命名为zip包,例如将pymysql的wheel包重命名为pymysql.zip
(2)将重命名后的zip包上传(文件类型为archive)
(3)在Dataworks spark节点引用(archive类型)
(4)在spark-defaults.conf或dataworks配置项中添加配置以下后即可import
## 配置
spark.executorEnv.PYTHONPATH=pymysql
spark.yarn.appMasterEnv.PYTHONPATH=pymysql
## 代码
import pymysql
若需要的额外依赖较多,则采取第2个选项上传单个wheel包
会导致重复操作量倍增。因此这里提供一个脚本(下载地址Here),只需提供一个编辑好的requirements文件(格式见Here),就能够直接生成完整的python环境用于PySpark使用,具体如下。
$ chmod +x generate_env_pyspark.sh
$ generate_env_pyspark.sh -h
Usage:
generate_env_pyspark.sh [-p] [-r] [-t] [-c] [-h]
Description:
-p ARG, the version of python, currently supports python 2.7, 3.5, 3.6 and 3.7 versions.
-r ARG, the local path of your python requirements.
-t ARG, the output directory of the gz compressed package.
-c, clean mode, we will only package python according to your requirements, without other pre-provided dependencies.
-h, display help of this script.
# 带有预装依赖的打包方式
$ generate_env_pyspark.sh -p 3.7 -r your_path_to_requirements -t your_output_directory
# 不带预装依赖的打包方式(clean mode)
generate_env_pyspark.sh -p 3.7 -r your_path_to_requirements -t your_output_directory -c
- 脚本适用于Mac/Linux环境,需要预先安装Docker, 见官方文档。
- 目前仅支持python 2.7、3.5、3.6和3.7版本,若对python版本不敏感,当前强烈推荐使用python 3.7。
- -c选项表示是否开启clean mode,clean mode无法使用预装依赖,但输出的python包更小。
- 当前MaxCompute对上传资源的大小有500MB的限制,因此如果大部分预装依赖用不到,强烈推荐使用-c选项打包。
generate_env_pyspark.sh脚本的输出为在指定目录下(-t选项)生成指定python版本(-p选项)的gz包,以python3.7为例,将生成py37.tar.gz。后续再将此包上传为archieve资源(以odpscmd执行为例,也可用odps-sdk上传,各种方式上传资源参考:资源操作):
# 在odpscmd中执行
add archive /your/path/to/py37.tar.gz -f;
然后在spark配置中增加以下两项配置即可:
spark.hadoop.odps.cupid.resources = your_project.py37.tar.gz
spark.pyspark.python = your_project.py37.tar.gz/bin/python
若上述两个参数不生效,例如用于zeppelin调试pyspark时notebook中的python环境,还需在spark作业中增加以下两项配置:
spark.yarn.appMasterEnv.PYTHONPATH = ./your_project.py37.tar.gz/bin/python
spark.executorEnv.PYTHONPATH = ./your_project.py37.tar.gz/bin/python
(1)需要引入的依赖包含so文件等,无法通过上述zip文件的方式使用 和 无法进行pip install安装
(2)确实对除2.7、3.5、3.6、3.7以外的python版本有特殊需求
针对以上特殊情况,同时保证打包环境与线上环境一致(如mac打出来的python环境与线上环境不兼容),我们基于Docker提供以下步骤进行打包(以Python3.7为例)。
- Docker镜像制作,在安装了docker环境的宿主机新建一个Dockerfile文件,python3参考如下:
FROM centos:7.6.1810
RUN set -ex \
# 预安装所需组件
&& yum install -y wget tar libffi-devel zlib-devel bzip2-devel openssl-devel ncurses-devel sqlite-devel readline-devel tk-devel gcc make initscripts zip\
&& wget https://www.python.org/ftp/python/3.7.0/Python-3.7.0.tgz \
&& tar -zxvf Python-3.7.0.tgz \
&& cd Python-3.7.0 \
&& ./configure prefix=/usr/local/python3 \
&& make \
&& make install \
&& make clean \
&& rm -rf /Python-3.7.0* \
&& yum install -y epel-release \
&& yum install -y python-pip
# 设置默认为python3
RUN set -ex \
# 备份旧版本python
&& mv /usr/bin/python /usr/bin/python27 \
&& mv /usr/bin/pip /usr/bin/pip-python27 \
# 配置默认为python3
&& ln -s /usr/local/python3/bin/python3.7 /usr/bin/python \
&& ln -s /usr/local/python3/bin/pip3 /usr/bin/pip
# 修复因修改python版本导致yum失效问题
RUN set -ex \
&& sed -i "s#/usr/bin/python#/usr/bin/python27#" /usr/bin/yum \
&& sed -i "s#/usr/bin/python#/usr/bin/python27#" /usr/libexec/urlgrabber-ext-down \
&& yum install -y deltarpm
# 更新pip版本
RUN pip install --upgrade pip
python2参考如下:
FROM centos:7.6.1810
RUN set -ex \
# 预安装所需组件
&& yum install -y wget tar libffi-devel zlib-devel bzip2-devel openssl-devel ncurses-devel sqlite-devel readline-devel tk-devel gcc make initscripts zip\
&& wget https://www.python.org/ftp/python/2.7.18/Python-2.7.18.tgz \
&& tar -zxvf Python-2.7.18.tgz \
&& cd Python-2.7.18 \
&& ./configure prefix=/usr/local/python2 \
&& make \
&& make install \
&& make clean \
&& rm -rf /Python-2.7.18*
# 设置默认为python
RUN set -ex \
&& mv /usr/bin/python /usr/bin/python27 \
&& ln -s /usr/local/python2/bin/python /usr/bin/python
RUN set -ex \
&& wget https://bootstrap.pypa.io/get-pip.py \
&& python get-pip.py
RUN set -ex \
&& rm -rf /usr/bin/pip \
&& ln -s /usr/local/python2/bin/pip /usr/bin/pip
# 修复因修改python版本导致yum失效问题
RUN set -ex \
&& sed -i "s#/usr/bin/python#/usr/bin/python27#" /usr/bin/yum \
&& sed -i "s#/usr/bin/python#/usr/bin/python27#" /usr/libexec/urlgrabber-ext-down \
&& yum install -y deltarpm
# 更新pip版本
RUN pip install --upgrade pip
- 构建镜像并运行容器:
# 在Dockerfile文件的目录下运行如下命令:
docker build -t python-centos:3.7 .
docker run -itd --name python3.7 python-centos:3.7
- 进入容器安装所需的python依赖库:
docker attach python3.7
pip install [所需依赖库]
- 打包python环境:
cd /usr/local/
zip -r python3.7.zip python3/
- 拷贝容器中的python环境到宿主机:
ctrl+P+Q退出容器
在宿主机运行命令:docker cp python3.7:/usr/local/python3.7.zip .
- 上传python3.7.zip包到Maxcompute资源,由于dataworks最大只能上传50MB的包,如果大于50MB可以通过Maxcompute客户端进行上传,上传类型为archive,命令参考资源操作
add archive /path/to/python2.7.tar.gz -f;
- 提交作业时只需要在spark-default.conf或dataworks配置项中添加以下配置即可(spark.hadoop.odps.cupid.resources配置项说明)
spark.hadoop.odps.cupid.resources=[project名].python3.7.zip
spark.pyspark.python=./[project名].python3.7.zip/python3/bin/python3.7
通过Docker容器打包,如果遇到so包找不到的情况,则需要手动将so包放到python环境中(一般so包都能在容器中都能找到),并在spark作业中添加以下环境变量:
spark.executorEnv.LD_LIBRARY_PATH=$LD_LIBRARY_PATH:./[project名].python3.7.zip/python3/[创建的so包目录]
spark.yarn.appMasterEnv.LD_LIBRARY_PATH=$LD_LIBRARY_PATH:./[project名].python3.7.zip/python3/[创建的so包目录]
很多情况下用户需要使用自定义的python文件,可以打包提交,这样避免了上传多个py文件,步骤如下:
- 将用户代码打一个zip包,需要在目录下自定义个一个空白的__init__.py
- 将用户代码zip包通过Maxcompute资源上传,并重命名,该资源在工作目录中将会被解压。详见配置spark.hadoop.odps.cupid.resources
- 配置参数:
spark.executorEnv.PYTHONPATH=.
spark.yarn.appMasterEnv.PYTHONPATH=. - 最后,主python文件就可以import该目录下的python文件了