December 9, 2022

Branches Tech

Engagé pour la qualité technologique

Spark sur l’intégration Hadoop avec Jupyter

6 min read

Depuis plusieurs années, Jupyter notebook s’est imposé comme la solution notebook dans l’univers Python. Historiquement, Jupyter est l’outil de prédilection des data scientists qui développent principalement en Python. Au fil des années, Jupyter a évolué et dispose désormais d’un large panel de fonctionnalités grâce à ses plugins. De plus, l’un des principaux avantages de Jupyter est sa facilité de déploiement.

De plus en plus de développeurs Spark préfèrent Python à Scala pour développer leurs différents travaux afin d’accélérer le développement.

Dans cet article, nous allons voir ensemble comment connecter un serveur Jupyter à un cluster Spark tournant sur Hadoop Yarn sécurisé avec Kerberos.

Comment installer Jupyter ?

Nous couvrons deux méthodes pour connecter Jupyter à un cluster Spark :

  1. Configurez un script pour lancer une instance Jupyter qui aura un interpréteur Python Spark.
  2. Connectez le bloc-notes Jupyter à un cluster Spark via l’extension Sparkmagic.

Méthode 1 : créer un script de démarrage

Conditions préalables:

  • Avoir accès à une machine de cluster Spark, généralement un nœud maître ou un nœud périphérique ;
  • Avoir un environnement (Conde, Mamba, environnement virtuel, ..) avec le paquet ‘jupyter’. Exemple avec Conda : conda create -n pysparktest python=3.7 jupyter.

Créer un script dans /home répertoire et insérez le code suivant en modifiant les chemins pour qu’ils correspondent à votre environnement :

#! /bin/bash


export PYSPARK_PYTHON=/home/adaltas/.conda/envs/pysparktest/bin/python


export PYSPARK_DRIVER_PYTHON=/home/adaltas/.conda/envs/pysparktest/bin/ipython3


export PYSPARK_DRIVER_PYTHON_OPTS="notebook --no-browser --ip=10.10.20.11--port=8888"

pyspark \
  --master yarn \
  --conf spark.shuffle.service.enabled=true \
  --conf spark.dynamicAllocation.enabled=false \
  --driver-cores 2 --driver-memory 11136m \
  --executor-cores 3 --executor-memory 7424m --num-executors 10

L’exécution de ce script crée un serveur Jupyter qui peut être utilisé pour développer vos travaux Spark.

Principaux avantages de cette solution :

  • Exécution rapide ;
  • Pas besoin de modifier la configuration du cluster ;
  • Personnalisation de l’environnement Spark par client ;
  • Environnement local du bord à partir duquel le serveur a été lancé ;
  • Environnement dédié par utilisateur qui évite les problèmes liés à la surcharge du serveur.

Principaux inconvénients de cette solution :

  • Dérive de personnalisation (utilisation de trop de ressources, mauvaise configuration, etc…) ;
  • Besoin d’avoir accès à un nœud de périphérie de cluster ;
  • L’utilisateur n’a qu’un interpréteur Python (qui est PySpark);
  • Un seul environnement (Conda ou autre) disponible par serveur.

Méthode 2 : Connecter un cluster Jupyter via Sparkmagic

Qu’est-ce que Sparkmagic ?

Sparkmagique est une extension Jupyter qui vous permet de lancer des processus Spark via Livy.


magie des étincelles

Conditions préalables:

  • Disposer d’un cluster Spark avec Livy et Spark (pour référence : HDP, CDP ou TDP) ;
  • Avoir un serveur Jupyter. JupyterHub est utilisé pour cette démonstration ;
  • Avoir configuré l’emprunt d’identité sur le cluster.

Création de l’utilisateur jupyter dans le cluster

Dans ce test, les utilisateurs sont gérés via FreeIPA sur un cluster Kerberized HDP.

Création de la jupyter utilisateur:

ipa user-add

Création du mot de passe :

ipa passwd jupyter

