JupyterHub: ambiente de desenvolvimento integrado Python e R (E o GovData?)

Essa semana eu instalei o JupyterHub em uma de nossas VMs. O amigo @augusto.herrmann levantou a bola e eu cabeceei :sweat_smile::soccer:. Após deixar o ambiente Python redondo, inclusive com acesso às nossas bases de dados - tanto aos DataMarts e DataLakes que os engenheiros de dados da CGINF/SEGES têm preparado, quanto ao serviço Quartzo do SERPRO (nesse a dor de cabeça foi maior, mas o desafio é a nosso energia :stuck_out_tongue_closed_eyes:) - eu resolvi ir além e instalei também o Kernel do R com acesso às mesmas bases (challenge, challenge, challenge :star_struck:).

Após essa atividade eu entrei em estado de contemplação, visualizando as infinitas possibilidades em um ambiente de desenvolvimento integrado com Python e R e com acesso a todas as nossas bases de dados.

Engenheiros de Dados mantendo os dados fluindo nos encanamentos e desaguando em nossos Lagos de Dados… Cientistas e Analistas pescando no lago :fishing_pole_and_fish: para alimentar os nossos Gestores :eagle: com as melhores informações :fish:. :scream: HolyShit! O céu é o limite! :yum:

Mas peraí, esse já não era o objetivo do GovData? :thinking:

O GovData é um ambiente integrado de desenvolvimento constituído de um DataLake Hadoop, rodando com com um Hive ou um Impala por cima para as querys e integrado com outras ferramentas como o R Studio, o Qlik Sense, o MicroStrategy e o Spotfire. UaU :scream:. O problema é apenas 1: não funciona. O R Studio sequer funciona.

Posso subir dados para o GovData e tirar proveito de todo o poder do Hadoop rodando em um cluster monstro? Não! Posso subir meus scripts Python ou R e conectar a todas as nossas bases de dados e tirar proveito de um ambiente Hadoop rodando em um cluster monstro? Não! Posso fazer uma consulta de teras-teras-e-teras de dados extrair um pedaço e baixar para o meu ambiente para trabalhar com a ferramenta que eu quiser? Não! […] imagine aqui uma atividade de engenharia, ciência ou análise de dados […] Não!

Será que tentou-se criar uma ferramenta para resolver um problema que não tínhamos? Fica a provocação.

Quanto ao JupyterHub, está rodando em uma máquina modesta para o desafio a ser enfrentado: 1TB de disco, 32GB de RAM e 16 CPUs. Infelizmente o Jupyter ainda não permite a edição colaborativa como em um Google Docs, essa feature está no roadmap dos desenvolvedores do JupyterLab. Uma solução que implementei foi disponibilizar uma pasta compartilhada no servidor para os usuários trocarem informações. Integração com o Git através do Jupytext está no radar.

Ansioso para ver a galera utilizando o novo ambiente, eu já comecei a brincar com o Dask para tirar proveito desses 16 CPUs rodando em paralelo :crazy_face:. Crazy!

5 Curtidas

Fantástico, @kafran! :exploding_head: :tada: :clap: :clap: :clap: :clap:

Quero muito usar! :star_struck:

Credenciais enviadas por DM. Será incrível ter o feedback e as ideias de um cara como você para a gente desenvolver esse negócio :smile:

Obrigado! Já estou experimentando o ambiente.

Inicialmente fiquei um pouco decepcionado pelo fato do Jupyter Hub usar a interface do Jupyter Notebook por padrão e não do Jupyter Lab. Pesquisando um pouco encontrei essa issue e descobri que é possível usar a interface do lab, bastando acrescentar /lab na URL. Na minha opinião, o Lab deveria ser a interface padrão, pois facilita muito as coisas.

Vi que não está disponível no ambiente padrão do Python o módulo pyodbc, necessário para se conectar ao data lake da SEGES, que é em MS SQL Server.

