Docker Raspberry: Jupyter kernels

Actualizado el 29 de Octubre de 2022

Docker

Jupyter tiene un sistema basado en kernels, que son procesos de ejecución independientes en un lenguaje de programación concreto, de tal forma que en Jupyter pueden convivir kernels ejecutándose en el mismo o en varios lenguajes.

En Python es posible crear entornos virtuales con configuraciones concretas de versiones de Python y paquetes, podemos usar esta idea junto con los kernels de Jupyter para crear diferentes kernels de Python.

Por ejemplo, vamos a suponer para este caso que queremos crear cuatro entornos de Python, cada una de ellas orientado a un propósito diferente con diferentes paquetes.

  • Un entorno con Python 3.9 orientado a IA pero usando la versión de TensorFlow lite que es la recomendada para Raspberry PI incluyendo los paquetes más habituales en ML (torch, pandas, keras, scikit, numpy, …) y algunos paquetes de utilidad (split-folder, BeautifulSoup4, …)
  • Un segundo entorno similar al anterior pero con la versión de TensorFlow completa compilada para Raspberry PI junto con algunos paquetes adicionales como keras-tuner.
  • Un tercer entorno similar al anterior pero orientado a Reinforcement Learning (gym, keras-rl, etc…)
  • Un cuarto entorno de ámbito más matemático (plotly, sympy, …)

En general los comandos que se usarán a continuación están pensados para Python >= 3.3, más adelante se mostrará una configuración para Docker.

Configuración

La configuración incluye dos pasos, la creación del entorno virtual en Python y la creación del kernel asociado al mismo, en este ejemplo vamos a lanzar los comandos directamente desde la consola de Jupyter.

Entorno virtual

Si no lo estuviera ya instalamos el paquete venv para poder crear los entornos, esto hay que hacerlo para cada uno de las versiones de Python que queramos usar:

apt-get install python3-venv

Si usamos la consola de Jupyter es más útil arrancar el shell bash, simplemente ejecutando bash.

Hay que tener en cuenta que si queremos crear los entornos de Python con una versión específica, por ejemplo por compatibilidad de librerías tendremos que usar el binario exacto de esa versión, por ejemplo python3.7 o python3.8, etc…

Nos movemos a la carpeta donde queramos crear el entorno para el primer escenario, para crear el entorno virtual usamos:

python3 -m venv env

En general los ficheros .gitignore están configurados para ignorar la carpeta env por lo que usar este nombre es la opción recomendada.

Esto lo hacemos para todos los demás entornos, nos movemos a la carpeta donde queramos crear este entorno y ejecutamos el mismo comando que antes para que nos cree una carpeta env igual que antes.

Ahora si queremos activar un entornos concreto, dentro de la carpeta env correspondiente ejecutamos:

source env/bin/activate

El entorno se activa para cualquier código Python que se ejecute a partir de ese momento dentro de esa carpeta por lo que se puede crear un entorno y usarlo para varios proyectos.

Si intentamos lanzar Python veremos que se ejecuta con la versión que hemos configurado para esa carpeta, por ejemplo si queremos ver la versión podemos usar:

python3 --version

Cuando queramos desactivar un entorno hacemos:

deactivate

Para eliminar el entorno virtual simplemente tenemos que borrar la carpeta que se ha creado.

rm -r env

Kernel

Para cada uno de los kernels que vamos a crear tendremos que hacer los siguientes pasos, esto habrá que hacerlo dentro de la carpeta de cada entorno y habiendo activado previamente el entorno.

Instalamos el paquete ipykernel que nos proporcionará el kernel de Python para Jupyter, este paquete a su vez requiere del paquete wheel.

python3 -m pip install --user wheel
python3 -m pip install --user ipykernel

Con esta opción –user instalaríamos los paquetes dentro de la carpeta del usuario que es la opción recomendada.

Puede ser que tengamos un error al ejecutar desde el entorno de Jupyter (ERROR: Can not perform a ‘–user’ install. User site-packages are not visible in this virtualenv) en ese caso se puede ejecutar el mismo comando sin la opción –user.

Una vez hecho esto nos movemos a la carpeta de uno de ellos, en concreto vamos a empezar por el primer escenario y vamos a instalar el kernel correspondiente.

python3 -m ipykernel install --user --display-name='Python AI' --name=envpyai

