Desplegamos Jupyter en la órbita LXD

¿Alguna vez has experimentado con el código o utilidades del sistema en Linux de manera que no tengas que preocuparte por el sistema base y no arruinar todo por un error en el código que debe ejecutarse con privilegios de root?

¿Qué tal si, por ejemplo, necesitas probar o ejecutar todo un clúster de diversos microservicios en una sola máquina? ¿Cientos o incluso miles?

Con máquinas virtuales gestionadas por un hipervisor, se pueden resolver estas tareas, pero ¿a qué costo? Por ejemplo, un contenedor en LXD basado en la distribución Alpine Linux consume mínimamente solo 7.60MB de RAM, y donde la partición raíz después de iniciar ocupa 9.5MB! ¿Qué te parece eso, Elon Musk? Te recomiendo que te familiarices con las capacidades básicas de LXD — un sistema de contenedores en Linux.

Después de que quedó claro qué son los contenedores LXD, avancemos y pensemos, ¿qué pasaría si existiera una plataforma-máquina donde se pudiera ejecutar código para el host de manera segura, generar gráficos, vincular dinámicamente (de forma interactiva) widgets de UI con tu código, complementar el código con texto que incluya blackjack… ¿y formato? ¿Algo así como un blog interactivo? ¡Wow… Quiero! ¡Quiero! 🙂

Mira debajo del spoiler donde ejecutaremos en un contenedor JupyterLab — la próxima generación de interfaz de usuario en lugar del obsoleto Jupyter Notebook, además de instalar módulos de Python como NumPy, Pandas, Matplotlib, IPyWidgets que permitirán hacer todo lo mencionado anteriormente y guardar todo en un archivo especial: un cuaderno IPython.

Desplegamos Jupyter en la órbita LXD

Plan de lanzamiento al espacio ^

Desplegamos Jupyter en la órbita LXD

Esbocemos un plan de acción breve para facilitar la implementación del esquema anterior:

  • Instalaremos y ejecutaremos un contenedor basado en la distribución Alpine Linux. Usaremos esta distribución ya que se centra en la minimalismo y solo instalaremos el software más necesario, nada más.
  • Añadiremos un disco virtual adicional en el contenedor al que le daremos el nombre — hostfs y montaremos al sistema de archivos raíz. Este disco nos permitirá utilizar archivos en el host desde el directorio especificado dentro del contenedor. De esta manera, los datos serán independientes del contenedor. Si se elimina el contenedor, los datos permanecerán en el host. Además, este esquema es útil para compartir algunos datos entre muchos contenedores sin usar los mecanismos de red estándar del sistema de distribución del contenedor.
  • Instalaremos Bash, sudo, las bibliotecas necesarias, y añadiremos y configuraremos un usuario del sistema.
  • Instalaremos Python, los módulos y compilaremos sus dependencias binarias.
  • Instalaremos y ejecutaremos. JupyterLab, configuraremos la apariencia e instalaremos extensiones para él.

En este artículo comenzaremos con el lanzamiento del contenedor, no vamos a revisar la instalación y configuración de LXD, toda esa información la puedes encontrar en otro artículo — Las capacidades básicas de LXD: el sistema de contenedores en Linux.

Instalación y configuración del sistema base ^

Creamos el contenedor con el comando donde especificamos la imagen — alpine3, el identificador para el contenedor — jupyterlab y, si es necesario, perfiles de configuración:

lxc init alpine3 jupyterlab --profile=default --profile=hddroot

Aquí estoy usando el perfil de configuración hddroot que indica crear un contenedor con una partición root en Grupo de Almacenamiento ubicado en un disco duro físico:

lxc profile show hddroot

config: {}
description: ""
devices:
  root:
    path: 
    pool: hddpool
    type: disk
name: hddroot
used_by: []
lxc storage show hddpool

config:
  size: 10GB
  source: /dev/loop1
  volatile.initial_source: /dev/loop1
description: ""
name: hddpool
driver: btrfs
used_by:
- /1.0/images/ebd565585223487526ddb3607f5156e875c15a89e21b61ef004132196da6a0a3
- /1.0/profiles/hddroot
status: Created
locations:
- none

Esto me da la oportunidad de experimentar con contenedores en un disco duro, ahorrando recursos del disco SSD que también tengo en mi sistema 🙂 para el cual he creado un perfil de configuración separado. ssdroot.

