Você já teve que experimentar com código ou utilitários de sistema no Linux, de modo que não precisasse se preocupar em danificar o sistema básico ou em perder tudo em caso de erro do código que deve ser executado com privilégios de root?
E quanto a necessidade de testar ou executar um cluster inteiro de diversos microsserviços em uma única máquina? Cem ou até mil?
Com máquinas virtuais gerenciadas por um hipervisor, pode até ser possível resolver essas tarefas, mas a que custo? Por exemplo, um contêiner no LXD baseado na distribuição Alpine Linux consome um mínimo de 7.60MB de RAM, e onde a partição raiz após a inicialização ocupa 9.5MB! O que você acha disso, Elon Musk? Recomendo que você conheça as capacidades básicas do LXD — sistema de contêineres no Linux.
Depois que ficou claro o que são os contêineres LXD, vamos mais adiante e pensar: e se houvesse uma plataforma combinada onde fosse possível executar código de forma segura para o host, gerar gráficos, conectar dinamicamente (interativamente) widgets de UI com seu código, adicionar texto ao código com blackjack... e formatação? Algo como um blog interativo? Uau... Eu quero! Eu quero! 🙂
Dê uma olhada abaixo, onde vamos executar no contêiner JupyterLab — a próxima geração de interface de usuário em vez do obsoleto Jupyter Notebook, além de instalar módulos Python como NumPy, Pandas, Matplotlib, IPyWidgets que permitirão fazer tudo isso mencionado acima e salvar tudo em um arquivo especial — um notebook IPython.

Navegação
- Plano de lançamento em órbita
- Instalação e configuração do sistema base
- Instalação do software básico e configuração do sistema
- Instalação e configuração do JupyterLab
- Compartilhando dados com o host
- Olá, Mundo!
- Expandindo as capacidades do Python
- Testando módulos no JupyterLab
- E mais?
Plano de lançamento em órbita ^

