PySpark Python版本和依赖

更新时间:
复制为 MD 格式

本文介绍PySpark Package依赖的多种打包方式。

功能简介

通常情况下,PySpark作业依赖两类Python资源:第三方库(如其他Python库、插件或项目)以及用户自定义模块。由于MaxCompute集群无法自由安装Python库,需在本地打包后通过spark-submit上传。

方案一:不打包直接使用公共资源

spark-defaults.confDataWorks配置中修改配置项,如下分别为不同版本Python默认提供的环境配置。

Python 2.7.13

默认提供的Python 2.7.13环境配置,点击查看第三方库列表

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

Python 3.6.12

默认提供的Python 3.6.12环境配置。

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

Python 3.7.9

默认提供的Python 3.7.9环境配置,点击查看第三方库列表

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

Python3.11

spark.hadoop.odps.spark.alinux3.enabled = true

方案二:上传单个wheel

如果依赖较为简单,则可以只上传单个wheel包,通常需要选用manylinux版本,点击下载wheel

  1. wheel包重命名为zip包,例如将下载下来的pymysqlwheel包重命名为pymysql.zip

  2. 登录DataWorks控制台,在左上角选择地域。

  3. 选择工作空间,单击进入Data Studio

  4. 在左侧导航栏单击image图标,然后单击image新建MaxCompute Archive资源。上传打包好的pymysql.zip

  5. DataWorks Spark节点应用。

  6. spark-defaults.conf配置文件或DataWorks配置项中添加以下配置:

    spark.executorEnv.PYTHONPATH=pymysql  
    spark.yarn.appMasterEnv.PYTHONPATH=pymysql
  7. 代码中导入包import pymysql

方案三:利用pyodps-pack快捷打包

若需要打包大量Python第三方库且对Python版本的依赖度较小时,推荐使用 pyodps-pack 工具。该工具依赖Docker提供打包环境,并支持通过 requirements.txt 文件批量处理依赖。以下将以打包 pandas 库(使用Python 3.11环境)为例,演示其具体用法。详细打包方式,参考制作和使用三方包-PyODPS 0.12.4

Python版本限制pyodps-pack 目前支持的Python版本范围为 3.8 至 3.14。

  1. 使用pyodps-pack打包

    # 在开发环境执行
    pip install pyodps
    
    # 此处使用的python版本建议和spark作业的python版本保持一致,若系统上没有Docker,需先安装并运行Docker。
    pyodps-pack pandas --python-version=3.11 -o pyodps-pandas.tar.gz
    
    # 也可以使用 requirements.txt
    pyodps-pack -r requirements.txt --python-version=3.11 -o pyodps-pandas.tar.gz
  2. odpscmd上传打包文件

    add archive PATH/pyodps-pandas.tar.gz -f;
  3. 设置作业启动配置

    修改spark-defaults.conf配置文件。

    spark.hadoop.odps.cupid.resources = {your_project}.pyodps-pandas.tar.gz
    spark.executorEnv.PYTHONPATH = ./{your_project}.pyodps-pandas.tar.gz/packages
    spark.yarn.appMasterEnv.PYTHONPATH = ./{your_project}.pyodps-pandas.tar.gz/packages
    
    # 如果使用 notebook 的用户打包请忽略以下内容
    # 设置spark版本为3.43.5
    spark.hadoop.odps.spark.version = spark-3.4.2-odps0.48.0
    或 spark.hadoop.odps.spark.version = spark-3.5.2-odps0.48.0
    
    # 使用该参数切换到python 3.11版本(可选,需要和pyodps-pack打包指定的python版本一致)
    spark.hadoop.odps.spark.alinux3.enabled = true

方案四:利用脚本一键生成Python环境

对于需要打包大量第三方库的场景,为避免方案二逐一上传wheel包重复且繁琐的操作,该方案提供了一个自动化打包脚本,只需准备一个requirements文件(文件格式请参考链接)即可通过脚本一键构建一个包含所有指定依赖的完整Python环境,可直接在PySpark作业中使用。目前仅支持Python 2.7、3.5、3.63.7版本,若对Python版本不敏感,当前推荐使用Python 3.7。

