Lançando Jupyter na órbita LXD

Você já teve que experimentar códigos ou utilitários de sistema no Linux para não se preocupar com o sistema básico e não destruir tudo em caso de erro no código que deveria rodar com privilégios de root?

Mas e quanto ao fato de que, digamos, você precisa testar ou executar um cluster inteiro de vários microsserviços em uma máquina? Cem ou até mil?

Com máquinas virtuais gerenciadas por um hipervisor, tais problemas podem e serão resolvidos, mas a que custo? Por exemplo, um contêiner em LXD baseado na distribuição Alpine Linux consome apenas 7.60MB RAM e onde a partição raiz ocupa após a inicialização 9.5MB! O que você acha disso, Elon Musk? Eu recomendo conferir recursos básicos do LXD - um sistema de contêineres no Linux

Depois que ficou claro em geral o que são contêineres LXD, vamos mais longe e pensar, e se existisse uma plataforma de colheita onde você pudesse executar código com segurança para o host, gerar gráficos, vincular dinamicamente (interativamente) widgets de UI ao seu código, complemente o código com texto com blackjack... formatação? Algum tipo de blog interativo? Nossa... eu quero! Querer! 🙂

Olhe embaixo do gato onde vamos lançar em um container JupyterLabGenericName - a próxima geração de interface de usuário em vez do desatualizado Jupyter Notebook, e também instalaremos módulos Python, como NumPy, Pandas, matplotlib, IPyWidgets que permitirá que você faça tudo listado acima e salve tudo em um arquivo especial - um laptop IPython.

Lançando Jupyter na órbita LXD

Plano de decolagem orbital ^

Lançando Jupyter na órbita LXD

Vamos delinear um breve plano de ação para facilitar a implementação do esquema acima:

  • Vamos instalar e lançar um contêiner baseado no kit de distribuição Linux alpino. Usaremos esta distribuição porque ela visa o minimalismo e instalaremos nela apenas os softwares mais necessários, nada supérfluo.
  • Vamos adicionar um disco virtual adicional ao contêiner e dar um nome a ele - hostfs e monte-o no sistema de arquivos raiz. Este disco possibilitará a utilização de arquivos no host de um determinado diretório dentro do container. Assim, nossos dados serão independentes do container. Se o contêiner for excluído, os dados permanecerão no host. Além disso, este esquema é útil para compartilhar os mesmos dados entre muitos contêineres sem usar os mecanismos de rede padrão da distribuição de contêineres.
  • Vamos instalar Bash, sudo, as bibliotecas necessárias, adicionar e configurar um usuário do sistema
  • Vamos instalar Python, módulos e compilar dependências binárias para eles
  • Vamos instalar e iniciar JupyterLabGenericName, personalize a aparência e instale extensões para ela.

Neste artigo começaremos lançando o contêiner, não consideraremos a instalação e configuração do LXD, você pode encontrar tudo isso em outro artigo - Recursos básicos do LXD - sistemas de contêineres Linux.

Instalação e configuração do sistema básico ^

Criamos um container com o comando no qual especificamos a imagem - alpine3, identificador do contêiner - jupyterlab e, se necessário, perfis de configuração:

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

Aqui estou usando um perfil de configuração hddroot que especifica a criação de um contêiner com uma partição raiz em Pool de armazenamento localizado em um disco HDD 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

Isso me dá a oportunidade de experimentar contêineres no disco HDD, economizando os recursos do disco SSD, que também está disponível no meu sistema 🙂 para o qual criei um perfil de configuração separado ssdroot.

Depois que o contêiner for criado, ele estará no estado STOPPED, então precisamos iniciá-lo executando o sistema init nele:

lxc start jupyterlab

Vamos exibir uma lista de contêineres no LXD usando a chave -c que indica qual cexibição de colunas:

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

Na criação do container, o endereço IP foi escolhido aleatoriamente, pois utilizamos um perfil de configuração default que foi configurado anteriormente no artigo Recursos básicos do LXD - sistemas de contêineres Linux.

Alteraremos esse endereço IP para um mais fácil de lembrar, criando uma interface de rede no nível do contêiner, e não no nível do perfil de configuração, como está agora na configuração atual. Você não precisa fazer isso, você pode pular.

Criando uma interface de rede eth0 que vinculamos ao switch (ponte de rede) lxdbr0 em que habilitamos o NAT conforme artigo anterior e o container agora terá acesso à Internet, e também atribuímos um endereço IP estático à interface - 10.0.5.5:

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

Após adicionar um dispositivo, o contêiner deve ser reinicializado:

lxc restart jupyterlab

Verificando o status do contêiner:

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

Instalando software básico e configurando o sistema ^

Para administrar nosso contêiner, você precisa instalar o seguinte software:

Pacote
Descrição

bater
O shell GNU Bourne Again

conclusão bash
Conclusão programável para o shell bash

sudo
Dê a certos usuários a capacidade de executar alguns comandos como root

sombra
Conjunto de ferramentas de gerenciamento de senhas e contas com suporte para arquivos shadow e PAM

tzdata
Fontes de dados de fuso horário e horário de verão

nano
Clone do editor Pico com melhorias

Além disso, você pode instalar o suporte nas páginas de manual do sistema instalando os seguintes pacotes - man man-pages mdocml-apropos less

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

Vejamos os comandos e teclas que usamos:

  • lxc - Ligue para o cliente LXD
  • exec - Método cliente LXD que executa um comando no contêiner
  • jupyterlab — ID do contêiner
  • -- — Uma chave especial que especifica não interpretar outras chaves como chaves para lxc e passe o resto da string como está para o contêiner
  • apk — Gerenciador de pacotes de distribuição Alpine Linux
  • add — Um método de gerenciamento de pacotes que instala pacotes especificados após o comando

A seguir, definiremos um fuso horário no sistema Europe/Moscow:

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

Depois de instalar o fuso horário, o pacote tzdata não for mais necessário no sistema, ele ocupará espaço, então vamos excluí-lo:

lxc exec jupyterlab -- apk del tzdata

Verificando o fuso horário:

lxc exec jupyterlab -- date

Wed Apr 15 10:49:56 MSK 2020

Para não perder muito tempo configurando o Bash para novos usuários no contêiner, nas etapas a seguir copiaremos arquivos skel prontos do sistema host para ele. Isso permitirá que você embeleze o Bash em um contêiner de forma interativa. Meu sistema host é Manjaro Linux e os arquivos que estão sendo copiados /etc/skel/.bash_profile, /etc/skel/.bashrc, /etc/skel/.dir_colors em princípio, eles são adequados para Alpine Linux e não causam problemas críticos, mas você pode ter uma distribuição diferente e precisar descobrir de forma independente se há um erro ao executar o Bash no contêiner.

Copie os arquivos skel para o contêiner. Chave --create-dirs criará os diretórios necessários se eles não existirem:

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 um usuário root existente, copie os arquivos skel recém-copiados no contêiner para o diretório inicial:

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

Alpine Linux instala um shell de sistema para usuários /bin/sh, iremos substituí-lo por root usuário no Bash:

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

Que root o usuário não estava sem senha, ele precisa definir uma senha. O seguinte comando irá gerar e definir uma nova senha aleatória para ele, que você verá na tela do console após sua execução:

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

Além disso, vamos criar um novo usuário do sistema - jupyter para o qual configuraremos mais tarde JupyterLabGenericName:

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

Vamos gerar e definir uma senha para ele:

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 seguir, executaremos dois comandos, o primeiro criará um grupo de sistema sudo, e o segundo adicionará um usuário a ele jupyter:

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

Vamos ver a quais grupos o usuário pertence jupyter:

lxc exec jupyterlab -- id -Gn jupyter

jupyter sudo

Está tudo bem, vamos em frente.

Permitir todos os usuários que são membros do grupo sudo usar comando sudo. Para fazer isso, execute o seguinte script, onde sed descomente a linha do parâmetro no arquivo de configuração /etc/sudoers:

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

Instalando e configurando o JupyterLab ^

JupyterLabGenericName é um aplicativo Python, portanto devemos primeiro instalar este interpretador. Também, JupyterLabGenericName vamos instalar usando o gerenciador de pacotes Python pip, e não o do sistema, pois pode estar desatualizado no repositório do sistema e portanto, temos que resolver manualmente as dependências dele instalando os seguintes pacotes - python3 python3-dev gcc libc-dev zeromq-dev:

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

Vamos atualizar os módulos python e o gerenciador de pacotes pip para a versão atual:

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

Conjunto JupyterLabGenericName através do gerenciador de pacotes pip:

lxc exec jupyterlab -- python3 -m pip install jupyterlab

Desde as extensões em JupyterLabGenericName são experimentais e não são enviados oficialmente com o pacote jupyterlab, então temos que instalá-lo e configurá-lo manualmente.

Vamos instalar o NodeJS e o gerenciador de pacotes para ele - NPM, já que JupyterLabGenericName os usa para suas extensões:

lxc exec jupyterlab -- apk add nodejs npm

Para extensões para JupyterLabGenericName que iremos instalar funcionou, eles precisam ser instalados no diretório do usuário, pois o aplicativo será iniciado a partir do usuário jupyter. O problema é que não existe nenhum parâmetro no comando de lançamento que possa ser passado para um diretório; a aplicação aceita apenas uma variável de ambiente e portanto devemos defini-la. Para fazer isso, escreveremos o comando de exportação de variável JUPYTERLAB_DIR no ambiente do usuário jupyter, arquivar .bashrcque é executado toda vez que o usuário faz login:

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