Segui o procedimento para criar um ambiente personalizado meu e instalei o módulo com pip install pyodbc. Reiniciei o servidor. Mesmo assim ele não funciona. Dá como se o módulo não estivesse instalado.

import pyodbc

---------------------------------------------------------------------------
ModuleNotFoundError                       Traceback (most recent call last)
<ipython-input-1-0a9cb9987950> in <module>
----> 1 import pyodbc

ModuleNotFoundError: No module named 'pyodbc'

Tentando pelo terminal também dá erro, mas são erros diferentes com kernels Python diferentes.

(env) jupyter@srv:~$ which ipython3
/opt/tljh/user/bin/ipython3
(env) jupyter@srv:~$ ipython3
Python 3.6.9 |Anaconda, Inc.| (default, Jul 30 2019, 19:07:31)
Type 'copyright', 'credits' or 'license' for more information
IPython 7.7.0 -- An enhanced Interactive Python. Type '?' for help.

In [1]: import pyodbc
---------------------------------------------------------------------------
ModuleNotFoundError                       Traceback (most recent call last)
<ipython-input-1-0a9cb9987950> in <module>
----> 1 import pyodbc

ModuleNotFoundError: No module named 'pyodbc'

Já com o outro kernel:

(env) jupyter-@srv:~$ which python
/home/jupyter-/.conda/envs/env/bin/python
(env) jupyter-@srv:~$ python
Python 3.7.3 | packaged by conda-forge | (default, Jul  1 2019, 21:52:21)
[GCC 7.3.0] :: Anaconda, Inc. on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> import pyodbc
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
ImportError: libodbc.so.2: cannot open shared object file: No such file or directory

Ambos os comandos para chamar o Python (python e ipython3) no path deveriam apontar para o mesmo lugar, não lugares diferentes. Além disso, ao selecionar o kernel customizado no Jupyter Notebook / Lab e rodar o comando de importação, o erro apresentado deveria ser o da falta do pacote no sistema operacional, e não a inexistência da biblioteca Python. O que indica que a configuração do ambiente não funcionou corretamente – usei o guia que você escreveu.

Sobre a falta do pacote do S.O., pelo que li nesta thread, parece que está faltando o pacote Unix ODBC no sistema operacional, que pode ser instalado assim:

sudo apt-get update && sudo apt-get install unixodbc-dev libmyodbc

Achei curioso o ODBC no R não precisar deste pacote (não testei, estou me baseando apenas pelo que você escreveu no caderno de R).

Sim, o JupyterHub é apenas o Hub. A distribuição TLJH traz 3 interfaces: o Notebook, o Lab e o Nteract. Basta mudar na URL o /tree para /lab ou /nteract para navegar por esses ambientes. Estou cogitando deixar o Lab como padrão pois pretendo instalar os plugins para melhor integração com o Git.

No entanto, pelo que observo dos ambientes de desenvolvimento dos colegas de trabalho, muitos parecem estar mais habituados ao Notebook. De repente, deixar a instrução do Lab como opção para que o usuário mais “avançado” e que esteja mais habituado a essa ambiente possa optar por ele.

ODBC e Linux não combinam muito bem. Nunca usei ODBC no Linux, não sei nem para onde vai, mas pelo pouco que já li, me parece ser um parto, haja vista demandar algumas configurações na máquina para definir os DSN (Data Source Names), vai demandar setar variáveis de ambiente, e mexer em alguns arquivos de configuração e em um ambiente multiusuário como o nosso, confesso que eu não sei muito para onde ir. Não sei se será possível fazer isso para todos os usuários, como ficam as credenciais de acesso ao banco? E por fim, tem ainda o fato de o ODBC ser pouco performático. (Lendo esse parágrafo lembrei que ODBC é um parto no Windows também :rofl::rofl::rofl:)

Enfim, acabei optando pelo JDBC.