–name se refiere al nombre del kernel que veremos al usar el comando list y –display-name se refiere al nombre que veremos en Jupyter Notebook, usamos –user para instalar el kernel en la carpeta del usuario y no tener problemas de permisos, si quisiéramos instalarlo a nivel global para todos los usuarios tendríamos que ejecutarlo como root.

El comando anterior emitirá una respuesta similar a la siguiente para indicar que todo ha ido bien, en la ruta que se muestra hay un archivo llamado kernel.json que contiene toda la configuración del kernel.

Installed kernelspec env in /home/jupyter/.local/share/jupyter/kernels/env

Ahora hacemos lo mismo para el resto de configuraciones, nos movemos a la carpeta y activamos el entorno correspondiente antes de ejecutar el comando anterior.

Hay que tener en cuenta que al ser entornos virtuales vienen vacíos por lo que habrá que instalar todos los paquetes que sean necesarios para poder desarrollar.

Para ver los kernel instalados podemos usar este comando, como detalle no se pueden tener dos kernel con el mismo nombre.

jupyter kernelspec list

SI queremos eliminar algún kernel, por ejemplo la versión para Python AI haríamos:

jupyter kernelspec uninstall envpyai

Verificación

Sí todo ha ido bien veremos algo como:

Si abrimos una consola de Python veremos la versión directamente, y para verificarla en un notebook podemos ejecutar el siguiente código:

import sys
print(sys.version)

No hay que usar el siguiente código que mostrará la versión global de Python.

!python3 --version

Desde la parte superior derecha o desde el menú podremos cambiar de kernel.

Dockerfile

Este sería un ejemplo de Dockerfile que resume todos los conceptos anteriores, puede que en Docker tener los entornos virtuales no tenga sentido, pero por comodidad vamos a hacerlo en este caso.

En este caso como estamos heredando de la imagen de Jupyter creada previamente cambiamos al usuario root ya que vamos a instalar algunos paquetes.

Como cada comando RUN se ejecuta de forma aislada de los demás una forma es emular lo que haría el entorno virtual de Python para poder activarlos, otra forma es activar los entornos usando el mismo comando que es lo que se ha hecho en este caso.

FROM joursain/rpi-jupyterlab:bullseye
LABEL "guru.raraavis.creator"="blog@raraavis.guru"
LABEL "guru.raraavis.version"="1.3.0"
LABEL "guru.raraavis.release-date"="23/05/2022"
LABEL "guru.raraavis.description"="Raspberry Pi image with JupyterLab Kernels"