O próximo comando instalará uma extensão especial - gerenciador de extensão em JupyterLabGenericName:

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

Agora está tudo pronto para o primeiro lançamento JupyterLabGenericName, mas ainda podemos instalar algumas extensões úteis:

  • toc — Índice, gera uma lista de títulos em um artigo/caderno
  • jupyterlab-horizon-theme - tema da interface do usuário
  • jupyterlab_neon_theme - tema da interface do usuário
  • jupyterlab-ubu-theme - Outro tema do autor este artigo :) Mas neste caso, a instalação do repositório GitHub será mostrada

Portanto, execute os seguintes comandos sequencialmente para instalar essas extensões:

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"

Após instalar as extensões, devemos compilá-las, pois previamente, durante a instalação, especificamos a chave --no-build para economizar tempo. Agora vamos acelerar significativamente compilando-os juntos de uma só vez:

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

Agora execute os dois comandos a seguir para executá-lo pela primeira vez JupyterLabGenericName. Seria possível iniciá-lo com um comando, mas neste caso, o comando de inicialização, que é difícil de lembrar em sua mente, será lembrado pelo bash no container, e não no host, onde já existem comandos suficientes para registrá-los na história :)

Faça login no contêiner como usuário jupyter:

lxc exec jupyterlab -- su -l jupyter

Em seguida, execute JupyterLabGenericName com chaves e parâmetros conforme indicado:

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

Acesse o endereço no seu navegador http://10.0.5.5:8888 e na página que abre digite token acesso que você verá no console. Copie e cole na página e clique em Entrar. Após o login, acesse o menu de extensões à esquerda, conforme mostrado na figura abaixo, onde você será solicitado, ao ativar o gerenciador de extensões, a correr riscos de segurança instalando extensões de terceiros para as quais o comando Desenvolvimento do JupyterLab não é responsável:

Lançando Jupyter na órbita LXD

No entanto, isolamos todo o JupyterLabGenericName e coloque-o em um contêiner para que extensões de terceiros que requerem e usam NodeJS não possam pelo menos roubar dados do disco, exceto aqueles que abrimos dentro do contêiner. Acesse seus documentos privados no host em /home é improvável que os processos do contêiner sejam bem-sucedidos e, se isso acontecer, você precisará ter privilégios nos arquivos do sistema host, já que executamos o contêiner em modo sem privilégios. Com base nessas informações, você pode avaliar o risco de incluir extensões em JupyterLabGenericName.

Notebooks IPython criados (páginas em JupyterLabGenericName) agora será criado no diretório inicial do usuário - /home/jupyter, mas nossos planos são dividir os dados (compartilhar) entre o host e o contêiner, então retorne ao console e pare JupyterLabGenericName executando a tecla de atalho - CTRL+C e respondendo y a pedido. Em seguida, encerre a sessão interativa do usuário jupyter completando uma tecla de atalho CTRL+D.

Compartilhando dados com o host ^

Para compartilhar dados com o host, você precisa criar um dispositivo no contêiner que permita fazer isso e para fazer isso, execute o seguinte comando onde especificamos as seguintes chaves:

  • lxc config device add — O comando adiciona a configuração do dispositivo
  • jupyter — ID do contêiner ao qual a configuração é adicionada
  • hostfs - ID de dispositivo. Você pode definir qualquer nome.
  • disk — O tipo de dispositivo é indicado
  • path — Especifica o caminho no contêiner no qual o LXD montará este dispositivo
  • source — Especifique a origem, o caminho para o diretório no host que você deseja compartilhar com o contêiner. Especifique o caminho de acordo com suas preferências
lxc config device add jupyterlab hostfs disk path=/mnt/hostfs source=/home/dv/projects/ipython-notebooks

Para o catálogo /home/dv/projects/ipython-notebooks a permissão deve ser definida para o usuário do contêiner que atualmente possui um UID igual a SubUID + UID, consulte o capítulo Segurança. Privilégios de contêiner Artigo Recursos básicos do LXD - sistemas de contêineres Linux.

Defina a permissão no host, onde o proprietário será o usuário do contêiner jupyter, e a variável $USER especificará seu usuário host como um grupo:

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

Olá Mundo! ^

Se você ainda tiver uma sessão de console aberta no contêiner com JupyterLabGenericNamee reinicie-o com uma nova chave --notebook-dir definindo o valor /mnt/hostfs como o caminho para a raiz dos laptops no contêiner do dispositivo que criamos na etapa anterior:

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