Después de crear el contenedor, se encuentra en estado STOPPED, por lo que necesitamos iniciarlo ejecutando el sistema init en él:

lxc start jupyterlab

Mostramos la lista de contenedores en LXD usando la opción — el número de paquetes, después de los cuales que indica cuáles ccolumnas mostrar en pantalla:

lxc list -c ns4b
+------------+---------+-------------------+--------------+
|    NAME    |  STATE  |       IPV4        | STORAGE POOL |
+------------+---------+-------------------+--------------+
| jupyterlab | RUNNING | 10.0.5.198 (eth0) | hddpool      |
+------------+---------+-------------------+--------------+

Al crear el contenedor, la dirección IP se eligió al azar, ya que usamos un perfil de configuración default que fue configurado previamente en el artículo. Las capacidades básicas de LXD: el sistema de contenedores en Linux.

Cambiaremos esta dirección IP a una más memorable, creando una interfaz de red a nivel de contenedor, y no a nivel de perfil de configuración como en la configuración actual. No es obligatorio hacerlo, puedes omitir este paso.

Creando la interfaz de red eth0 que vinculamos con el conmutador (puente de red) lxdbr0 en la que habilitamos NAT según el artículo anterior y el contenedor ahora tendrá acceso a Internet, además, asignamos una dirección IP estática a la interfaz — 10.0.5.5:

lxc config device add jupyterlab eth0 nic name=eth0 nictype=bridged parent=lxdbr0 ipv4.address=10.0.5.5

Después de agregar el dispositivo, es necesario reiniciar el contenedor:

lxc restart jupyterlab

Verificamos el estado del contenedor:

lxc list -c ns4b
+------------+---------+------------------+--------------+
|    NAME    |  STATE  |       IPV4       | STORAGE POOL |
+------------+---------+------------------+--------------+
| jupyterlab | RUNNING | 10.0.5.5 (eth0)  | hddpool      |
+------------+---------+------------------+--------------+

Instalación del software básico y configuración del sistema ^

Para administrar nuestro contenedor, es necesario instalar el siguiente software:

Package
Descripción

bash
El shell GNU Bourne Again

bash-completion
Finalización programable para el shell bash

sudo
Dar a ciertos usuarios la capacidad de ejecutar algunos comandos como root

shadow
Conjunto de herramientas de gestión de contraseñas y cuentas con soporte para archivos de sombra y PAM

tzdata
Fuentes para la zona horaria y datos de horario de verano

nano
Clone del editor Pico con mejoras

Además, puedes instalar soporte en el sistema de man-pages instalando los siguientes paquetes — man man-pages mdocml-apropos less

lxc exec jupyterlab -- apk add bash bash-completion sudo shadow tzdata nano

Analicemos los comandos y las claves que utilizamos:

  • lxc — Llamada al cliente LXD
  • exec — Método del cliente LXD que ejecuta un comando en el contenedor
  • jupyterlab — Identificador del contenedor
  • -- — Clave especial que indica no interpretar más las claves como tales lxc y pasar toda la cadena restante tal como está al contenedor
  • apk — Gestor de paquetes de la distribución Alpine Linux
  • add — Método del gestor de paquetes que instala los paquetes especificados después del comando

A continuación, estableceremos la zona horaria en el sistema Europe/Moscow:

lxc exec jupyterlab -- cp /usr/share/zoneinfo/Europe/Moscow /etc/localtime

Después de establecer la zona horaria, el paquete tzdata ya no es necesario en el sistema, ocupará espacio, así que lo eliminaremos:

lxc exec jupyterlab -- apk del tzdata

Verificamos la zona horaria:

lxc exec jupyterlab -- date

Wed Apr 15 10:49:56 MSK 2020

Para no perder mucho tiempo configurando Bash para nuevos usuarios en el contenedor, realizaremos las siguientes acciones: copiaremos de la sistema host los archivos skel ya listos. Esto permitirá embellecer Bash en el contenedor en modo interactivo. Mi sistema host es Manjaro Linux y los archivos copiados /etc/skel/.bash_profile, /etc/skel/.bashrc, /etc/skel/.dir_colors En principio, son adecuados para Alpine Linux y no causan problemas críticos, pero puede que usted tenga otra distribución y necesitará resolverlo por sí mismo en caso de error al iniciar Bash en el contenedor.

