PySpark on Yarn 的 Python 环境与包依赖解决方式

1、问题

Spark on Yarn是将yarn作为ClusterManager的运行模式,Spark会将资源(container)的管理与协调统一交给yarn去处理。

Spark on Yarn分为client/cluster模式:
对于client模式,Spark程序的Driver/SparkContext实例用户提交机上,该机器可以位于yarn集群之内或之外,只需要起能正常与ResourceManager通信及正确配置HADOOP_CONF_DIR或YARN_CONF_DIR环境变量指向yarn集群。生产环境中,通常提交机不会是yarn集群内部的节点,手握配置权限的情况下,可以按需配置支撑Spark程序需要的软件、环境、文件等。
对于cluster模式,Spark程序的Driver/SparkContext实例位于ApplicationMaster(am)中,am作为一个container可以起在yarn集群中任何一个NodeManager上,默认情况下,我们就需要为所有的节点机器准备好Spark程序需要的所有运行环境。

Python提供了非常丰富的数学运算、机器学习处理库——如numpypandasscipy等等。越来越多的同事希望利用这些高效的库开发各种算法然后以PySpark程序跑到我们的Spark上。

对于scala/java写的Spark程序,我们可以将我们所依赖的jar一起与我们的main函数所在的主程序打成一个fat jar,通过spark-submit提交后,这些依赖就会通过Yarn的Distribute Cache分发到所有节点支撑运行。
对于python写的Spark程序如果有外部依赖就很尴尬了,python本身就是两种语言,在所有NodeManager节点上安装你所有需要的依赖对于IT运维人员也是一个非常痛苦的事情。

参考官方文档

For Python, you can use the --py-files argument of spark-submit to add .py, .zip or .egg
files to be distributed with your application. If you depend on multiple Python files we recommend
packaging them into a .zip or .egg.

--py-files,可以解决部分依赖的问题,但对于有些场景就可能不是很方便,或者不可能实现。

  • 依赖太多,包括传递依赖
  • python包在deploy前需要依赖的C代码提前编译
  • 基于不同版本的python的pyspark跑在同一个yarn集群上

对于这些问题 ,社区也有相关的讨论,详细可以看下 这个ticket https://issues.apache.org/jira/browse/SPARK-13587

2、原理

pyspark原理的资料比较少,可以看下wiki

https://cwiki.apache.org/confluence/display/SPARK/PySpark+Internals

可以看下上面链接中的图,图中左右分为driver/executor, 图白色和绿色分python和java,可以看到不管PySpark适宜client还是cluster模式跑在yarn上,driver和executor端都有python的进程起着,这就需要集群中的所有节点都有相应的python依赖环境。

3、方案

从灵活性的角度来讲,这里从前辈的讨论中总结一下,提供一种在运行时创建python运行及相关依赖的办法

1、下载并安装anaconda
https://www.anaconda.com/download/#linux

2、安装anaconda

sh Anaconda2-5.0.1-Linux-x86_64.sh

3、创建需要的依赖环境 conda create

/home/username/anaconda3/bin/conda create --prefix=/home/username/name1/tools/anaconda2/envs/projname_py36 python==3.6
# 查看成功的环境
$ conda env list |grep projname_py36

第一次根据网络情况下载上述这些依赖,可能会比较久,以后就会快很多。

du -sh  projname_py36
847M    projname_py36

可以看到依赖包整个大小还是挺大的,对于一些实时性比较高的场景这种方式其实不太有利,有些不需要的依赖在创建的时候可以不打进去。当然我们还需要zip压缩一下,可以减小部分网络开销。当然如果我们把这个环境直接提前put到hdfs,也就没有这个问题了。

# 附:如果后续每次都要使用这个 conda projname_py36 环境,可以做成自动加载 conda 配置并初始化
#!/usr/bin/env bash
# set_conda_env.sh
export CONDA_HOME=/home/username/anaconda3
export JAVA_HOME=/opt/soft/jdk/jdk1.7.0_40
export JRE_HOME=/opt/soft/jdk/jdk1.7.0_40/jre
export HAADOOP_HOME=/usr/lib/software/hadoop
export SPARK_HOME=/usr/lib/software/spark/spark-2.1
export PATH=$SPARK_HOME/bin:$SPARK_HOME/python:$PATH:$CONDA_HOME/bin/conda 
export PYTHONPATH=$SPARK_HOME/python:$SPARK_HOME/python/lib/py4j-0.10.4-src.zip:$PYTHONPATH

