Montando un entorno multiusuario para Jupyter con JupyterHub

Logo del proyecto JupyterEn anteriores entradas describimos qué era Jupyter Notebook y cómo nos permitía crear y compartir documentos interactivos que contienen código fuente, ecuaciones, texto y visualización de datos. Su potencial en el entorno científico y académico es muy grande, no solo para el análisis de datos, sino para poder reproducir los resultados de una investigación, lo que le ha servido para aparecer en la reconocida revista científica Nature. En esta entrada instalaremos un entorno multiusuario para Jupyter con JupyterHub.

JupyterHub es un servidor que permite el acceso simultáneo de varios usuarios a sus notebooks, ejecutando un servidor Jupyter de manera independiente para cada uno de ellos. Esta funcionalidad evita que los usuarios tengan que instalar el entorno de ejecución en su máquina local (algo especialmente importante para usuarios sin conocimientos informáticos) y comenzar a ser productivos desde el minuto cero. Para acceder al entorno de usuario solamente sería necesaria la dirección web del servidor y el usuario y contraseña de acceso a la plataforma.

En JupyterHub hay tres procesos principales:

  • Servidor de usuario: Es una instancia de Jupyter Notebook dedicada para cada usuario, que se crea cuando éste se autentica.
  • Proxy: Se encarga de enrutar las peticiones HTTP del componente hub a los servidores de usuario.
  • Hub: Gestiona las cuentas de usuario y genera los servidores de usuario bajo demanda. El responsable de crear estos procesos es el spawner.

El proxy es el único de los tres procesos que necesita una dirección de red pública. El hub está accesible a través de la dirección del proxy en /hub y los servidores de usuario está accesibles en /user/[usuario].

Para describir la instalación y configuración de JupyterHub, utilizaremos una instancia del entorno cloud de CETA, basado en OpenStack, partiendo de una imagen base con el sistema operativo CentOS 7. Utilizaremos una configuración con 2 cores, 2GB de RAM y 20 GB de almacenamiento en disco.

Dependencias

JupyterHub necesita al menos Python 3.3 para funcionar. Para verificar la versión se puede utilizar el comando:

whereis -b python

Si Python3 no aparece en el listado, lo instalaremos vía yum.

sudo yum search python3
sudo yum -y install python34

NOTA: Si la búsqueda no devuelve resultados recomendamos habilitar el repositorio EPEL con el siguiente comando:

sudo yum install -y epel-release

Adicionalmente y debido a que algunas dependencias de Jupyter requieren compilar fuentes, instalaremos las herramientas de desarrollo:

sudo yum -y groupinstall 'Development Tools'
sudo yum -y install python34-devel

Por otro lado, instalaremos el gestor de módulos pip para Python 3 que necesitaremos para instalar JupyterHub. Previamente instalaremos wget para descargar el instalador de pip3:

sudo yum -y install wget
wget https://bootstrap.pypa.io/get-pip.py
chmod +x ./get-pip.py
sudo python3.4 get-pip.py

Para la instalación del proxy también es necesario instalar NodeJS y su gestor de paquetes npm:

sudo yum install -y npm
sudo npm install -g configurable-http-proxy

Instalación

Una vez instaladas las dependencias, instalamos JupyterHub vía pip3:

sudo pip3 install jupyterhub

Et voilà, ya está instalado JupyterHub. Para ejecutarlo solamente deberemos lanzar el comando sudo jupyterhub –no-ssl y acceder a la dirección http://<proxy-ip>:8000.

NOTA: El flag –no-ssl es necesario porque por defecto JupyterHub se ejecuta con SSL.

Esta práctica no es recomendada para redes públicas ya que las comunicaciones no están encriptadas. Para ejecutar JupyterHub con soporte SSL deberemos especificar la ruta de los certificados como parámetros:

sudo jupyterhub --ssl-key /path/to/ssl.key --ssl-cert /path/to/ssl.cert

Finalmente, como vamos a ejecutar los servidores de usuario en la misma máquina que el hub, es necesario actualizar el módulo notebook. Para ello lo actualizamos vía pip3:

sudo pip3 --upgrade notebook

En este punto JupyterHub está ejecutándose con las opciones por defecto. Es necesario que se esté ejecutando con un usuario con privilegios (de ahí el uso de sudo) para poder lanzar los procesos como otros usuarios.Configuración por defecto

Por otro lado, con la configuración por defecto los servidores de usuario se ejecutarán en la misma máquina que el hub y, tal y como vimos anteriormente, el proxy escucha en el puerto 8000.

A título informativo, JupyterHub generará otros dos ficheros en disco en el directorio de trabajo actual:

  • jupyterhub.sqlite: Es la base de datos que contiene el estado del hub, permitiéndole conocer qué usuarios tienen servidores en ejecución y dónde, entre otra información relevante para su correcto funcionamiento.
  • jupyterhub_cookie_secret: es la clave de encriptación para securizar las cookies. Es necesario para que el hub no invalide las cookies tras un reinicio. Consecuentemente, si se elimina y se reinicia JupyterHub todas las cookies de autenticación quedarán invalidadas.

Toda esta configuración puede modificarse ya que JupyterHub es altamente configurable. En entradas posteriores describiremos con más algunas opciones de configuración para continuar con la puesta en marcha de nuestra instancia del hub.

Por último, debemos comentar que, por defecto, JupyterHub autentica a los usuarios con su usuario y contraseña de sistema (vía PAM) por lo que cualquier usuario de la máquina podrá trabajar con la aplicación.

En nuestro caso particular, la imagen por defecto del entorno cloud de CETA que hemos utilizado no proporciona un usuario con contraseña con el que podamos probar el hub, por lo que nos deberemos crear uno. Para ello lanzamos el siguiente comando:

sudo adduser josemi

Hecho esto, podemos entrar en el hub y crear y ejecutar nuestro primer notebook:

Extras

Para los más impacientes, el proyecto tiene también una imagen en el DockerHub con la que podréis trabajan lanzando el siguiente comando:

docker run -d --name jupyterhub jupyter/jupyterhub jupyterhub --no-ssl

Esta imagen no tiene configurado un usuario por defecto, por lo que tendréis que entrar en el contenedor y crear el usuario a mano. Por otro lado, hemos mapeado el puerto 8000 al de nuestra máquina local y lo ejecutamos con el flag –no-ssl para lanzarlo sin soporte HTTPS (recordemos que sin este parámetro el arranque abortaría por no especificar la ruta del certificado).

Referencias

A continuación os dejamos algunas referencias que utilizadas para la elaboración de esta entrada de blog. Espero que os resulten útiles 🙂