具体操作步骤如下:

  1. 下载自动化打包脚本

    脚本适用于Mac/Linux环境,需要预先安装Docker,见官方文档

  2. 在命令行修改文件权限并查看使用方法:

    $ 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.

    -c选项表示是否开启clean mode,clean mode无法使用预装依赖,但输出的Python包更小。当前MaxCompute对上传资源的大小有500MB的限制,因此如果大部分预装依赖用不到,强烈推荐使用-c选项打包。点击链接查看不同Python版本的预装依赖:2.7预装依赖3.5预装依赖3.6预装依赖3.7预装依赖

  3. 打包示例代码

    # 带有预装依赖的打包方式
    $ 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
  4. Spark中使用

    generate_env_pyspark.sh脚本的输出为在指定目录下(-t选项)生成指定Python版本(-p选项)的gz包。以Python3.7为例,将生成py37.tar.gz。后续再将此包上传为archive资源,以odpscmd执行为例,也可用odps-sdk上传,上传资源的各种方式参考资源操作

    # 在odpscmd中执行
    add archive /your/path/to/py37.tar.gz -f;

    然后在spark-defaults.conf配置文件或DataWorks配置项中添加以下配置:

    spark.hadoop.odps.cupid.resources = your_project.py37.tar.gz
    spark.pyspark.python = your_project.py37.tar.gz/bin/python
    
    若上述两个参数不生效,例如用于zeppelin调试pysparknotebook中的python环境,还需在spark作业中增加以下两项配置:
    spark.yarn.appMasterEnv.PYTHONPATH = ./your_project.py37.tar.gz/bin/python
    spark.executorEnv.PYTHONPATH = ./your_project.py37.tar.gz/bin/python

方案五:利用docker容器打包Python环境

在满足以下任一条件时,推荐采用本方案打包:

  • 依赖包含非Python代码:待打包的库包含.so等二进制文件,导致无法通过简单的pip install或上传zip压缩包的方式分发。

  • 需要自定义Python版本:项目要求使用特定版本的Python环境(例如Python 3.8),而非平台默认提供的版本(如2.7、3.5、3.6、3.7)。

为此,提供了基于Docker的打包方案。以下将以构建Python 3.8环境为例,演示其操作步骤。

  1. 准备Dockerfile。

    在已安装Docker的本地开发机上,创建一个名为 Dockerfile 的文件。根据目标环境,从以下两个模板中选择一个。

    Python3.8 + alinux2

    FROM alibaba-cloud-linux-2-registry.cn-hangzhou.cr.aliyuncs.com/alinux2/alinux2:latest
    RUN curl -o /etc/yum.repos.d/CentOS-Base.repo https://mirrors.aliyun.com/repo/Centos-7.repo
    RUN curl -o /etc/yum.repos.d/epel.repo https://mirrors.aliyun.com/repo/epel-7.repo
    RUN set -ex \
        # 预安装所需组件
        && yum clean all \
        && yum makecache \
        && 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.8.20/Python-3.8.20.tgz \
        && tar -zxvf Python-3.8.20.tgz \
        && cd Python-3.8.20 \
        && ./configure prefix=/usr/local/python3 \
        && make \
        && make install \
        && make clean \
        && rm -rf /Python-3.8.20* \
        && 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.8 /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

    Python3.7 + alinux3

    FROM alibaba-cloud-linux-3-registry.cn-hangzhou.cr.aliyuncs.com/alinux3/alinux3:latest
    RUN set -ex \
        && yum clean all \
        && yum makecache \
        && yum install -y \
            wget \
            tar \
            libffi-devel \
            zlib-devel \
            bzip2-devel \
            openssl-devel \
            ncurses-devel \
            sqlite-devel \
            readline-devel \
            tk-devel \
            xz-devel \
            gcc \
            make \
            initscripts \
            zip \
            which
    RUN set -ex \
        && wget https://www.python.org/ftp/python/3.7.17/Python-3.7.17.tgz \
        && tar -zxvf Python-3.7.17.tgz \
        && rm -f Python-3.7.17.tgz \
        && cd Python-3.7.17 \
        && ./configure --prefix=/usr/local/python3 \
            --enable-optimizations \
        && make -j$(nproc) \
        && make install \
        && make clean \
        && cd / \
        && rm -rf Python-3.7.17 \
        && yum clean all
    
    RUN ln -sf /usr/local/python3/bin/python3.7 /usr/bin/python \
        && ln -sf /usr/local/python3/bin/pip3 /usr/bin/pip
    
    # 升级pip
    RUN pip install --no-cache-dir --upgrade pip
  2. 构建镜像并运行容器

    Dockerfile文件的目录下运行如下命令:

    # 1. 构建Docker镜像 (以Python 3.8为例)
    # -t 给镜像命名,格式为 <image_name>:<tag>
    docker build --platform linux/amd64 -t python-centos:3.8 .
    
    # 2. 启动一个后台运行的容器
    # --name 给容器命名,便于后续操作
    docker run --platform linux/amd64 -itd --name python3.8 python-centos:3.8 bash
  3. 进入容器安装所需的Python依赖库

    docker attach python3.8
    pip install [所需依赖库]
  4. 打包Python环境

    cd /usr/local/
    zip -r python3.8.zip python3/
  5. 拷贝容器中的Python环境到宿主机

    ctrl+P+Q退出容器
    docker cp python3.8:/usr/local/python3.8.zip .
  6. 上传Python3.8.zip包到MaxCompute资源

    可以通过本地客户端(odpscmd)上传,上传类型为archive,详细命令参考资源操作

    add archive /path/to/python3.8.zip -f;
  7. 修改spark-defaults.confDataWorks配置项

    提交Spark作业时在spark-defaults.conf配置文件或DataWorks配置项中添加如下配置:

    spark.hadoop.odps.cupid.resources=[project名].python3.8.zip
    spark.pyspark.python=./[project名].python3.8.zip/python3/bin/python3.8