Vérification que l’utilisateur dispose d’un keytab sur l’un des nœuds périphériques du cluster et que l’emprunt d’identité d’utilisateur fonctionne en conséquence :

kinit jupyter
curl --negotiate -u : -i -X PUT
"http://edge01.local:9870/webhdfs/v1/user/vagrant/test?doas=vagrant&op=MKDIRS"

Remarque, la commande ci-dessus crée un /user/vagrant répertoire dans HDFS. Il nécessite des autorisations de type administrateur via l’emprunt d’identité décrit dans la section suivante.

Enfin, vérifiez que le jupyter l’utilisateur fait en effet partie du sudo groupe sur le serveur où Jupyter sera installé.

Usurpation d’identité pour l’utilisateur jupyter

Puisque nous sommes dans le cas d’un cluster HDP Kerberisé, nous devons activer l’emprunt d’identité pour le jupyter utilisateur.

Pour ce faire, modifiez le core-site.xml dossier:

<property>
    <name>hadoop.proxyuser.jupyter.hosts</name>
    <value>*</value>
</property>
<property>
    <name>hadoop.proxyuser.jupyter.groups</name>
    <value>*</value>
</property>

Installation et activation de l’extension Sparkmagic

Comme indiqué dans le Documentationvous pouvez utiliser les commandes suivantes pour installer l’extension :

pip install sparkmagic
jupyter nbextension enable --py --sys-prefix widgetsnbextension
pip3 show sparkmagic
cd /usr/local/lib/python3.6/site-packages
jupyter-kernelspec install sparkmagic/kernels/sparkkernel
jupyter-kernelspec install sparkmagic/kernels/pysparkkernel
jupyter-kernelspec install sparkmagic/kernels/sparkrkernel

Cet exemple utilise pip mais cela fonctionne également avec d’autres gestionnaires de packages Python.

Configuration de Sparkmagic

Sparkmagic a besoin que chaque utilisateur dispose des éléments suivants :

  • UN .sparkmagic répertoire à la racine de chaque utilisateur dans le /home/ annuaire;
  • Une coutume config.json fichier dans les utilisateurs .sparkmagic annuaire.

Voici un exemple config.json dossier:

{
   "kernel_python_credentials":
      "username":" username ",
      "url":"http://master02.cdp.local:8998",
      "auth":"Kerberos"
   ,
   "kernel_scala_credentials":
      "username":" username ",
      "url":"http://master02.cdp.local:8998",
      "auth":"Kerberos"
   ,
   "kernel_r_credentials":
      "username":" username ",
      "url":"http://master02.cdp.local:8998",
      "auth":"Kerberos"
   ,
   "logging_config":
      "version":1,
      "formatters":
         "magicsFormatter":
            "format":"%(asctime)s\t%(levelname)s\t%(message)s",
            "datefmt":""
         
      ,
      "handlers":
         "magicsHandler":
            "class":"hdijupyterutils.filehandler.MagicsFileHandler",
            "formatter":"magicsFormatter",
            "home_path":"~/.sparkmagic"
         
      ,
      "loggers":
         "magicsLogger":
            "handlers":[
               "magicsHandler"
            ],
            "level":"DEBUG",
            "propagate":0
         
      
   ,
   "authenticators":
      "Kerberos":"sparkmagic.auth.kerberos.Kerberos",
      "None":"sparkmagic.auth.customauth.Authenticator",
      "Basic_Access":"sparkmagic.auth.basic.Basic"
   ,
   "wait_for_idle_timeout_seconds":15,
   "livy_session_startup_timeout_seconds":60,
   "fatal_error_suggestion":"The code failed because of a fatal error:\n\t.\n\nSome things to try:\na) Make sure Spark has enough available resources for Jupyter to create a Spark context.\nb) Contact your Jupyter administrator to make sure the Sparkmagic library is configured correctly.\nc) Restart the kernel.",
   "ignore_ssl_errors":false,
   "session_configs":
      "driverMemory":"1000M",
      "executorCores":2,
      "conf":
         "spark.master":"yarn-cluster"
      ,
      "proxyUser":"jupyter"
   ,
   "use_auto_viz":true,
   "coerce_dataframe":true,
   "max_results_sql":2500,
   "pyspark_dataframe_encoding":"utf-8",
   "heartbeat_refresh_seconds":30,
   "livy_server_heartbeat_timeout_seconds":0,
   "heartbeat_retry_seconds":10,
   "server_extension_default_kernel_name":"pysparkkernel",
   "custom_headers":

   ,
   "retry_policy":"configurable",
   "retry_seconds_to_sleep_list":[
      0.2,
      0.5,
      1,
      3,
      5
   ],
}