Vamos fazer um breve plano de ação para facilitar a implementação do esquema acima:
- Vamos instalar e executar um contêiner baseado na distribuição Alpine Linux. Usaremos essa distribuição porque é voltada para a minimalidade e instalaremos apenas o software mais necessário, sem excessos.
- Adicionaremos um disco virtual extra ao contêiner, ao qual daremos o nome —
hostfse montaremos na FS raiz. Este disco permitirá usar arquivos no host a partir do diretório especificado dentro do contêiner. Assim, os dados permanecerão independentes do contêiner. No caso da remoção do contêiner, os dados permanecerão no host. Além disso, esse esquema é útil para compartilhar dados entre vários contêineres sem usar os mecanismos de rede padrão do sistema de contêineres. - Vamos instalar Bash, sudo, as bibliotecas necessárias, adicionar e configurar um usuário do sistema
- Vamos instalar Python, os módulos e compilar suas dependências binárias
- Vamos instalar e iniciar JupyterLab, configurar a aparência, instalar extensões para ele.
Neste artigo, começaremos com a execução do contêiner, não abordaremos a instalação e configuração do LXD, tudo isso pode ser encontrado em outro artigo — Funcionalidades básicas do LXD - sistema de containers no Linux.
Instalação e configuração do sistema base ^
Criamos o contêiner com o comando que especifica a imagem — alpine3, o identificador para o contêiner — jupyterlab e, se necessário, os perfis de configuração:
lxc init alpine3 jupyterlab --profile=default --profile=hddrootAqui uso o perfil de configuração hddroot que indica criar um contêiner com a partição root em Storage Pool localizada no 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:
- noneIsso me permite experimentar com contêineres no disco HDD, economizando recursos do disco SSD que também está no meu sistema 🙂 para o qual eu criei um perfil de configuração separado. ssdroot.
Após a criação do contêiner, ele está em estado STOPPED, então precisamos iniciá-lo executando o sistema init nele:
lxc start jupyterlabVamos exibir a lista de contêineres no LXD usando a chave -c que indica quais ccolunas exibir na tela:
lxc list -c ns4b
+------------+---------+-------------------+--------------+
| NAME | STATE | IPV4 | STORAGE POOL |
+------------+---------+-------------------+--------------+
| jupyterlab | RUNNING | 10.0.5.198 (eth0) | hddpool |
+------------+---------+-------------------+--------------+Ao criar o contêiner, o endereço IP foi escolhido aleatoriamente, pois usamos o perfil de configuração padrão que foi previamente configurado no artigo Funcionalidades básicas do LXD - sistema de containers no Linux.
Vamos mudar esse endereço IP para um mais memorável, criando uma interface de rede em nível de contêiner, e não em nível de perfil de configuração como está na configuração atual. Não é obrigatório fazer isso, você pode pular.
Criamos uma interface de rede eth0 que vinculamos ao switch (ponte de rede) lxdbr0 no qual ativamos o NAT conforme o artigo anterior, e agora o contêiner terá acesso à Internet, além de 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.5Após adicionar o dispositivo, é necessário reiniciar o contêiner:
lxc restart jupyterlabVerificamos o status do contêiner:
lxc list -c ns4b
+------------+---------+------------------+--------------+
| NAME | STATE | IPV4 | STORAGE POOL |
+------------+---------+------------------+--------------+
| jupyterlab | RUNNING | 10.0.5.5 (eth0) | hddpool |
+------------+---------+------------------+--------------+Instalação do software básico e configuração do sistema ^
Para administrar nosso contêiner, precisamos instalar o seguinte software:
Pacote
Descrição
bash
O shell GNU Bourne Again
bash-completion
Conclusão programável para o shell bash
sudo
Dar a certos usuários a capacidade de executar alguns comandos como root
shadow
Conjunto de ferramentas para gerenciamento de senhas e contas com suporte para arquivos shadow e PAM
tzdata
Fontes para dados sobre fusos horários e horário de verão
nano
Clone do editor Pico com melhorias
Além disso, você pode instalar o suporte no sistema de man-pages instalando os seguintes pacotes — man man-pages mdocml-apropos less
lxc exec jupyterlab -- apk add bash bash-completion sudo shadow tzdata nanoVamos analisar os comandos e as chaves que usamos:
lxc— Chamada do cliente LXDexec— Método do cliente LXD que executa o comando no contêinerjupyterlab— Identificador do contêiner--— Chave especial que indica não interpretar as chaves subsequentes como chaves paralxce passar toda a string restante como está para o contêinerapk— Gerenciador de pacotes da distribuição Alpine Linuxadd— Método do gerenciador de pacotes que instala os pacotes indicados após o comando
Em seguida, vamos definir o fuso horário no sistema Europe/Moscow:
lxc exec jupyterlab -- cp /usr/share/zoneinfo/Europe/Moscow /etc/localtimeApós a instalação do fuso horário, o pacote tzdata não é mais necessário no sistema, ele ocupará espaço, então, vamos removê-lo:
lxc exec jupyterlab -- apk del tzdataVerificamos o fuso horário:
lxc exec jupyterlab -- date
Wed Apr 15 10:49:56 MSK 2020Para não perder muito tempo configurando o Bash para novos usuários no contêiner, nas próximas ações, copiaremos os skel-arquivos prontos do sistema host. Isso permitirá embelusar o Bash no contêiner em modo interativo. Meu sistema host é o Manjaro Linux e os arquivos copiados /etc/skel/.bash_profile, /etc/skel/.bashrc, /etc/skel/.dir_colors basicamente se adequam ao Alpine Linux e não causam problemas críticos, mas você pode ter outra distribuição e precisará investigar por conta própria em caso de erro ao iniciar o Bash no contêiner.
Copiamos os skel-arquivos para o contêiner. A chave --create-dirs criará os diretórios necessários, se 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_colorsPara um usuário root já existente, copiaremos os arquivos skel copiados para o diretório home:
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_colorsNo Alpine Linux, os usuários recebem um shell padrão do sistema /bin/sh, vamos substituí-lo por root Bash:
lxc exec jupyterlab -- usermod --shell=/bin/bash rootPara root se o usuário não tiver uma senha, será necessário definir uma. O seguinte comando gerará e definirá uma nova senha aleatória que será exibida na tela do console após a 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 "Nova Senha: $PASSWD""
Nova Senha: sFiXEvBswuWAAlém disso, vamos criar um novo usuário do sistema — jupyter para o qual configuraremos mais tarde JupyterLab:
lxc exec jupyterlab -- useradd --create-home --shell=/bin/bash jupyterGeraremos e estabeleceremos uma senha:
lxc exec jupyterlab -- /bin/bash -c "PASSWD=$(head /dev/urandom | tr -dc A-Za-z0-9 | head -c 12); echo "jupyter:$PASSWD" | chpasswd && echo "Nova Senha: $PASSWD""
Nova Senha: ZIcbzWrF8tkiEm seguida, executaremos dois comandos; o primeiro criará um grupo do 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 jupyterVamos verificar em quais grupos o usuário está jupyter:
lxc exec jupyterlab -- id -Gn jupyter
jupyter sudoTudo pronto, vamos em frente.
Iremos permitir que todos os usuários que pertencem ao grupo sudo usem o comando sudo. Para isso, execute o seguinte script que sed descomenta 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"Instalação e configuração do JupyterLab ^
JupyterLab — é um aplicativo Python, portanto, precisamos instalar esse interpretador primeiro. Além disso, JupyterLab usaremos o gerenciador de pacotes do Python pip, e não o do sistema, porque o repositório do sistema pode estar desatualizado, e, portanto, devemos manualmente resolver as dependências instalando os seguintes pacotes — python3 python3-dev gcc libc-dev zeromq-dev:
lxc exec jupyterlab -- apk add python3 python3-dev gcc libc-dev zeromq-devVamos atualizar os módulos Python e o gerenciador de pacotes pip para a versão mais recente:
lxc exec jupyterlab -- python3 -m pip install --upgrade pip setuptools wheelInstalando JupyterLab pelo gerenciador de pacotes pip:
lxc exec jupyterlab -- python3 -m pip install jupyterlabComo as extensões em JupyterLab são experimentais e oficialmente não vêm com o pacote jupyterlab, portanto, precisamos instalá-las e configurá-las manualmente.
Vamos instalar o NodeJS e o gerenciador de pacotes para ele — NPM, pois JupyterLab o utiliza para suas extensões:
lxc exec jupyterlab -- apk add nodejs npmPara que as extensões do JupyterLab que vamos instalar funcionem, elas precisam ser instaladas no diretório do usuário, pois o aplicativo será executado a partir do usuário jupyter. O problema é que não há parâmetro no comando de inicialização onde podemos passar o diretório, o aplicativo reconhece apenas variáveis de ambiente e, portanto, devemos defini-la. Para isso, escreveremos o comando para exportar a variável JUPYTERLAB_DIR no ambiente do usuário jupyter, no arquivo .bashrc, que é executado toda vez que o usuário entra no sistema:
lxc exec jupyterlab -- su -l jupyter -c "echo -e "nexport JUPYTERLAB_DIR=$HOME/.local/share/jupyter/lab" >> .bashrc"Na próxima linha, instalaremos uma extensão especial — o gerenciador de extensões em 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"Agora tudo já está pronto para o primeiro lançamento JupyterLab, mas também podemos instalar algumas extensões úteis:
toc— Table of Contents, gera uma lista de cabeçalhos no artigo/notebookjupyterlab-horizon-theme— Tema de interfacejupyterlab_neon_theme— Tema de interfacejupyterlab-ubu-theme— Outro tema do autor deste artigo 🙂 Mas neste caso, a instalação será mostrada a partir do repositório do GitHub.
Então, execute as 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, precisamos compilá-las, pois anteriormente, ao instalá-las, especificamos a chave --no-build para economizar tempo. Agora vamos acelerar significativamente compilando todas de uma vez:
lxc exec jupyterlab -- su -l jupyter -c "export JUPYTERLAB_DIR=$HOME/.local/share/jupyter/lab; jupyter lab build"Agora execute os seguintes dois comandos para o primeiro lançamento. JupyterLabPoderíamos iniciá-lo com um único comando, mas, nesse caso, o comando de inicialização, que se mantém em mente, será salvo pelo bash no contêiner, e não no host, onde já há muitos comandos registrados no histórico 🙂
Fazemos login no contêiner como usuário jupyter:
lxc exec jupyterlab -- su -l jupyterEm seguida, execute JupyterLab com as chaves e parâmetros conforme indicado:
[jupyter@jupyterlab ~]$ jupyter lab --ip=0.0.0.0 --no-browserAcesse no navegador da web o endereço http://10.0.5.5:8888 e na página aberta insira token o acesso que você verá no console. Copie e cole na página, em seguida, clique Iniciar sessão. Depois de entrar, vá para o menu de extensões à esquerda, como mostrado na imagem abaixo, onde você será solicitado, ao ativar o gerenciador de extensões, a aceitar os riscos de segurança ao instalar extensões de terceiros, pelas quais a equipe JupyterLab development não se responsabiliza:

No entanto, é exatamente para isso que isolamos completamente JupyterLab e o colocamos em um contêiner, para que extensões externas que requerem e utilizam NodeJS não possam, no mínimo, roubar dados do disco, exceto aqueles que abrirmos dentro do contêiner. Acesse seus documentos privados no host em /home processos do contêiner será difícil, e mesmo que consiga, será necessário ter privilégios sobre os arquivos no sistema host, uma vez que executamos o contêiner em modo não privilegiado. Com base nessa informação, você pode avaliar o risco de habilitar extensões em JupyterLab.
Os notebooks IPython criados (páginas em JupyterLab) agora serão criados no diretório home do usuário — /home/jupyter, mas em nossos planos está dividir os dados (compartilhar) entre o host e o contêiner, portanto, volte ao console e pare JupyterLab executando a tecla de atalho — CTRL+C e respondendo y ao prompt. Em seguida, desconecte a sessão interativa do usuário jupyter executando a tecla de atalho CTRL+D.
Compartilhando dados com o host ^
Para compartilhar dados com o host, é necessário criar um dispositivo no contêiner que permita isso e, para isso, execute o seguinte comando onde especificamos as seguintes chaves:
lxc config device add— O comando adiciona a configuração do dispositivojupyter— O identificador do contêiner ao qual a configuração é adicionadahostfs— O identificador do dispositivo. Você pode escolher qualquer nome.disk— Especifica o tipo de dispositivopath— Especifica o caminho no contêiner onde o LXD montará este dispositivosource— Especifica a origem, o caminho para o diretório no host que você deseja compartilhar com o contêiner. Forneça o caminho de acordo com suas preferências.
lxc config device add jupyterlab hostfs disk path=\/mnt\/hostfs source=\/home\/dv\/projects\/ipython-notebooksPara o diretório /home/dv/projects/ipython-notebooks deve ser definido o permissão do usuário do contêiner que atualmente possui UID igual a SubUID + UID, consulte o capítulo Segurança. Privilégios dos contêineres no artigo Funcionalidades básicas do LXD - sistema de containers no Linux.
Definimos a permissão no host, onde o proprietário será o usuário do contêiner jupyter, e a variável $USER indica seu usuário do host como grupo:
sudo chown 1001000:$USER \/home\/dv\/projects\/ipython-notebooksOlá, Mundo! ^
Se você ainda tiver uma sessão de console aberta no contêiner com JupyterLab, reinicie-a com a nova chave --notebook-dir definindo o valor /mnt/hostfs como o caminho até a raiz dos notebooks no contêiner para o dispositivo que criamos na etapa anterior:
jupyter lab --ip=0.0.0.0 --no-browser --notebook-dir=\/mnt\/hostfsEm seguida, acesse a página http://10.0.5.5:8888 e crie seu primeiro notebook clicando no botão na página, conforme mostrado na imagem abaixo:

Em seguida, no campo da página, digite o código em Python que exibirá o clássico Hello World!. Após a inserção, pressione CTRL+ENTER ou o botão 'play' na barra de ferramentas acima para que o JupyterLab execute isso:

Quase tudo está pronto para uso, mas seria entediante se não instalássemos módulos Python adicionais (aplicativos completos) que permitem expandir significativamente as funcionalidades padrão do Python em JupyterLab, portanto, vamos seguir em frente 🙂
P.S. Curiosamente, a antiga implementação Jupyter sob o codinome Jupyter Notebook não desapareceu e existe paralelamente a JupyterLab. Para acessar a versão antiga, vá para o link adicionando ao endereço o sufixo/tree, enquanto a transição para a nova versão é feita com o sufixo /lab, mas não é obrigatório indicá-lo:
- Jupyter Notebook — http://10.0.5.5:8888/tree
- Jupyter Lab — http://10.0.5.5:8888/lab
Expandindo as capacidades do Python ^
Nesta seção, instalaremos módulos poderosos da linguagem Python cujos resultados podem ser integrados nos notebooks. NumPy, Pandas, Matplotlib, IPyWidgets Antes de instalar os módulos Python mencionados através do gerenciador de pacotes, JupyterLab.
devemos primeiro resolver as dependências do sistema no Alpine Linux: pip g++
— Necessário para compilar módulos, pois alguns deles são implementados na linguageme conectam-se ao Python em tempo de execução como módulos binários. C++ freetype-dev— dependência para o módulo Python.Instalando as dependências: Matplotlib
Instalando dependências:
lxc exec jupyterlab -- apk add g++ freetype-devHá um problema: na atual versão do Alpine Linux, não é possível compilar uma nova versão do NumPy, ocorrerá um erro de compilação que não consegui resolver:
ERRO: Não foi possível construir as rodas para o numpy que usam PEP 517 e não podem ser instaladas diretamente
Portanto, este módulo será instalado como um pacote de sistema que distribui uma versão já compilada, mas um pouco mais antiga do que a disponível no site:
lxc exec jupyterlab -- apk add py3-numpy py3-numpy-devEm seguida, instalamos os módulos Python através do gerenciador de pacotes pip. Tenha paciência, pois alguns módulos serão compilados e isso levará alguns minutos. Na minha máquina, a compilação levou cerca de 15 minutos:
lxc exec jupyterlab -- python3 -m pip install pandas ipywidgets matplotlibLimpe os caches de instalações:
lxc exec jupyterlab -- rm -rf /home/*/.cache/pip/*
lxc exec jupyterlab -- rm -rf /root/.cache/pip/*Testando módulos no JupyterLab ^
Se você tiver um JupyterLab, reinicie-o para que os novos módulos instalados sejam ativados. Para isso, na sessão do console, pressione CTRL+C onde ele está sendo executado e digite y quando solicitado a parar, e depois inicie novamente JupyterLab pressionando a setinha para cima no teclado, para não ter que digitar o comando novamente e então Enter para iniciar:
jupyter lab --ip=0.0.0.0 --no-browser --notebook-dir=\/mnt\/hostfsVá para a página http://10.0.5.5:8888/lab ou atualize a página no seu navegador, e então digite 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_plotVocê deve obter um resultado como na imagem abaixo, onde IPyWidgets gera um elemento de interface do usuário na página que interage interativamente com o código-fonte, além de Matplotlib exibir o resultado do código como uma imagem, como o gráfico de uma função:

Muitos exemplos IPyWidgets podem ser encontrados nos tutoriais aqui
E mais? ^
Parabéns se você ficou e chegou ao fim deste artigo. Eu optei por não disponibilizar um script pronto no final do artigo que instalasse JupyterLab em um "clique", para incentivar os trabalhadores 🙂 Mas você pode fazer isso sozinho, pois já sabe como, reunindo os comandos em um único script Bash 🙂
Além disso, você pode:
- Definir um nome de rede para o contêiner em vez do endereço IP, escrevendo-o em um simples
/etc/hostse digitar o endereço no navegador http://jupyter.local:8888 - Brincar com o limite de recursos para o contêiner. Para isso, leia o capítulo sobre as capacidades básicas do LXD ou obtenha mais informações no site do desenvolvedor do LXD.
- Mudar o tema:

E muito mais você pode! Isso é tudo. Desejo sucesso a você!
ATUALIZAÇÃO: 15.04.2020 18:30 — Corrigi erros no capítulo 'Olá, Mundo!'
ATUALIZAÇÃO: 16.04.2020 10:00 — Corrigi e adicionei texto na descrição da ativação do gerenciador de extensões JupyterLab
ATUALIZAÇÃO: 16.04.2020 10:40 — Corrigi erros encontrados no texto e melhorei um pouco o capítulo 'Instalação do software básico e configuração do sistema'
Fonte: habr.com