如果遇到so包找不到的情况,则需要手动将so包放到Python环境中,并在Spark配置中添加以下环境变量(一般so包都能在容器中找到):

spark.executorEnv.LD_LIBRARY_PATH=$LD_LIBRARY_PATH:./[project名].python3.8.zip/python3/[创建的so包目录]
spark.yarn.appMasterEnv.LD_LIBRARY_PATH=$LD_LIBRARY_PATH:./[project名].python3.8.zip/python3/[创建的so包目录]

引用用户自定义的python

  1. 生成压缩包

    在目录下创建一个空白的__init__.py,生成一个zip压缩包。

    cd /path/to/parent_dir
    touch __init__.py
    zip -r target_dir.zip target_dir/
    注意:使用相对路径压缩,不要将父目录打入zip包,target_dir.zip解压后不包含parent_dir路径
  2. 上传zip压缩包

    • 方式一:使用DataWorks上传为archive类型资源

    • 方式二:使用odpscmd上传为archive类型资源

      add archive /path/to/python3.8.zip -f;
  3. 任务引用zip

    • 方式一:在DataWorks任务节点archive资源中引入zip压缩包

    • 方式二:修改spark-defaults.conf配置文件。通过spark.hadoop.odps.cupid.resources参数引用zip包,并重命名。

      参数示例:
      spark.hadoop.odps.cupid.resources=[替换project].target_dir.zip:target_dr 
      多个资源用逗号隔开。
  4. 任务配置参数

    修改spark-defaults.conf配置文件

    任务配置参数并引入 Python 包,如果对文件位置有疑惑请参考数据互通配置,务必清楚上传的包内部目录结构。以下的 ./ 就表示当前工作目录 /workdir/,填写合理的PYTHONPATH环境变量和对应的import路径。

    ## 1. 第一种情况:假设当前 target_dir.zip 的子目录结构为 target_dir.zip/sub/target_module
    spark.executorEnv.PYTHONPATH=./target_dir
    spark.yarn.appMasterEnv.PYTHONPATH=./target_dir
    # 引入 Python 包
    from sub import target_module.xxx
    
    ## 2. 第二种情况:假设当前 target_dir.zip 的子目录结构为 target_dir.zip/target_module
    spark.executorEnv.PYTHONPATH=./
    spark.yarn.appMasterEnv.PYTHONPATH=./
    # 引入 Python 包
    from target_module import xxx
    
    ## 其余情况类似,不再赘述...