網上提交 scala spark 任務的攻略非常多,官方文檔其實也非常詳細仔細的介紹了 spark-submit 的用法。但是對於 python 的提交提及得非常少,能查閱到的資料非常少導致是有非常多的坑需要踩。
官方文檔對於任務提交有這么一段介紹,但是初次使用者依然會非常疑惑:
Bundling Your Application’s Dependencies
If your code depends on other projects, you will need to package them alongside your application in order to distribute the code to a Spark cluster. To do this, create an assembly jar (or “uber” jar) containing your code and its dependencies. Both sbt and Maven have assembly plugins. When creating assembly jars, list Spark and Hadoop as
provided
dependencies; these need not be bundled since they are provided by the cluster manager at runtime. Once you have an assembled jar you can call thebin/spark-submit
script as shown here while passing your jar.For Python, you can use the
--py-files
argument ofspark-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
.
可以看到如果我們使用 java 系語言,例如 java scala 我們可以輕松的將相關的依賴環境打包成 .jar,然后在提交的時候使用官方建議使用在的姿勢進行集群提交。例如使用:
sudo -u hdfs spark-submit \ --class "Excellent" \ --master yarn \ --deploy-mode cluster \ --driver-memory 2g \ --executor-memory 2g \ --executor-cores 1 \ /home/zhizhizhi/sparktry_2.11-0.1.jar
主要程序是 Excellent, 使用 yarn 進行調度,使用集群模式運行。需要分配的執行和 driver 的內存,以及執行的時候指定的核數。
其實對 python 的 submit 使用 yarn 也和這個命令差不多,我們可以使用
/etc/alternatives/spark-submit \ --master yarn \ --deploy-mode cluster \ --name md_day_dump_user \ --conf "spark.pyspark.driver.python=/home/uther/miniconda2/envs/uther/bin/python2.7" \ --conf "spark.pyspark.python=/home/uther/miniconda2/envs/uther/bin/python2.7" \ --py-files /home/uther/uther/uther.zip \ /home/uther/uther/spark_run/md_day_dump_users.py
好了讓我們來談下這里面的坑。
首先注意一下我這里顯示申明了使用 /etc/alternatives/spark-submit 如果我們不使用這個申明我們會默認使用安裝 pyspark 的時候給我們帶的 spark-submit。
這一點非常重要,因為我們的集群使用的 CDH 部署的,所以其實很多環境變量依賴什么的 CDH 都已經給我們配置好了,使用自己的 spark-submit 就需要自己配置這些東西,可能會導致很多問題,比如你無法直接連接到目標 hive 等等等。
默認會使用
(uther) [uther@zed-2 ~]$ which spark-submit ~/miniconda2/envs/uther/bin/spark-submit
這一點要非常難發現。。。。。。得非常小心。
使用集群進行運行這一點感覺也有坑,按照我查閱的一些資料來看,如果使用集群調度,很有可能在分配 application master 的時候被分配到別的機器上去,這就需要別的機器也有這一套環境,否則可能會導致失敗。可能會報出類似下面的問題,但是也不太確定,因為最近跑似乎每次都分配給了提交任務的節點進行執行的,之后再觀察一下。
thread "main" java.io.FileNotFoundException: File
經過一段時間的觀察,如果使用 yarrn 進行任務調度,的確需要在每個可以分配的節點上同樣的路徑下有相同的運行環境才可以保證程序的正常運行這一點要 注意。
我使用的是 miniconda 的環境,所以直接將 miniconda 的包拷貝到相同的路徑下然后將權限設置成 777 就可以了。
另外最關鍵的一步指定虛擬環境可以使用類似命令:
--conf "spark.pyspark.driver.python=/home/uther/miniconda2/envs/uther/bin/python2.7" \ --conf "spark.pyspark.python=/home/uther/miniconda2/envs/uther/bin/python2.7" \
這個兩條命令指定了集群使用哪里的環境來運行你的程序。明顯我們可能會關聯非常多的依賴包,使用這種方法會比較優雅。
另外 spark 提供了另外一條命令給我們引入包
--py-files /home/uther/uther/uther.zip
這條命令的意思類似於我的程序里有 import uther.xxxx.xxx or from uther.xx.xx import xxx 類似語句,我需要將 uther 當錯一個 egg or zip 包來 import 。像第三方包的行為一樣。
指定之后 spark 就能在執行你的代碼的時候找到對應的環境了。這對在使用 pyspark 的代碼結構有一定的要求,盡量將你的邏輯打包成一個 python 包來方便引用。
另外還值得一提的是,當我們操作提交代碼的時候還會報出各種奇奇怪怪的錯誤,但是基本上分為 權限問題 | 和環境變量問題。
例如沒有在 hdfs 上操作讀寫的權限,就需要你耐心的去 hdfs 上面把相關權限加上,這里列舉一個我遇到的比較典型的問題,由於我們需要將環境部署到每一台可能生成 application master 的機器上去,所以我們所有的機器都需要部署相關的 python_env 環境。
我們可以把 miniconda 包打包好然后分別傳到各個服務器上的目標路徑去。
我在一切都弄好了之后還是收到如下報錯:
19/03/06 21:23:36 INFO yarn.ApplicationMaster: Unregistering ApplicationMaster with FAILED (diag message: User class threw exception: java.io.IOException:
Cannot run program "/home/uther/miniconda2/envs/uther/bin/python2.7": error=13, Permission denied)
這毫無疑問是一個權限問題,但是我當時檢查了我的目標文件夾包括整個 miniconda 包都已經全部 777 了究竟為啥還是沒有權限?
最后我發現原來是 /home/uther 這個我的 /home 目錄下的用戶目錄還沒有設置權限,當設置完畢之后 it works!
ps:spark-6358 的一個同學和我遇到了相同的問題。
要用 yarn 調用相關的程序也記得把 yarn 加入被調用方的組,然后仔細檢查相關的權限。
Trouble Shooting:
在新創建用戶提交的時候還可能遇到的問題可能有,在某個機器上新創建賬號並賦予權限之后,可能 hdfs 上還並沒有該用戶的賬號。
所以我們需要在 hdfs 上也創建該用戶,這里為了方便我直接通過 HUE 在 hdfs 上創建了跟新機器上一樣名稱的用戶,就未再報出過沒有權限訪問 /user 權限不足的問題。
另外有一點也值得注意,我嘗試在我經常使用的 zed-2 上試圖訪問集群上的執行日志
yarn logs -applicationId application_1572242315020_51855
卻一直得到如下輸出
19/12/06 14:56:36 INFO client.RMProxy: Connecting to ResourceManager at /0.0.0.0:8032 19/12/06 14:56:38 INFO ipc.Client: Retrying connect to server: 0.0.0.0/0.0.0.0:8032. Already tried 0 time(s); retry policy is RetryUpToMaximumCountWithFixedSleep(maxRetries=10, sleepTime=1000 MILLISECONDS) 19/12/06 14:56:39 INFO ipc.Client: Retrying connect to server: 0.0.0.0/0.0.0.0:8032. Already tried 1 time(s); retry policy is RetryUpToMaximumCountWithFixedSleep(maxRetries=10, sleepTime=1000 MILLISECONDS) 19/12/06 14:56:40 INFO ipc.Client: Retrying connect to server: 0.0.0.0/0.0.0.0:8032. Already tried 2 time(s); retry policy is RetryUpToMaximumCountWithFixedSleep(maxRetries=10, sleepTime=1000 MILLISECONDS) 19/12/06 14:56:41 INFO ipc.Client: Retrying connect to server: 0.0.0.0/0.0.0.0:8032. Already tried 3 time(s); retry policy is RetryUpToMaximumCountWithFixedSleep(maxRetries=10, sleep
起初我一直懷疑是連接問題或者配置問題,但是檢查了一圈發現都沒有。並且以前可以正常訪問現在卻突然不可以了。
於是去逐一查找問題,最后發現是因為我們提交 submit 任務的機器目前已經資源問題已經被踢出了 NodeManager 所以無法再和 ResourceManager 通信了。
所以可以換台機器看,或者重新將其加入即可。
Reference:
https://zhuanlan.zhihu.com/p/43434216 spark-python版本依賴與三方模塊方案
https://spark.apache.org/docs/2.2.0/submitting-applications.html 官方 Submitting Applications 文檔
https://issues.apache.org/jira/browse/SPARK-6358