Para o SQL Server temos o pacote pymssql que é o pacote mais utilizando para acesso ao SQL Server no Python. Funciona out-of-the-box com os nossos servidores. O pymssql não utiliza nem Driver, acho que ele implementa o protocolo TDS (Tabular Data Stream) no próprio Python, ainda não li a fundo o que tem por baixo do capô, só sei que funciona. @vitor.bellini e @nitai estão utilizando o pymssql para conectar nossos bancos ao Airflow.

O SQL Alchemy também funciona direitinho com os nossos bancos SQL Server, já conectei o SuperSet aos nossos bancos com SQL Alchemy e o @vitor.bellini também tem utilizado o SuperSet. Para usar o Dask é melhor ir de Alchemy também.

Para o Quartzo, na pasta /srv/jdbc_drivers tem o Driver JDBC TEIID teiid-12.2.1-jdbc.jar e tem também o Driver JDBC da Miscrosoft para conexão ao SQL Server, que em sua versão mais recente suporta acesso via NTLM (ou seja, com usuário e senha rede sem precisar de nenhum REALM) no Linux.

Nos notebooks de exemplo em R as duas conexões foram utilizando esses dois drivers JDBC.

Por enquanto, é melhor tomar como exemplo os Scripts que fiz em R e converter para Python, assim que sobrar uma energia aqui eu escrevo os notebooks de exemplo conectando ao SQL Server com pymssql e ao Quartzo com JDBC.

Enfim, dito tudo isso, eu sou contrário à instalação do ODBC no servidor. Se alguém souber um caminho feliz para fazer isso, toda ajuda é bem vinda :sweat_smile:.

Ambos os comandos para chamar o Python ( python e ipython3 ) no path deveriam apontar para o mesmo lugar, não lugares diferentes. Além disso, ao selecionar o kernel customizado no Jupyter Notebook / Lab e rodar o comando de importação, o erro apresentado deveria ser o da falta do pacote no sistema operacional, e não a inexistência da biblioteca Python.

Pelo que entendi o Python e o IPython foram chamados de ambientes distintos e por isso o binário está localizado em ambientes distintos. Todos os usuários por padrão ao abrir o Terminal estão no ambiente /opt/tljh/user que é um ambiente conda.

Para compreender melhor a mecânica do ambiente, sugiro a leitura do manual do TLJH https://tljh.jupyter.org/, basicamente a distribuição define 3 ambientes de trabalho: tem o Python do sistema operacional, tem um ambiente Python Virtual Env (venv) para a instalação do Hub e um ambiente Conda que é onde o usuário roda.

Os desenvolvedores optaram por essa configuração para tirar proveito do Systemd para configurar esse ambiente multiusuário e também porque eles desejam dar suporte ao Raspberry Pi no futuro e o Conda não tem pacotes ARM.

Para o Kernel R foi um parto definir um ambiente Conda com os pacotes R e eu acabei optando por rodar o Kernel em cima do R dos repositórios CRAN. Isso pode trazer problemas como a impossibilidade de se definir e congelar ambientes de desenvolvimento em R melhorando a reprodutibilidade da análise ou atualizações dos pacotes R acabarem quebrando no futuro, mas como é um ambiente de desenvolvimento e de análise exploratória, não acho que seja tão crítico assim. Alguém sabe dizer se é possível usar o ASDF para controlar o ambiente de desenvolvimento R?

O que indica que a configuração do ambiente não funcionou corretamente – usei o guia que você escreveu.

Essa dica que eu dei vale inclusive para outros ambientes Jupyter como os Kaggle e Colab do Google caso se queria criar um ambiente Conda. Mas como não há terminal é preciso fazer tudo via !.

1 Curtida

Maravilha das galáxias, @kafran! Nosso Ecossistema de Dados está ficando cada dia mais rico. Isso aqui tá virando uma escola pra mim. Obrigado! Já tô pensando em tatuar nossa logo :t_rex::
logo-ecodados-quadrado

