web-dev-qa-db-fra.com

Le moyen le plus simple d'installer les dépendances Python sur les nœuds exécuteurs Spark?

Je comprends que vous pouvez envoyer des fichiers individuels en tant que dépendances avec Python Spark. Mais qu'en est-il des bibliothèques à part entière (par exemple numpy)?

Spark a-t-il un moyen d'utiliser un gestionnaire de packages fourni (par exemple pip) pour installer les dépendances de bibliothèque? Ou cela doit-il être fait manuellement avant Spark sont réalisé?

Si la réponse est manuelle, quelles sont les approches de "meilleure pratique" pour synchroniser les bibliothèques (chemin d'installation, version, etc.) sur un grand nombre de nœuds distribués?

22
trianta2

En fait, après l'avoir essayé, je pense que le lien que j'ai publié en tant que commentaire ne fait pas exactement ce que vous voulez avec les dépendances. Ce que vous demandez assez raisonnablement, c'est un moyen pour que Spark joue bien avec setuptools et pip concernant l'installation des dépendances. Cela me fait penser que cela n'est pas mieux pris en charge dans Spark. Le tiers Le problème de dépendance est largement résolu dans Python à usage général, mais sous Spark, il semble que vous supposiez que vous retourniez à la gestion manuelle des dépendances ou quelque chose.

J'utilise un pipeline imparfait mais fonctionnel basé sur virtualenv . L'idée de base est

  1. Créez un virtualenv uniquement pour vos nœuds Spark
  2. Chaque fois que vous exécutez une tâche Spark, exécutez une nouvelle pip install de toutes vos propres bibliothèques internes Python bibliothèques. Si vous les avez configurées avec setuptools, cela installera leurs dépendances
  3. Compressez le répertoire site-packages du virtualenv. Cela inclura votre bibliothèque et ses dépendances, dont les nœuds de travail auront besoin, mais pas la bibliothèque standard Python, qu'ils ont déjà
  4. Passez le single .Zip fichier, contenant vos bibliothèques et leurs dépendances comme argument pour --py-files

Bien sûr, vous voudriez coder certains scripts d'aide pour gérer ce processus. Voici un script d'aide adapté de celui que j'ai utilisé, qui pourrait sans doute être beaucoup amélioré:

#!/usr/bin/env bash
# helper script to fulfil Spark's python packaging requirements.
# Installs everything in a designated virtualenv, then zips up the virtualenv for using as an the value of
# supplied to --py-files argument of `pyspark` or `spark-submit`
# First argument should be the top-level virtualenv
# Second argument is the zipfile which will be created, and
#   which you can subsequently supply as the --py-files argument to 
#   spark-submit
# Subsequent arguments are all the private packages you wish to install
# If these are set up with setuptools, their dependencies will be installed

VENV=$1; shift
ZIPFILE=$1; shift
PACKAGES=$*

. $VENV/bin/activate
for pkg in $PACKAGES; do
  pip install --upgrade $pkg
done
TMPZIP="$TMPDIR/$RANDOM.Zip" # abs path. Use random number to avoid clashes with other processes
( cd "$VENV/lib/python2.7/site-packages" && Zip -q -r $TMPZIP . )
mv $TMPZIP $ZIPFILE

J'ai une collection d'autres scripts wrapper simples que j'exécute pour soumettre mes travaux spark. J'appelle simplement ce script d'abord dans le cadre de ce processus et je m'assure que le deuxième argument (nom d'un fichier Zip ) est ensuite passé comme argument --py-files lorsque j'exécute spark-submit (comme indiqué dans les commentaires). J'exécute toujours ces scripts, donc je ne finis jamais d'exécuter accidentellement l'ancien code. Par rapport à la surcharge Spark, la surcharge de l'emballage est minime pour mon projet à petite échelle.

Il y a beaucoup d'améliorations qui pourraient être apportées - par exemple, savoir quand créer un nouveau fichier Zip, le diviser en deux fichiers Zip, l'un contenant des packages privés souvent changeants et l'autre contenant des dépendances rarement changeantes, qui n'ont pas besoin de être reconstruit si souvent. Vous pourriez être plus intelligent sur la vérification des modifications de fichiers avant de reconstruire le Zip. Vérifier également la validité des arguments serait une bonne idée. Cependant, pour l'instant, cela suffit pour mes besoins.

La solution que j'ai trouvée n'est pas conçue pour les dépendances à grande échelle comme NumPy spécifiquement (bien qu'elle puisse fonctionner pour elles). En outre, cela ne fonctionnera pas si vous créez des extensions basées sur C et que votre nœud de pilote a une architecture différente de vos nœuds de cluster.

J'ai vu des recommandations ailleurs pour simplement exécuter une distribution Python comme Anaconda sur tous vos nœuds car il inclut déjà NumPy (et de nombreux autres packages ), et cela pourrait être le meilleur moyen d'obtenir NumPy ainsi que d'autres extensions basées sur C. Quoi qu'il en soit, nous ne pouvons pas toujours nous attendre à ce qu'Anaconda ait le package PyPI que nous voulons dans la bonne version, et en plus vous pourriez ne pas être capable de contrôler votre Spark environnement pour pouvoir y mettre Anaconda, donc je pense que cette approche basée sur virtualenv est toujours utile.

20
Andy MacKinlay