export PATH=/home/username/anaconda3/bin:$PATH
source activate base
conda deactivate
conda activate /home/username/ooxx/tools/anaconda2/envs/projname_py36

export PYTHONUTF8=1

然后执行:
. set_conda_env.sh

4、环境与依赖整体打包上传 HDFS

zip -r projname_py36_env.zip ./projname_py36/
#unzip -t projname_py36_env.zip|grep bin/python
#    testing: projname_py36/bin/python3   OK
#    testing: projname_py36/bin/python3.6m   OK
#    testing: projname_py36/bin/python3.6   OK
#    testing: projname_py36/bin/python    OK
#    testing: projname_py36/bin/python3-config   OK
#    testing: projname_py36/bin/python3.6m-config   OK
#    testing: projname_py36/bin/python3.6-config   OK
hadoop fs -put projname_py36_env.zip /tmp/hadoop-username/projname/
hadoop fs -ls /tmp/hadoop-username/projname/
rm -rf projname_py36_env.zip

这样我们就可以通过 --archives  hdfs://hdp-66-cluster/tmp/hadoop-username/projname/projname_py36_env.zip#PyEnv 的方式将python及其依赖环境上传并分发到spark各个进程的working dir。

4、测试

为了节约时间,直接从spark示例代码里拷一个出来测试,并且以 cluster 模式提交:

# correlations_example1.py
from __future__ import print_function

import numpy as np

from pyspark import SparkContext
# $example on$
from pyspark.mllib.stat import Statistics
# $example off$

if __name__ == "__main__":
    sc = SparkContext(appName="CorrelationsExample")  # SparkContext

    # $example on$
    seriesX = sc.parallelize([1.0, 2.0, 3.0, 3.0, 5.0])  # a series
    # seriesY must have the same number of partitions and cardinality as seriesX
    seriesY = sc.parallelize([11.0, 22.0, 33.0, 33.0, 555.0])

    # Compute the correlation using Pearson's method. Enter "spearman" for Spearman's method.
    # If a method is not specified, Pearson's method will be used by default.
    print("Correlation is: " + str(Statistics.corr(seriesX, seriesY, method="pearson")))

    data = sc.parallelize(
        [np.array([1.0, 10.0, 100.0]), np.array([2.0, 20.0, 200.0]), np.array([5.0, 33.0, 366.0])]
    )  # an RDD of Vectors

    # calculate the correlation matrix using Pearson's method. Use "spearman" for Spearman's method.
    # If a method is not specified, Pearson's method will be used by default.
    print(Statistics.corr(data, method="pearson"))
    rdd = sc.parallelize([str(Statistics.corr(data, method="pearson"))])
    rdd.saveAsTextFile("hdfs://hdp-66-cluster/tmp/username/name2/correlations_example.txt")
    # $example off$

    sc.stop()
# 完整 cluster 提交流程
(1)
cp /usr/lib/software/spark/spark-2.1/examples/src/main/python/mllib/correlations_example.py ~/projname/correlations_example1.py

(2)
/usr/lib/software/spark/spark-2.3.2-bin-U1.1/bin/spark-submit \
--master yarn \
--deploy-mode cluster \
--queue root.spark.username.spark \
--num-executors 8 \
--executor-cores 1 \
--archives  hdfs://hdp-66-cluster/tmp/hadoop-username/projname/projname_py36_env.zip#PyEnv \
--conf spark.yarn.appMasterEnv.PYSPARK_PYTHON=PyEnv/projname_py36/bin/python \
--conf spark.yarn.appMasterEnv.PYSPARK_DRIVER_PYTHON=PyEnv/projname_py36/bin/python \
--conf spark.executorEnv.PYSPARK_PYTHON=PyEnv/projname_py36/bin/python \
--conf spark.executorEnv.PYSPARK_DRIVER_PYTHON=PyEnv/projname_py36/bin/python \
#--py-files a.py,b.py,c.py \
~/projname/correlations_example1.py

#--archives后面的参数默认是找本地路径的文件,只有加上hdfs://host:port/path才会找hdfs上的文件

(3)
hadoop fs -cat hdfs://hdp-66-cluster/tmp/hadoop-username/projname/correlations_example.txt/*

Refer

[1] PySpark on Yarn的相关依赖的解决方式

https://www.jianshu.com/p/df0a189ff28b

转载于:https://my.oschina.net/leejun2005/blog/75941

  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值