Por enquanto não consigo dar feedbacks quanto ao Jupyterhub. Ainda tô desenferrujando e aprendendo os notebooks da vida. Tõ no rastro de vocês!

1 Curtida

Tentei usar o ODBC apenas como primeira tentativa baseado em uma pesquisa que fiz. Não fazia ideia de que seria difícil configurar, ou mesmo que seria possível usar JDBC em Python. Como não encontrei nenhum exemplo de uso de banco de dados em Python, fui seguindo o que encontrei e tentei usar o pacote pyodbc. Não faço nenhuma questão de usar o ODBC especificamente. :grin:

Na documentação encontrei apenas como instalar pacotes no ambiente geral para todos os usuários, não as instruções para o ambiente personalizado. Mesmo fazendo:

$ source activate env
(env) $ pip install nltk

(usei o pacote nltk como outro exemplo), e selecionando o kernel “env” no Lab, e reiniciando o servidor no Jupyter Hub, ele não encontra o pacote no Python ao fazer import nltk. O que estou fazendo errado?

Por que o Jupyter Lab está na versão 0.35.4 ainda? Na minha máquina local já estou usando a 1.0.4.

Consegui fazer uma consulta usando o SQL Alchemy e gostaria de compartilhar o caderno para ajudar a quem está começando, mas…

Quando copio cadernos Jupyter para essa pasta, com a intenção de compartilhá-los, ao abrir o caderno dá um erro “unhandled error”. O usuário dono do arquivo ainda é o meu, não sei se é por isso. Tentei mudar o dono para jupyter-kafran com chown, mas deu “operation not permitted”.

IMG_20190819_162035

Com calma jovem Jedi :vulcan_salute::joy:.
De cada vez um problema atacar você deve!
Mestre Yoda

Acho que já descobri o problema do environment.

Nas minhas instruções faltou uma parte importante, tente o seguinte processo:

$ source activate env
(env) $ conda install ipykernel
(env) $ python -m ipykernel install --user --name env
(env) $ cat .local/share/jupyter/kernels/env/kernel.json

{
 "argv": [
  "~/.conda/envs/env/bin/python", <- **verifique essa linha**
  "-m",
  "ipykernel_launcher",
  "-f",
  "{connection_file}"
 ],
 "display_name": "env",
 "language": "python"
}

Eu sei que você está usando o Pip apenas para me ajudar nos testes, mas quando for começar a brincar pra valer dê preferência ao Conda. Além do mais, a fim de evitar consumir nosso disco, procure usar o ambiente padrão, deixando para criar environments apenas quando realmente necessário.

Me dê um feedback se a instalação do ipykernel solucionou o problema para eu atualizar as instruções.

O seu está atrasado, nós já estamos na 1.0.6 :relieved:. :joy: brincadeiras à parte, eu não havia me atentado para a versão do JupyterLab, então, como eu havia dito, o TLJH utiliza três “ambientes”, os pacotes da distribuição, o conda e o pip :man_facepalming: tem sido um martírio gerenciar isso. Atualizei alguns pacotes do pip, entre eles o Lab.

Desde o conda 4.6.0 que foi adicionar suporte à interoperabilidade com o Pip. Já estamos no 4.7.11, essa feature já deve estar mais madura, estou com vontade de ativar isso por default no servidor, mas estou com medo de quebrar tudo porque ele vai substituir onde possível os pacotes do Pip pelos do Conda. https://docs.conda.io/projects/conda/en/latest/user-guide/configuration/pip-interoperability.html

2 Curtidas

Eu realmente não sei porque está dando aquele erro quando se move arquivos para a pasta compartilhada. No servidor as permissões são 770. Acho que vou mudar o dono da pasta para ver se altera algo. Enfim, de todo o jeito, ainda que dê o erro (pode ser bug do notebook/lab) o arquivo é movido normalmente para dentro da pasta. Identifiquei o seu notebook lá e aqui está abrindo normal. Tente fazer uploads para a pasta compartilhada para ver se dá o mesmo erro.

