Spark sur l’intégration Hadoop avec Jupyter
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 :
- Configurez un script pour lancer une instance Jupyter qui aura un interpréteur Python Spark.
- 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.
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’usurpation 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.