# 0. Prepare system
# 0.1 Required for Matplotlib
USER    root
RUN     apt-get update -y && apt-get install -y \
                libopenjp2-7 \
                libjpeg62-turbo \
                libtiff5 \
                libhdf5-dev \
                libpng-dev \
                libavcodec-dev \
                libavformat-dev \
                libswscale-dev \
                libgtk-3-dev \
                unrar-free \
                # Required for nbconvert (to PDF)
                pandoc \
                libxslt1.1 \
                texlive-xetex \
                # Required for RL
                ffmpeg \
                mediainfo \
                xvfb \
                openmpi-bin \
                openmpi-common \
                libxcb1 \
                libopenmpi3 \
                libpomp-dev \
                libomp5 \
                libopenblas-dev \
                libopenmpi-dev && \
        rm -rf /var/lib/apt/lists/* && \
        rm -rf /tmp/*

# 1. Create virtual environments
WORKDIR /home/jupyter/ai
COPY --chown=jupyter    wheels/tensorflow-2.8.0-cp39-cp39-linux_aarch64.whl .
COPY --chown=jupyter    wheels/torch-1.11.0a0+gitbc2c6ed-cp39-cp39-linux_aarch64.whl .
COPY --chown=jupyter    wheels/ale_py-0.7.5+8f3bc3b-cp39-cp39-linux_aarch64.whl .

RUN     chown -R jupyter:jupyter /home/jupyter/ai
USER    jupyter

RUN     python3 -m venv envailite
RUN     source envailite/bin/activate && \
        python3 -m pip install --upgrade pip setuptools wheel && \
        python3 -m pip install --upgrade        \
                typing-extensions               \
                ipykernel                       \
                dask[complete]                  \
                keras                           \
                numpy                           \
                scipy                           \
                scikit-learn                    \
                scikit-image                    \
                pandas                          \
                matplotlib                      \
                split-folders                   \
                opencv-contrib-python==4.5.5.62 \
                theano                          \
                seaborn                         \
                scikit-fuzzy                    \
                mlflow                          \
                pyforest                        \
                html5lib                        \
                BeautifulSoup4                  \
                scrapy                          \
                requests                        \
                bokeh                           \
                plotly                          \
                lxml                            \
                graphviz                        \
                split-folders                   \
                nltk                            && \
        python3 -m pip install --extra-index-url https://google-coral.github.io/py-repo/ tflite_runtime && \
        python3 -m pip install torch-1.11.0a0+gitbc2c6ed-cp39-cp39-linux_aarch64.whl && \
        python3 -m ipykernel install --user --display-name='Python AI Lite' --name=envpyailite && \
        deactivate


RUN     python3 -m venv envai
RUN     source envai/bin/activate && \
        python3 -m pip install --upgrade pip setuptools wheel && \
        python3 -m pip install --upgrade        \
                typing-extensions               \
                ipykernel                       \
                dask[complete]                  \
                keras                           \
                numpy                           \
                scipy                           \
                scikit-learn                    \
                scikit-image                    \
                pandas                          \
                matplotlib                      \
                split-folders                   \
                opencv-contrib-python==4.5.5.62 \
                theano                          \
                seaborn                         \
                scikit-fuzzy                    \
                mlflow                          \
                pyforest                        \
                html5lib                        \
                BeautifulSoup4                  \
                scrapy                          \
                requests                        \
                bokeh                           \
                plotly                          \
                sympy                           \
                lxml                            \
                graphviz                        \
                h5py                            \
                kaggle                          \
                split-folders                   \
                keras-tuner                     \
                nltk                            && \
        python3 -m pip install  \
                torch-1.11.0a0+gitbc2c6ed-cp39-cp39-linux_aarch64.whl \
                tensorflow-2.8.0-cp39-cp39-linux_aarch64.whl && \
        python3 -m ipykernel install --user --display-name='Python AI' --name=envpyai && \
        deactivate

RUN     python3 -m venv envpyrl
RUN     source envpyrl/bin/activate && \
        python3 -m pip install --upgrade pip setuptools wheel && \
        python3 -m pip install  \
                torch-1.11.0a0+gitbc2c6ed-cp39-cp39-linux_aarch64.whl \
                tensorflow-2.8.0-cp39-cp39-linux_aarch64.whl && \
        python3 -m pip install --upgrade        \
                typing-extensions               \
                ipykernel                       \
                dask[complete]                  \
                keras                           \
                numpy                           \
                scipy                           \
                scikit-learn                    \
                scikit-image                    \
                pandas                          \
                matplotlib                      \
                split-folders                   \
                opencv-contrib-python==4.5.5.62 \
                theano                          \
                seaborn                         \
                scikit-fuzzy                    \
                mlflow                          \
                pyforest                        \
                html5lib                        \
                BeautifulSoup4                  \
                scrapy                          \
                requests                        \
                bokeh                           \
                plotly                          \
                sympy                           \
                lxml                            \
                graphviz                        \
                nltk                            \
                gym                             \
                h5py                            \
                pillow                          \
                keras-rl2                       \
                pyvirtualdisplay                \
                keras-tuner                     && \
        python3 -m pip install  \
                ale_py-0.7.5+8f3bc3b-cp39-cp39-linux_aarch64.whl \
                torch-1.11.0a0+gitbc2c6ed-cp39-cp39-linux_aarch64.whl \
                tensorflow-2.8.0-cp39-cp39-linux_aarch64.whl && \
        python3 -m ipykernel install --user --display-name='Python RL' --name=envpyrl && \
        deactivate


RUN     python3 -m venv envmaths
RUN     source envmaths/bin/activate && \
        python3 -m pip install --upgrade pip setuptools wheel && \
        python3 -m pip install --upgrade        \
                typing-extensions               \
                ipykernel                       \
                keras                           \
                numpy                           \
                scipy                           \
                scikit-learn                    \
                pandas                          \
                matplotlib                      \
                opencv-contrib-python==4.5.5.62 \
                theano                          \
                seaborn                         \
                scikit-fuzzy                    \
                requests                        \
                bokeh                           \
                plotly                          \
                sympy                           \
                lxml                            && \
        python3 -m ipykernel install --user --display-name='Python Maths' --name=envmaths && \
        deactivate

RUN     rm torch-1.11.0a0+gitbc2c6ed-cp39-cp39-linux_aarch64.whl
RUN     rm tensorflow-2.8.0-cp39-cp39-linux_aarch64.whl

Compilación

La compilación no tiene ninguna novedad, en este caso el proceso es un poco más largo.

docker image build --tag user/image_name -f Dockerfile.ai .

Ejecución

La ejecución tampoco tiene modificaciones.

docker container run --init -p 8888:8888 --detach --volume /home/pi/jupyter:/home/jupyter/notebooks id_imagen

Referencias

https://janakiev.com/blog/jupyter-virtual-envs/
https://queirozf.com/entries/jupyter-kernels-how-to-add-change-remove

Docker Raspberry: Jupyter kernels

Docker Raspberry: Jupyter TensorFlow

Docker

Es normal que al usar Jupyter se use Tensorflow, siguiendo con el ejemplo de Docker anterior voy a poner dos ejemplos, uno para usar una configuración de Docker con la última versión de TensorFlow y otra para tener dos versiones de TensorFlow, la correspondiente a la rama 1.x y otra con la versión 2.x.

TensorFlow 1.x y 2.x

El siguiente DockerFile está basado en el anterior así que solo se comentarán las diferencias.

FROM ubuntu:latest

# 1. Environment vars
ARG DEBIAN_FRONTEND=noninteractive
ARG TZ=Europe/Madrid
ENV TZ=$TZ
ENV NODE_OPTIONS=--max-old-space-size=768

# 2. Install packages
# 2.1 Update system and install Jupyter
RUN apt-get update && apt-get -y upgrade && \
        apt-get install -y --no-install-recommends libhdf5-dev && \
        apt-get install -y --no-install-recommends tzdata && \
        apt-get install -y --no-install-recommends libzbar-dev libzbar0 && \
        apt-get install -y --no-install-recommends build-essential python3-pip python3-dev python3-venv && \
        apt-get install -y --no-install-recommends git && \
        apt-get install -y --no-install-recommends software-properties-common && \
        python3 -m pip install --upgrade virtualenv && \
        python3 -m pip install --upgrade wheel && \
        python3 -m pip install --upgrade ipykernel && \
        python3 -m pip install --upgrade pip && \
        python3 -m pip install --upgrade setuptools && \
# 2.2 Build git extension
        python3 -m pip install jupyter && \
        apt-get install -y --no-install-recommends nodejs && \
        apt-get install -y --no-install-recommends npm && \
        python3 -m pip install jupyterlab && \
        python3 -m pip install --upgrade jupyterlab-git && \
        jupyter lab build --minimize=False && \
# 2.3 Clean
        apt-get clean && \
        rm -rf /var/lib/apt/lists/* && \
        rm -rf /tmp/*

# 3 Install TensorFlow
RUN python3 -m pip install --no-cache-dir --force-reinstall grpcio
# 3.1 Install Tensorflow 1 for Python 3.7
RUN add-apt-repository ppa:deadsnakes/ppa
RUN apt-get install -y python3.7 python3.7-dev python3.7-venv
RUN python3.7 -m pip install --upgrade pip && python3.7 -m pip install setuptools
COPY tensorflow-1.14.0-cp37-none-linux_armv7l.whl ./
RUN python3.7 -m pip install tensorflow-1.14.0-cp37-none-linux_armv7l.whl
# 3.2 Install Tensorflow 2 for Python 3.8
COPY tensorflow-2.4.0rc0-cp38-none-linux_armv7l.whl ./
RUN python3 -m pip install tensorflow-2.4.0rc0-cp38-none-linux_armv7l.whl

# 4. Add Jupyter user
RUN adduser --disabled-password --gecos '' jupyter
RUN adduser jupyter sudo
RUN echo '%sudo ALL=(ALL) NOPASSWD:ALL' >> /etc/sudoers
USER jupyter
WORKDIR /home/jupyter/
RUN chmod a+rwx /home/jupyter/

# 5. Folder to download git files
RUN mkdir /home/jupyter/notebooks

# 6. Execute jupyter
CMD ["jupyter", "lab", "--port=8888", "--no-browser", "--ip=0.0.0.0", "--allow-root", "--notebook-dir=/home/jupyter/notebooks", "--NotebookApp.token=''", "--NotebookApp.password]

Hay que instalar algunos paquetes nuevos como wheel, virtualenv o ipykernel para poder usar varias versiones de Python en Jupyter a través del uso de kernels, esto se comentará más adelante para configuraciones con varias versiones de TensorFlow.

Instalamos libhdf5-dev y actualizamos el paquete setuptools para evitar un error durante la instalación del paquete grpcio y para evitar posibles errores en la creación de entornos virtuales.

El paquete software-properties-common es necesario para poder agregar repositorios personalizados que harán falta más adelante.

Install TensorFlow (3)

Este código ejecuta las dos versiones de Python, la correspondiente a la 1.14 y 2.0rc4 en el caso de grpcio lo instalamos previamente ya que va a ser usado por ambas versiones de TensorFlow y además necesita una configuración especial para no producir un error durante la instalación.

Install TensorFlow 1 para Python 3.7 (3.1)

La versión Ubuntu de RaspberryPi Buster incorpora la versión 3.8 de Python pero la última versión disponible de TensorFlow 1.x (1.4) solo funciona con Python 3.7 que no se puede instalar a través de los repositorios oficiales de Ubuntu, por lo que configuramos un repositorio alternativo (3.1) donde se encuentran versiones de Python para varias versiones de Ubuntu.

Instalamos los mismos paquetes Python que hemos instalado para la versión 3.8 pero en este caso para la versión 3.7 usando el repositorio anterior y copiamos el paquete de TensorFlow 1.4 (3.1) para poder instalarlo, el paquete lo podemos compilar o cogerlo de aquí.

Install Tensorflow 2 for Python 3.8

En el caso de TensorFlow 2 solo tenemos que copiar (3.2) la versión correspondiente que podemos copiarla del mismo repositorio anterior o compilarla como se describe aquí.

TensorFlow 2.x

La configuración de Docker sería muy similar a la anterior, simplemente quitamos la parte relacionada con TensorFlow 1.x

FROM ubuntu:latest

# 1. Environment vars
ARG DEBIAN_FRONTEND=noninteractive
ARG TZ=Europe/Madrid
ENV TZ=$TZ
ENV NODE_OPTIONS=--max-old-space-size=768

# 2. Install packages
# 2.1 Update system and install Jupyter
RUN apt-get update && apt-get -y upgrade && \
        apt-get install -y --no-install-recommends libhdf5-dev && \
        apt-get install -y --no-install-recommends tzdata && \
        apt-get install -y --no-install-recommends libzbar-dev libzbar0 && \
        apt-get install -y --no-install-recommends build-essential python3-pip python3-dev python3-venv && \
        apt-get install -y --no-install-recommends git && \
        apt-get install -y --no-install-recommends software-properties-common && \
        python3 -m pip install --upgrade virtualenv && \
        python3 -m pip install --upgrade wheel && \
        python3 -m pip install --upgrade ipykernel && \
        python3 -m pip install --upgrade pip && \
        python3 -m pip install --upgrade setuptools && \
# 2.2 Build git extension
        python3 -m pip install jupyter && \
        apt-get install -y --no-install-recommends nodejs && \
        apt-get install -y --no-install-recommends npm && \
        python3 -m pip install jupyterlab && \
        python3 -m pip install --upgrade jupyterlab-git && \
        jupyter lab build --minimize=False && \
# 2.3 Clean
        apt-get clean && \
        rm -rf /var/lib/apt/lists/* && \
        rm -rf /tmp/*

# 3 Install TensorFlow
RUN python3 -m pip install --no-cache-dir --force-reinstall grpcio
COPY tensorflow-2.4.0rc0-cp38-none-linux_armv7l.whl ./
RUN python3 -m pip install tensorflow-2.4.0rc0-cp38-none-linux_armv7l.whl

# 4. Add Jupyter user
RUN adduser --disabled-password --gecos '' jupyter
RUN adduser jupyter sudo
RUN echo '%sudo ALL=(ALL) NOPASSWD:ALL' >> /etc/sudoers
USER jupyter
WORKDIR /home/jupyter/
RUN chmod a+rwx /home/jupyter/

# 5. Folder to download git files
RUN mkdir /home/jupyter/notebooks

# 6. Execute jupyter
CMD ["jupyter", "lab", "--port=8888", "--no-browser", "--ip=0.0.0.0", "--allow-root", "--notebook-dir=/home/jupyter/notebooks", "--NotebookApp.token=''", "--NotebookApp.password=''"]

Compilación

La compilación no tiene ninguna novedad, en este caso el proceso es mucho más largo y para la configuración con dos versiones de TensorFlow puede llevar entre 8-10 horas en una Raspberry Pi 3.

docker image prune -f
docker image build --tag user/image_name .

Ejecución

La ejecución tampoco tiene modificaciones.

docker container run --init -p 8888:8888 --detach --volume /home/pi/jupyter:/home/jupyter/notebooks id_imagen
Docker Raspberry: Jupyter TensorFlow