Copiamos los archivos skel al contenedor. La clave --create-dirs creará los directorios necesarios si no existen:

lxc file push /etc/skel/.bash_profile jupyterlab/etc/skel/.bash_profile --create-dirs
lxc file push /etc/skel/.bashrc jupyterlab/etc/skel/.bashrc
lxc file push /etc/skel/.dir_colors jupyterlab/etc/skel/.dir_colors

Para el usuario root ya existente, copiaremos a su directorio personal los archivos skel recién copiados al contenedor:

lxc exec jupyterlab -- cp /etc/skel/.bash_profile /root/.bash_profile
lxc exec jupyterlab -- cp /etc/skel/.bashrc /root/.bashrc
lxc exec jupyterlab -- cp /etc/skel/.dir_colors /root/.dir_colors

En Alpine Linux, se establece una shell del sistema para los usuarios /bin/sh, la reemplazaremos en root el usuario por Bash:

lxc exec jupyterlab -- usermod --shell=/bin/bash root

Para root el usuario no era sin contraseña, necesita establecer una contraseña. El siguiente comando generará y configurará una nueva contraseña aleatoria que verá en la consola después de su ejecución:

lxc exec jupyterlab -- /bin/bash -c "PASSWD=$(head /dev/urandom | tr -dc A-Za-z0-9 | head -c 12); echo "root:$PASSWD" | chpasswd && echo "New Password: $PASSWD""

New Password: sFiXEvBswuWA

Además, crearemos un nuevo usuario del sistema — jupyter para el cual configuraremos más tarde JupyterLab:

lxc exec jupyterlab -- useradd --create-home --shell=/bin/bash jupyter

Generaremos y estableceremos una contraseña para él:

lxc exec jupyterlab -- /bin/bash -c "PASSWD=$(head /dev/urandom | tr -dc A-Za-z0-9 | head -c 12); echo "jupyter:$PASSWD" | chpasswd && echo "New Password: $PASSWD""

New Password: ZIcbzWrF8tki

A continuación, ejecutaremos dos comandos, el primero creará un grupo del sistema sudo, y el segundo añadirá al usuario a él jupyter:

lxc exec jupyterlab -- groupadd --system sudo
lxc exec jupyterlab -- groupmems --group sudo --add jupyter

Veremos a qué grupos pertenece el usuario jupyter:

lxc exec jupyterlab -- id -Gn jupyter

jupyter sudo

Todo está bien, continuemos.

Permitiremos que todos los usuarios que pertenecen al grupo sudo utilicen el comando sudo. Para hacer esto, ejecute el siguiente script, que sed descomentar la línea del parámetro en el archivo de configuración /etc/sudoers:

lxc exec jupyterlab -- /bin/bash -c "sed --in-place -e '/^#[ t]*%sudo[ t]*ALL=(ALL)[ t]*ALL$/ s/^[# ]*// ' /etc/sudoers"

Instalación y configuración de JupyterLab ^

JupyterLab — es una aplicación de Python, por lo que primero debemos instalar este intérprete. Además, JupyterLab vamos a instalar mediante el gestor de paquetes de Python pip, y no el del sistema, porque en el repositorio del sistema puede estar desactualizado, y por eso debemos resolver manualmente sus dependencias instalando los siguientes paquetes — python3 python3-dev gcc libc-dev zeromq-dev:

lxc exec jupyterlab -- apk add python3 python3-dev gcc libc-dev zeromq-dev

Actualicemos los módulos de python y el gestor de paquetes pip a la versión más reciente:

lxc exec jupyterlab -- python3 -m pip install --upgrade pip setuptools wheel

Instalamos JupyterLab a través del gestor de paquetes pip:

lxc exec jupyterlab -- python3 -m pip install jupyterlab

Dado que las extensiones en JupyterLab son experimentales y no se incluyen oficialmente con el paquete jupyterlab, debemos instalarlas y configurarlas manualmente.

Instalemos NodeJS y su gestor de paquetes — NPM, ya que JupyterLab los utiliza para sus extensiones:

lxc exec jupyterlab -- apk add nodejs npm