Então vá para a página http://10.0.5.5:8888 e crie seu primeiro laptop clicando no botão da página conforme mostrado na imagem abaixo:

Lançando Jupyter na órbita LXD

Em seguida, no campo da página, insira o código Python que exibirá o clássico Hello World!. Quando terminar de entrar, pressione CTRL+ENTER ou o botão "play" na barra de ferramentas na parte superior para que o JupyterLab faça isso:

Lançando Jupyter na órbita LXD

Neste ponto, quase tudo está pronto para uso, mas não será interessante se não instalarmos módulos Python adicionais (aplicativos completos) que possam expandir significativamente os recursos padrão do Python em JupyterLabGenericName, portanto, vamos em frente :)

PS O interessante é que a implementação antiga jupyter sob o codinome Caderno Jupyter não desapareceu e existe em paralelo com JupyterLabGenericName. Para mudar para a versão antiga, siga o link adicionando o sufixo no endereço/tree, e a transição para a nova versão é feita com o sufixo /lab, mas não precisa ser especificado:

Expandindo os recursos do Python ^

Nesta seção, instalaremos módulos poderosos da linguagem Python como NumPy, Pandas, matplotlib, IPyWidgets cujos resultados são integrados em laptops JupyterLabGenericName.

Antes de instalar os módulos Python listados por meio do gerenciador de pacotes pip devemos primeiro resolver as dependências do sistema no Alpine Linux:

  • g++ — Necessário para compilar módulos, já que alguns deles são implementados na linguagem C + + e conecte-se ao Python em tempo de execução como módulos binários
  • freetype-dev - dependência para módulo Python matplotlib

Instalando dependências:

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

Há um problema: no estado atual da distribuição Alpine Linux, não será possível compilar a nova versão do NumPy; aparecerá um erro de compilação que não consegui resolver:

ERRO: Não foi possível construir rodas para numpy que usam PEP 517 e não podem ser instaladas diretamente

Portanto, instalaremos este módulo como um pacote de sistema que distribui uma versão já compilada, mas um pouco mais antiga do que está disponível atualmente no site:

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

Em seguida, instale os módulos Python por meio do gerenciador de pacotes pip. Por favor, seja paciente, pois alguns módulos serão compilados e podem levar alguns minutos. Na minha máquina, a compilação demorou cerca de 15 minutos:

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

Limpando caches de instalação:

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

Testando módulos no JupyterLab ^

Se você estiver correndo JupyterLabGenericName, reinicie-o para que os módulos recém-instalados sejam ativados. Para fazer isso, em uma sessão de console, clique em CTRL+C onde você o está rodando e entre y para interromper a solicitação e começar novamente JupyterLabGenericName pressionando a seta para cima no teclado para não digitar o comando novamente e depois Enter para iniciá-lo:

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

Vá para a página http://10.0.5.5:8888/lab ou atualize a página em seu navegador e insira o seguinte código em uma nova célula do notebook:

%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

Você deve obter um resultado como na imagem abaixo, onde IPyWidgets gera um elemento de UI na página que interage interativamente com o código-fonte e também matplotlib exibe o resultado do código na forma de uma imagem como um gráfico de função:

Lançando Jupyter na órbita LXD

Muitos exemplos IPyWidgets você pode encontrá-lo em tutoriais aqui

O que mais? ^

Muito bem se você ficou e chegou ao final do artigo. Deliberadamente não postei um script pronto no final do artigo que instalaria JupyterLabGenericName em “um clique” para incentivar os trabalhadores :) Mas você pode fazer isso sozinho, pois já sabe como, tendo reunido os comandos em um único script Bash :)

Você também pode:

  • Defina um nome de rede para o contêiner em vez de um endereço IP, escrevendo-o de forma simples /etc/hosts e digite o endereço no navegador http://jupyter.local:8888
  • Brinque com o limite de recursos do contêiner, para isso leia o capítulo em recursos básicos do LXD ou obtenha mais informações no site do desenvolvedor LXD.
  • Mude o tema:

Lançando Jupyter na órbita LXD

E muito mais você pode fazer! Isso é tudo. Eu te desejo sucesso!

ATUALIZAÇÃO: 15.04.2020/18/30 XNUMXhXNUMX - Corrigidos erros no capítulo “Olá, Mundo!”
ATUALIZAÇÃO: 16.04.2020/10/00 XNUMX:XNUMX — Corrigido e adicionado texto na descrição de ativação do extension manager JupyterLabGenericName
ATUALIZAÇÃO: 16.04.2020/10/40 XNUMXhXNUMX — Corrigidos erros encontrados no texto e ligeiramente alterado para melhor o capítulo “Instalando software básico e configurando o sistema”

Fonte: habr.com

Adicionar um comentário