P.s.: Esse tipo de erro que você relatou costuma ocorrer com jupyter “corrompido”, por exemplo com um merge para ser feito no git. Antes de mover o documento, feche-o e dê um shutdown no Kernel dele, pode ser isso. Vamos investigar. Vou procurar se tem algum issue disso por aí.

1 Curtida

Inventei de colocar o Conda para controlar tudo e atualizar todos os pacotes e acabei quebrando o ambiente, mas já recuperei tudo. Realmente os Pacotes no Pypi e os pacotes no Conda têm algumas particularidades e agora começo a entender o porque dos desenvolvedores do TLJH terem decidido por manter os pacotes que estruturam o ambiente vindos do Pypi.

Mantive a instalação padrão do TLJH e atualizei os pacotes Pypi para a versão mais recente onde possível. Acho que está tudo funcionando redondinho agora. O ambiente padrão é o JupyterLab mais recente (1.0.6). Adicionei a extensão que dá suporte ao Git e adicionei uma extensão que dá suporte a um Diff, essa extensão é muuuito show.

Botões novos agora aparecem na barra de trabalho do JupyterLab com suporte ao Diff: Screenshot%20from%202019-08-20%2012-43-12

Me digam se isso não é Mind-Blowing :scream::boom:

Screenshot%20from%202019-08-20%2012-43-31

Com isso, acho que não vou mais instalar o jupyter-text. Me parece ser bem menos confuso fazer controle de versões apenas com o Git + Diff.

Happy Findings!

2 Curtidas

Assim deu certo. Obrigado!

Vejo que é bem parecido com o que faço localmente na minha máquina, só que uso o pip em vez do conda.

Instalei o ipykernel pelo conda e funcionou. Antes e depois de instalar, fiz um pip freeze e pude constatar que ao instalar pelo conda, ficou aparecendo os mesmos pacotes no pip. Então acho que o pip e o conda estão “enxergando”, de fato, cada um o pacote que o outro instala. Você chegou a habilitar aquela flag no conda?

Tentei de três maneiras e em todas elas o erro ocorre:

  1. copiar e colar no Lab (neste caso, inclusive, adicionalmente, dá um Erro 500 na hora de colar);
  2. comando cp no terminal;
  3. upload pela interface do Lab.

Também fiz shutdown nos kernels, não fez diferença.

Muito interessante! Ele faz diff direto de arquivos .ipynb.

Só que o diff só fica visível no Lab. No comando git diff e em ferramentas como GitLab ou Github o diff ainda é ininteligível. Então acho que vai depender mais do seu fluxo de trabalho para saber se vale a pena usar assim.

Caso ainda queira instalar o Jupytext, os comandos são:

$ conda install -c conda-forge jupytext

e dentro do ambiente do Jupyter:

python -m pip install jupytext --upgrade --user

jupyter serverextension enable jupytext

Muito bom! :clap:t5::clap:t5::clap:t5::clap:t5:
Manda as credenciais pra nim. Quero fazer um teste com o R.

1 Curtida

Show. Vou atualizar as instruções adicionando a instalação do ipykernel.

Mas isso já é esperado. Se você der um conda list todos os pacotes python serão listados e os que tiverem sido instalado com pip vai ter uma flag pypi.

Sim, mas aí o conda substituiu todos os pacotes pip pela versão mais recente existente no conda e simplesmente quebrou o ambiente. Tive que restaurar tudo. Os pacotes que os desenvolvedores do TLJH escolheram ser instalado via pip eu mantive no pip, apenas atualizei para a versão mais recente.

Eu ainda não consegui identificar porque isso ocorre, não quero dar permissão 777 na pasta compartilhada. Apesar do erro no navegador, o arquivo acaba sendo movido ou adicionado à pasta compartilhada, então, apesar do feedback visual que o usuário recebe a ação de mover o arquivo funciona.