Para que las extensiones de JupyterLab que instalemos funcionen, deben instalarse en el directorio de usuario, ya que la aplicación se ejecutará como usuario jupyter. El problema es que no hay un parámetro en el comando de inicio para pasar el directorio; la aplicación solo reconoce la variable de entorno y por lo tanto debemos definirla. Para ello, escribiremos el comando de exportación de la variable JUPYTERLAB_DIR en el entorno del usuario jupyter, en el archivo .bashrc, que se ejecuta cada vez que el usuario inicia sesión en el sistema:

lxc exec jupyterlab -- su -l jupyter -c "echo -e "nexport JUPYTERLAB_DIR=$HOME/.local/share/jupyter/lab" >> .bashrc"

El siguiente comando instalará una extensión especial — el gestor de extensiones en JupyterLab:

lxc exec jupyterlab -- su -l jupyter -c "export JUPYTERLAB_DIR=$HOME/.local/share/jupyter/lab; jupyter labextension install --no-build @jupyter-widgets/jupyterlab-manager"

Ahora ya está todo listo para el primer lanzamiento JupyterLab, pero aún podemos instalar algunas extensiones útiles:

  • toc — Tabla de Contenidos, genera una lista de encabezados en el artículo/cuaderno
  • jupyterlab-horizon-theme — Tema de interfaz de usuario
  • jupyterlab_neon_theme — Tema de interfaz de usuario
  • jupyterlab-ubu-theme — Otro tema del autor de este artículo 🙂 Pero en este caso, la instalación se mostrará desde el repositorio de GitHub

Así que, ejecute los siguientes comandos en secuencia para instalar estas extensiones:

lxc exec jupyterlab -- su -l jupyter -c "export JUPYTERLAB_DIR=$HOME/.local/share/jupyter/lab; jupyter labextension install --no-build @jupyterlab/toc @mohirio/jupyterlab-horizon-theme @yeebc/jupyterlab_neon_theme"
lxc exec jupyterlab -- su -l jupyter -c "wget -c https://github.com/microcoder/jupyterlab-ubu-theme/archive/master.zip"
lxc exec jupyterlab -- su -l jupyter -c "unzip -q master.zip && rm master.zip"
lxc exec jupyterlab -- su -l jupyter -c "export JUPYTERLAB_DIR=$HOME/.local/share/jupyter/lab; jupyter labextension install --no-build jupyterlab-ubu-theme-master"
lxc exec jupyterlab -- su -l jupyter -c "rm -r jupyterlab-ubu-theme-master"

Después de instalar las extensiones, debemos compilarlas, ya que anteriormente, al instalarlas, se especificó la clave --no-build para ahorrar tiempo. Ahora nos aceleraremos significativamente compilándolos juntos de una sola vez:

lxc exec jupyterlab -- su -l jupyter -c "export JUPYTERLAB_DIR=$HOME/.local/share/jupyter/lab; jupyter lab build"

Ahora ejecute los siguientes dos comandos para el primer inicio JupyterLab. Podría haberse iniciado con un solo comando, pero en este caso, el comando de inicio que memorizamos quedaría almacenado en el bash dentro del contenedor, y no en el host, donde ya hay suficientes comandos para grabar en el historial 🙂

Iniciamos sesión en el contenedor como usuario jupyter:

lxc exec jupyterlab -- su -l jupyter

Luego, ejecute JupyterLab con las claves y parámetros como se indica:

[jupyter@jupyterlab ~]$ jupyter lab --ip=0.0.0.0 --no-browser

Vaya en el navegador web a la dirección http://10.0.5.5:8888 y en la página que se abre, ingrese token de acceso que verá en la consola. Copie este token y pégalo en la página, luego haga clic en Iniciar sesión. Después de iniciar sesión, dirígete al menú de extensiones a la izquierda, como se muestra en la imagen de abajo, donde se le pedirá que al activar el gestor de extensiones acepte los riesgos de seguridad al instalar extensiones de terceros por las cuales el equipo desarrollo de JupyterLab no se hace responsable:

Desplegamos Jupyter en la órbita LXD