Éditer /etc/jupyterhub/jupyterhub_config.py – SparkMagic

Dans mon cas, j’ai décidé de modifier le /etc/jupyterhub/jupyterhub_config.py fichier afin d’automatiser certains processus liés à SparkMagic :

  • Création de la .sparkmagic dossier est le /home/ répertoire de chaque nouvel utilisateur ;
  • Générer le config.json dossier.
c.LDAPAuthenticator.create_user_home_dir = True
import os
import jinja2
import sys, getopt
from pathlib import Path
from subprocess import check_call
def config_spark_magic(spawner):
    username = spawner.user.name
    templateLoader = jinja2.FileSystemLoader(searchpath="/etc/jupyterhub/")
    templateEnv = jinja2.Environment(loader=templateLoader)
    TEMPLATE_FILE = "config.json.template"

    tm = templateEnv.get_template(TEMPLATE_FILE)
    msg = tm.render(username=username)

    path = "/home/" + username + "/.sparkmagic/"
    Path(path).mkdir(mode=0o777, parents=True, exist_ok=True)

    outfile = open(path + "config.json", "w")
    outfile.write(msg)
    outfile.close()
    os.popen('sh /etc/jupyterhub/install_jupyterhub.sh ' + username)
c.Spawner.pre_spawn_hook = config_spark_magic
c.JupyterHub.authenticator_class = 'ldapauthenticator.LDAPAuthenticator'
c.LDAPAuthenticator.server_hosts = ['ipa.cdp.local']
c.LDAPAuthenticator.server_port = 636
c.LDAPAuthenticator.server_use_ssl = True
c.LDAPAuthenticator.server_pool_strategy = 'FIRST'
c.LDAPAuthenticator.bind_user_dn = 'uid=admin,cn=users,cn=accounts,dc=cdp,dc=local'
c.LDAPAuthenticator.bind_user_password = 'passWord1'
c.LDAPAuthenticator.user_search_base = 'cn=users,cn=accounts,dc=cdp,dc=local'
c.LDAPAuthenticator.user_search_filter = '(&(objectClass=person)(uid=username))'
c.LDAPAuthenticator.user_membership_attribute = 'memberOf'
c.LDAPAuthenticator.group_search_base = 'cn=groups,cn=accounts,dc=cdp,dc=local'
c.LDAPAuthenticator.group_search_filter = '(&(objectClass=ipausergroup)(memberOf=group))'

Principaux avantages de cette solution :

  • A trois interprètes via Sparkmagic (Python, Scala et R);
  • Personnalisation des ressources Spark via config.json dossier;
  • Pas besoin d’avoir un accès physique au cluster pour les utilisateurs ;
  • Possibilité d’avoir plusieurs environnements Python disponibles ;
  • Connexion de JupyterHub avec un LDAP.

Principaux inconvénients de cette solution :

  • Dérive de personnalisation (utilisation de trop de ressources, mauvaise configuration, etc…) ;
  • Modification de la configuration HDP/CDP ;
  • Déploiement plus complexe ;
  • Un notebook n’a qu’un seul interprète.

Conclusion

Si vous développez des travaux Spark et des solutions historiques telles que Zeppelin ne vous convient plus ou que vous êtes limité par sa version, vous pouvez désormais mettre en place un serveur Jupyter à moindre frais pour développer vos jobs tout en profitant des ressources de vos clusters.

Leave a Reply