• MaxCompute(ODPS)中Python UDF使用:如何打包所有依赖包


    背景

    ODPS平台进行数据处理和分析,有时候进行复杂的数据开发需要使用UDF,JAVA版本的UDF暂且不提,这里只讲述基于Python进行UDF编写,而如何基于Python开发UDF,以及如何进行第三方依赖包的配置,包括如何编译生成Wheel包。大概有几种方法。

    总结大概过程,Python UDF开发及使用主要分为几步:

    1. 如果有二进制包[**.whl]则下载对应的Python版本的wheel包,并改后缀为.zip,如果只有第三方包的源代码,没有编译后的二进制包,需要下载相应文件[比如.tar.gz, .tar],对源代码编译,编译为whl包;
    2. 上传zip文件到dataworks里面并提交;
    3. 写UDF(在evaluate这个method里面import各种dependency)并提交;
    4. 在SQL里面运行UDF

    而至于为什么需要自己上传资源包,首先我们知道ODPS是一个分布式的储存和计算框架,如果需要运行某个python package,就需要在每一个worker上保证都可以需要运行这个包,但是如果平台给全部的worker安装统一的python环境,不仅不灵活且特别耗费资源,毕竟每个用户的计算所需要的python dependency可能是非常不同的。

    ODPS解决这个问题的方法是通过上传资源的依赖文件的方式。就是说每次运行一个用户自定义的计算时(UDF),ODPS都会把这个UDF所依赖的所有的资源都传输到分配的worker节点,然后在worker节点运行这些依赖的资源。

    当依赖包比较单一或者比较少时,使用上述方法是可行的,但是当依赖包比较多、比较复杂时,就会面临几个比较大的问题:

    • 如果是基于Python2开发UDF的话,随着 Python 2 逐渐被社区抛弃,numpy, scipy 等大部分项目新版本已经不再支持 Python 2,手动寻找兼容 ODPS 的依赖包非常辛苦不说,还会给找到兼容 ODPS 的 Python 2 的 wheel 增加了不少难度;
    • 依赖包一般还有自己的依赖包,所以在寻找兼容 ODPS 的 Python二进制wheel包的时候很容易出错;
    • 如果第三方包只有源代码,没有编译后的二进制包,则需要对源代码进行编译

    所以就需要一种更省事的方法。况且手动寻找兼容 ODPS 的依赖也比较耗费时间和精力,也容易出错,况且依赖一般还有自己的依赖。

    基于上述应用需求,结合别的几篇打包Python依赖上ODPS跑的经验的文章,加了一些自己的优化和实践,总结了本文。主要步骤分以下几步:

    第一步:准备工作

    1.1 Docker安装

    对于mac电脑,可以安装:

    Docker

    1.2 修改镜像地址

    有的镜像地址非常慢,需要尝试不同的镜像地址,见作者另一篇文章:《Docker配置国内代理解决办法》

    1.3 拉取linux镜像

    本机terminal输入:

    docker pull quay.io/pypa/manylinux2010_x86_64
    
    • 1

    1.4 查看镜像内支持的python版本

    进入到所下载的manylinux镜像的docker环境中,并查看其支持的python版本。

    docker run -it quay.io/pypa/manylinux2010_x86_64 /bin/bash
    
    • 1

    进入容器后执行:

    cd /opt/python/
    ll
    
    • 1
    • 2

    得到:

    total 0
    lrwxrwxrwx 1 root root 29 Jan 17 05:18 cp310-cp310 -> /opt/_internal/cpython-3.10.2
    lrwxrwxrwx 1 root root 29 Jan 17 05:17 cp36-cp36m -> /opt/_internal/cpython-3.6.15
    lrwxrwxrwx 1 root root 29 Jan 17 05:18 cp37-cp37m -> /opt/_internal/cpython-3.7.12
    lrwxrwxrwx 1 root root 29 Jan 17 05:18 cp38-cp38 -> /opt/_internal/cpython-3.8.12
    lrwxrwxrwx 1 root root 29 Jan 17 05:18 cp39-cp39 -> /opt/_internal/cpython-3.9.10
    lrwxrwxrwx 1 root root 28 Jan 17 05:18 pp37-pypy37_pp73 -> /opt/_internal/pypy3.7-7.3.7
    lrwxrwxrwx 1 root root 28 Jan 17 05:17 pp38-pypy38_pp73 -> /opt/_internal/pypy3.8-7.3.7
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    如上所示,可以支持pytho3.6、3.7、3.8、3.9、3.10版本

    第二步:生成依赖包

    2.1 文件及命令准备

    在本地新建一个工作目录,任意命名;

    mkdir MyFolder
    
    • 1

    在里面新建两个文件:一个为项目依赖包清单requirements.txt,一个为build.sh脚本。

    2.1.1 生成requirements.txt
    numpy
    folium
    geopandas
    
    • 1
    • 2
    • 3

    在里面填上依赖的包名及版本。也可以:

    statsmodels==0.10.1
    requests==2.22.0
    odps==3.5.1
    pandas==0.25.1
    numpy==1.17.2
    scikit_learn==0.23.2
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    2.1.2 生成build.sh
    #!/bin/bash
    set -e -x
    
    PROJ=folium_geopandas
    PYBIN=/opt/python/cp37-cp37m/bin
    
    "${PYBIN}/pip" install --target __pypackages__ -r requirements.txt -i https://pypi.tuna.tsinghua.edu.cn/simple/
    tar c __pypackages__ | gzip > $PROJ-depends.tar.gz
    rm -rf __pypackages__
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    注释(按照行):

    • 1:指定此脚本使用/bin/bash来解释执行;
    • 2:x在执行每一行shell脚本时把执行的内容输出来,-e执行出错时立即结束程序;
    • 4:自定义生成的依赖包名称,最后生成的文件名由$PROJ-depends.tar.gz决定,这里可以自定义;
    • 5:指定linux镜像中调用的python的路径,此处用的是cp37-cp37m以支持python3。也可用cp27以支持python2。具体有哪些可用python版本,需要用docker进入该镜像进行查看[见准备工作-查看镜像内支持的python版本];
    • 7:执行pip以安装清单列表中列出的第三方依赖包,并将安装生成的文件放入目录__pypackages__,最手将该目录压缩打包,生成.tar.gz文件。此处通过-i来声明采用清华源的镜像地址(其它镜像源亦可),以加速各种依赖包的下载。

    2.2 一键生成依赖包

    通过docker挂载的方式,生成依赖包的压缩文件包.tar.gz。在本地的terminal中,切换到MyFolder路径之下,运行如下命令:

    docker run -it  --rm  -v $PWD:/build -w /build quay.io/pypa/manylinux2010_x86_64 /bin/bash build.sh
    
    • 1

    注释:
    ● 将当前路径(即MyFolder文件夹)中的文件(即requirements.txt和build.sh)挂载到docker中的build目录下,并指定docker环境内的工作路径为build目录——至于docker中是否已存在build目录则无妨
    ● --rm、-v、-w后面均有空格。

    这里会运行的比较慢,见下图,可以看到,不仅找到了numpy、folium、geopandas,还找到了所有的依赖包。
    在这里插入图片描述在这里插入图片描述
    运行完上述代码后,会在MyFolder下生成一个压缩包,即所需要的最终的包。
    在MyFolder执行ls,可以看到:

    build.sh			folium_geopandas-depends.tar.gz	requirements.txt
    
    • 1

    folium_geopandas-depends.tar.gz即为我们所需要的包。

    第三步:ODPS部署

    3.1 上传资源

    1、在ODPS的资源中以Archive类型上传folium_geopandas-depends.tar.gz并提交。上传资源最大限制200M。

    2、Function Studio里,选择UDF,命名print_success.py并提交资源至DataStudio开发环境。
    Function Studio为编写 Python UDF 很方便的Web编辑器,当然也可以在DataStudio里直接新建.py资源。

    from odps.udf import annotate
    
    @annotate("->string")
    class Print_Success(object):
        def __init__(self):
            import sys
            sys.path.insert(0, "work/" "folium_geopandas-depends.tar.gz" "/__pypackages__")
    
        def evaluate(self):
            import numpy
            import folium
            import geopandas
            return "import succeed"
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    3.2 新建函数

    Function Studio里,将print_success.py提交函数至DataStudio开发环境,定义函数名为print_success,另外此时需要在额外资源里填入folium_geopandas-depends.tar.gz,如果有多个资源以逗号分割。
    在这里插入图片描述

    第四步:测试

    odps里,开发环境里输入sql语句测试:

    set odps.isolation.session.enable=true;
    set odps.sql.python.version=cp37;
    SELECT TRY_NUMPY()
    ;
    
    • 1
    • 2
    • 3
    • 4

    注释:

    • set odps.isolation.session.enable=true是因为有些包需要对isolation的设置进行更改;
    • set odps.sql.python.version=cp37是因为本文的python UDF需要采用Python3环境执行,如果是Python2则不需要。

    输出:
    import succeed

    如果选择在生产环境应用此函数,需要将folium_geopandas-depends.tar.gz、print_success.py、以及函数print_success发布。

  • 相关阅读:
    js设计模式
    2024年山东省职业院校技能大赛中职组 “网络安全”赛项竞赛试题-C卷
    HackBGRT 更改开机logo以及如何修复
    ubuntu配置vscode c++环境
    LeetCode每日一题(2348. Number of Zero-Filled Subarrays)
    Vue开发时Vscode建议安装的常用插件
    获取最新时次空气质量国控站数据的接口
    java基础巩固第四天
    S7-1500 PLC之间进行TCP通信的具体方法和步骤详解(图文)
    【数据结构】链表经典OJ题,常见几类题型(一)
  • 原文地址:https://blog.csdn.net/yawei_liu1688/article/details/126563681