Sin embargo, estamos aislando completamente JupyterLab y colocándolo en un contenedor para que las extensiones de terceros que requieren y utilizan NodeJS no puedan, al menos, robar datos del disco más allá de aquellos que abramos dentro del contenedor. Es poco probable que lleguen a sus documentos privados en el host /home es poco probable que los procesos del contenedor lo logren, y si lo hacen, necesitarán tener privilegios sobre los archivos en el sistema host, ya que estamos ejecutando el contenedor en modo no privilegiado. A partir de esta información, puede evaluar el riesgo de habilitar extensiones en JupyterLab.

Los cuadernos IPython creados (páginas en JupyterLab) ahora se crearán en el directorio personal del usuario — /home/jupyter, pero en nuestros planes está dividir los datos (compartir) entre el host y el contenedor, así que regrese a la consola y detenga JupyterLab ejecutando la tecla de acceso rápido — CTRL+C y respondiendo y a la solicitud. Luego, termine la sesión interactiva del usuario jupyter ejecutando la tecla de acceso rápido CTRL+D.

Compartimos datos con el host ^

Para dividir los datos con el host, necesita crear en el contenedor un dispositivo que permita hacerlo, y para ello, ejecute el siguiente comando donde indicamos las siguientes claves:

  • lxc config device add — El comando agrega la configuración del dispositivo
  • jupyter — Identificador del contenedor en el que se añade la configuración
  • hostfs — Identificador del dispositivo. Puede asignar cualquier nombre.
  • disco — Se especifica el tipo de dispositivo
  • path — Se indica la ruta en el contenedor a la que LXD montará este dispositivo
  • source — Se indica la fuente, la ruta al directorio en el host que desea compartir con el contenedor. Especifique la ruta según sus preferencias
lxc config device add jupyterlab hostfs disco path=\mnt\hostfs source=\home\dv\projects\ipython-notebooks

Para el directorio /home/dv/projects/ipython-notebooks debe haberse otorgado permiso al usuario del contenedor que actualmente tiene UID igual a SubUID + UID, consulte el capítulo Seguridad. Privilegios de los contenedores en el artículo Las capacidades básicas de LXD: el sistema de contenedores en Linux.

Otorgamos permiso en el host, donde el usuario propietario será el usuario del contenedor jupyter, y la variable $USER indica a su usuario del host como grupo:

sudo chown 1001000:$USER \home\dv\projects\ipython-notebooks

¡Hola, mundo! ^

Si aún tiene una sesión de consola abierta en el contenedor con JupyterLab, reiníciela con la nueva opción --notebook-dir especificando el valor /mnt/hostfs como la ruta a la raíz de los notebooks en el contenedor para el dispositivo que creamos en el paso anterior:

jupyter lab --ip=0.0.0.0 --no-browser --notebook-dir=\mnt\hostfs

Luego, dirígete a la página http://10.0.5.5:8888 y crea tu primer notebook haciendo clic en el botón de la página como se indica en la imagen de abajo:

Desplegamos Jupyter en la órbita LXD

Luego, en el campo de la página ingrese el código en Python que generará el clásico ¡Hola Mundo!. Al finalizar la entrada, presione CTRL+ENTER o el botón «play» en la barra de herramientas de arriba para que JupyterLab lo ejecute:

Desplegamos Jupyter en la órbita LXD

En este punto, casi todo está listo para usarse, pero será más interesante si instalamos módulos adicionales de Python (aplicaciones completas) que permiten expandir significativamente las capacidades estándar de Python en JupyterLab, así que sigamos 🙂

P.D. Lo interesante es que la antigua implementación Jupyter bajo el nombre clave Jupyter Notebook no ha desaparecido y existe en paralelo con JupyterLab. Para acceder a la versión antigua, visite el enlace agregando el sufijo en la dirección/tree, y el acceso a la nueva versión se realiza con el sufijo /lab, aunque no es necesario indicarlo:

Ampliando las capacidades de Python ^

En esta sección instalaremos módulos poderosos del lenguaje Python cuyas NumPy, Pandas, Matplotlib, IPyWidgets resultados se integrarán en los notebooks JupyterLab.

Antes de instalar los módulos de Python mencionados a través del gestor de paquetes pip debemos primero habilitar las dependencias del sistema en Alpine Linux:

  • g++ — Necesario para compilar módulos, ya que algunos de ellos están implementados en el lenguaje C++ y se conectan a Python en tiempo de ejecución como módulos binarios
  • freetype-dev — dependencia para el módulo de Python Matplotlib