Então, a ideia de usar o notebook é exatamente ter esse ambiente de desenvolvimento e os metadados em um arquivo reproduzível e compartilhável. Depois que conheci essa extensão que possibilita o diff dentro de um ambiente Jupyter, eu não vejo muito sentido em ter o jupytext. Até porque o maior problema do Jupyter era na hora de resolver algum conflito de merge no git, e o diff agora resolve isso.

Jupytext can write a given notebook to multiple files. In addition to the original notebook file, Jupytext can save the input cells to a text file — either a script or a Markdown document. Put the text file under version control for a clear commit history. Or refactor the paired script, and reimport the updated input cells by simply refreshing the notebook in Jupyter.

Para salvar o código fonte o usuário pode exportar um .py quando preciso. Será que não vai ficar confuso dentro do repositório ter o jupytext exportando mais arquivos?

Sim. Mas se ninguém conseguir abrir e usar o arquivo, qual seria o sentido de copiá-lo para uma área compartilhada?

Experimentei mais duas coisas:

  1. Fazer o download do arquivo que está dando erro na área compartilhada e abri-lo no Jupyter Lab local. Resultado: o arquivo abre normalmente. Aparentemente é um problema no ambiente do TLJH mesmo.
  2. Criar um novo caderno Jupyter, pela interface do Lab, dentro da pasta compartilhada. Resultado: 2 erros: “Error: 500 Internal Server Error” e “Launcher Error: model is undefined”.

tljh-erro

A ferramenta de diff é também ferramenta de merge? São duas coisas diferentes. Não cheguei a experimentar com ela ainda.

Na minha opinião, não fica confuso. O Jupytext não sincroniza com o .md ou com o .py sem você pedir. Só se você, para cada caderno, for no menu e selecionar que ele deve sincronizar o caderno com o formato desejado. No meu caso, estou colocando uma regra no .gitignore para não incluir arquivos .ipynb.

A minha ideia é pegar depois esses .py exportados automaticamente pelo Jupytext e chamá-los a partir de DAGs do Apache Airflow.

Por enquanto estou experimentando assim, para avaliar. Conforme o tempo e a interação com outras pessoas vou ver se esse fluxo de trabalho funciona bem.

Todas as ferramentas de Diff que eu conheço fazem também Merge :thinking:
Ainda não sei como essa que instalei no Lab vai funcionar pois também ainda não usei, mas ela é sim Diff/Merge.

Perfeito então Augusto. Vou instalar o JupyText para não quebrar o Workflow que você está habituado. :kissing_heart:

:thinking::thinking::thinking: hmmmmmn :cow: agora eu vi vantagem. Vou instalar o JupyText e deixar essa sua mente aí explodir nas ideias. Compartilha esse workflow com a gente depois.

1 Curtida

Eita, se você copiar da área compartilhada para a sua pasta não consegue editar? :scream: Essa é novidade, vou testar aqui.

Cara, em um time realmente colaborativo eu acho que a única utilidade da área compartilhada seria colocar DataSets e só (Até porque dependendo do tamanho do DataSet vai ser dor de cabeça no Git). Por mim todo e qualquer código deveria estar no Git. Mas aí fica do time. Em minha humilde opinião colocar notebooks em pasta compartilhada vai ser uma Zorrah. Eu coloquei só o Bem-Vindo ali com algumas orientações básicas e os exemplos de como conectar às nossas bases, mas qualquer trabalho sério de análise deveria estar no Git, seja o notebook seja o código fonte (JupyText :stuck_out_tongue_winking_eye:), tanto que estamos instalando ferramentas para isso.

1 Curtida

Prontinho @augusto.herrmann, instalei o Jupytext, está disponível no menu File do Notebook e no Command Palette do Lab.

1 Curtida