Instalamos las dependencias:

lxc exec jupyterlab -- apk add g++ freetype-dev

Hay un problema, en el estado actual de la distribución de Alpine Linux no se podrá compilar una nueva versión de NumPy, se producirá un error de compilación que no he podido resolver:

ERROR: No se pudieron construir las ruedas para numpy que usan PEP 517 y no se pueden instalar directamente

Por lo tanto, instalaremos este módulo como un paquete del sistema que ya distribuye una versión compilada, aunque un poco más antigua que la disponible actualmente en el sitio:

lxc exec jupyterlab -- apk add py3-numpy py3-numpy-dev

A continuación, instalamos los módulos de Python a través del gestor de paquetes pip. Tengan paciencia, ya que algunos módulos se compilarán y esto tomará unos minutos. En mi máquina, la compilación tardó aproximadamente 15 minutos:

lxc exec jupyterlab -- python3 -m pip install pandas ipywidgets matplotlib

Limpiamos las cachés de las instalaciones:

lxc exec jupyterlab -- rm -rf /home/*/.cache/pip/*
lxc exec jupyterlab -- rm -rf /root/.cache/pip/*

Probamos módulos en JupyterLab ^

Si tienes JupyterLab, reinícialo para que los nuevos módulos instalados se activen. Para ello, en la sesión de consola presiona CTRL+C donde se está ejecutando y escribe y en la solicitud de detención, luego reinicia JupyterLab presionando la flecha hacia arriba en el teclado, para no tener que volver a escribir el comando y luego Enter para iniciar:

jupyter lab --ip=0.0.0.0 --no-browser --notebook-dir=\mnt\hostfs

Ve a la página http://10.0.5.5:8888/lab o actualiza la página en el navegador, y luego ingresa el siguiente código en una nueva celda del cuaderno:

%matplotlib inline

from ipywidgets import interactive
import matplotlib.pyplot as plt
import numpy as np

def f(m, b):
    plt.figure(2)
    x = np.linspace(-10, 10, num=1000)
    plt.plot(x, m * x + b)
    plt.ylim(-5, 5)
    plt.show()

interactive_plot = interactive(f, m=(-2.0, 2.0), b=(-3, 3, 0.5))
output = interactive_plot.children[-1]
output.layout.height = '350px'
interactive_plot

Deberías obtener un resultado como en la imagen de abajo, donde IPyWidgets generar un elemento de interfaz de usuario en la página que interactúa de forma interactiva con el código fuente, y también Matplotlib muestra el resultado del código en forma de imagen como el gráfico de la función:

Desplegamos Jupyter en la órbita LXD

Muchos ejemplos IPyWidgets puedes encontrar en tutoriales aquí

¿Qué más? ^

¡Bien hecho si has llegado hasta el final del artículo! No incluí un script listo al final del artículo que lo instalaría JupyterLab en «un clic», para animar a los trabajadores 🙂 Pero puedes hacerlo tú mismo, ya que ya sabes cómo, juntando los comandos en un solo script Bash 🙂

Además, puedes:

  • Establecer un nombre de red para el contenedor en lugar de la dirección IP escribiéndolo en texto plano /etc/hosts y en el navegador ingresar la dirección http://jupyter.local:8888
  • Jugar con la limitación de recursos para el contenedor, para ello lea el capítulo en las capacidades básicas de LXD o obtenga más información en el sitio del desarrollador de LXD.
  • Cambiar el tema visual:

Desplegamos Jupyter en la órbita LXD

¡Y hay mucho más que puedes hacer! Eso es todo. ¡Te deseo éxito!

ACTUALIZACIÓN: 15.04.2020 18:30 — Corrigí errores en el capítulo 'Hola, Mundo!'
ACTUALIZACIÓN: 16.04.2020 10:00 — Ajusté y añadí texto en la descripción de activación del gestor de extensiones JupyterLab
ACTUALIZACIÓN: 16.04.2020 10:40 — Corregí errores encontrados en el texto y mejoré un poco el capítulo 'Instalación del software básico y configuración del sistema'

Fuente: habr.com

Compra un hosting fiable para sitios web con protección contra DDoS, servidores VPS VDS 🔥 Compra un hosting fiable para sitios web con protección contra DDoS, servidores VPS VDS | ProHoster