Ambiente Docker para análise de dados
2020 Oct 27Docker para análise de dados
TL;DR: Reproducibilidade garante não só mais transparência, como é uma ferramenta essencial para qualquer trabalho de “Ciência”.
…
Ao longo da minha carreira eu venho lutado um bom combate em prol da reprodutibilidade em ciência de dados dentro do mundo corporativo.
Seja falando de algumas ferramentas que eu uso diariamente, seja falando que até a toda poderosa revista científica Nature aprova pesquisa que não seria aprovada nem em trabalho de TCC, de como a reproducibilidade ajuda na detecção de problemas metodológicos.
Por fim eu penso que a reproducibilidade não apenas acaba com a cultura de opinião, como desmascara um monte de pessoas com muita opinião e poucos dados, dado que os dados e o código falam por sí só.
Nesse post eu vou compartilhar o que eu uso no dia a dia para análises de dados, prototipações e afins e espero que ajude a quem esteja indo para uma direção de maior reprodutibilidade nas análises.
Muito do que eu estou usando tem ligação direta com o post do Danny Janz, chamado “Containerize your whole Data Science Environment (or anything you want) with Docker-Compose”, então para quem quiser algo mais detalhado no que se refere às imagens Docker eu recomendo fortemente o post.
Estrutura de arquivos
Eu uso a seguinte estrutura de diretórios para gerar o ambiente para cada análise:
analysis
|- src
| - data
|- Dockerfile
|- .dockerignore
|- docker-compose.yml
|- README.md
|- requirements.txt
Tem muitos templates legais de estrutura de projetos como o excelente Cookiecutter Data Science, mas no meu caso eu prefiro de começar com menos pastas possíveis, por questão de organização pessoal. Claro que dependendo do projeto pode ser que exista a necessidade de algo mais estruturado desde o começo.
Ainda em relação a diretórios, eu costumo colocar os dados dentro da pasta src
.
Eu sei que isso é um anti-padrão, mas isso além de me aliviar em relação à
navegação nos sub-diretórios eu tenho a vantagem de usar o
DVC para versionamento dos dados (os dados em
si ficam no AWS S3 e o DVC registra apenas os ponteiros de versionamento
dos aquivos) e os arquivos do DVC ficam organizados em um único
diretório.
Mas vamos ao que interessa que são os outros arquivos.
Dockerfile
No Dockerfile
eu uso como imagem base o python:3.6-buster
.
A minha escolha pelo Buster
foi (i) pela estabilidade da distribuição,
(ii) o fato de que vai ter ainda um longo suporte para o Python 3.6,
e (iii) ainda que a imagem não seja um primor em relação ao espaço em disco em comparação
com o péssimo Alpine, ao menos
não vai ter o absurdo tamanho de imagem das versões ubuntu:18+
.
Como alguns desses pipelines eu executo nos do Gitlab/Github
e no Amazon ECS eu tenho alguns
critérios de desempenho no deploy (i.e. o deploy não pode demorar muito)
o python:3.6-buster
consegue entregar um balanço interessante entre
a distribuição e a velocidade da criação da imagem.
O artigo do Itamar Turner-Trauring chamado “The best Docker base image for your Python application (April 2020)”
ajudou muito na minha escolha. Contudo, como eu tive alguns problemas
no passado com o uso de imagens slim
eu acabei indo para a
imagem buster
completa.
(A propósito o blog dele é sensacional para quem trabalha com Python + Docker e tem um benchmark interessante sobre essas imagens.)
O restante do Dockerfile
não é nada especial, a não ser que para desenvolvimento local eu rodo o Jupyter Notebook
com as opções padrão:
.dockerignore
O Alexei Ledenev fez um post sobre
os perigos e os problemas de não usar o .dockerignore
.
Essencialmente ele coloca que o não uso do
.dockerignore
além de deixar o build lento, a
ausência do .dockerignore
pode potencialmente causar casos
de vazamento de variável de ambiente e credenciais.
Este é o .dockerignore
que eu uso:
requirements.txt
No requirements.txt
eu mantenho um conjunto canônico
de coisas que eu geralmente uso para análise exploratória
de dados como jupyter
, matplotlib
, pandas
e numpy
.
Caso eu precise trainar algum algoritmo com referência
(baseline) eu deixo o sklearn
.
Como parte dos meus dados estão no AWS S3 e em bancos de dados
do MySQL eu já deixo como padrão os pacotes boto3
e o s3fs
para acessar os dados no S3, e o PyMySQL
se eu precisar extrair
dados via MySQL queries.
Dois pontos sobre o requirements.txt
que eu gosto de ressaltar:(i)
manutenção de versão de desenvolvimento e produção e (ii) a
importância de passar a versão do pacote pip de forma explícita.
Eu sempre deixo as versões do Docker iguais ao que tem em produção não importa o quão grande é o pacote.
Isso garante desde o minuto zero da análise que eu não vou ter problemas de ambiente com bibliotecas diferentes.
Isso garante a consistência de ambiente (sistema operacional +
bibliotecas), o que vai evitar que eu cometa erros como o
cometido pela Universidade do Hawaii em relação ao uso da
biblioteca glob
.
No caso, o glob
apresenta diferenças de comportamento de
ordenação (e na consequente sequência de leitura dos arquivos)
de acordo com o sistema operacional.
Devido a uma falta de atenção dos pesquisadores que pegaram o script do estudo original escrito 6 anos antes (época em que o sistema operacional lidava com a ordenação e a biblioteca não tinha a função de ordenação) a versão do estudo publicado começou a apresentar diferenças e invalidou parte dos experimentos.
Resultado final: Mais de 100 artigos tiveram que ser corrigidos.
Lição: Sempre use a versão explicitamente no requirements.txt
.
O outro ponto sobre o requirements.txt
é que como eu uso
alguns pacotes que tem algumas dependências obscuras
(pela falta de um nome melhor), eu sempre estou olhando alguns
relatórios de vulnerabilidade
do snyk, Safety e do Bandit.
Para quem não tiver um orçamento para algumas dessas feramentas, o Github oferece o mesmo tipo de serviço.
Dá um pouco mais de trabalho, pois se alguma alteração tiver que ser colocada em em produção isso tem que ser transposto para os ambientes de desenvolvimento.
Ao menos pra mim funciona bem, pois eu tenho de cabeça o controle de tudo o que precisa ser checado para garantir a consistência dos ambientes.
docker-compose.yml
A primeira pergunta que pode aparecer é: “Porque usar o docker compose?”.
No meu caso foi uma necessidade de realizar prototipação em ferramentas fora do espectro de Ciência de Dados, como Redis para teste de caching com predições/scores feitas(os) offline, SQLLite quando eu precisei testar coisas de engenharia de dados para embarcar um banco de dados, ou quando tive que usar o H2O.ai para um treinamento de machine learning específico.
Nestes casos eu precisei apenas escolher uma imagem destas plataformas (ou criar uma própria) para ter todos serviços isolados em cada um em seu próprio contêiner. Adeus inferno de dependências.
Uma última coisa que eu queria ressaltar em relação ao uso de configurações e credenciais em variáveis de ambiente: Evite ao máximo a utilização de variáveis de ambiente no Docker, dado os problemas claros de segurança como pode ser visto no post do Diego Monica chamado “Why you shouldn’t use ENV variables for secret data”
Eu recomendo o uso para gerenciamento de credenciais o uso do Docker Secrets. Pessoalmente, só quando eu preciso passar algo que não vale a pena armazenar no Docker Secrets, aí eu passo na forma que está no arquivo abaixo
README.md
Finalmente no README.md
eu coloco as instruções como, que
é a solução, quais são os dados, o ticket do Jira, como fazer
o build
da imagem e como subir o container no Docker Compose.
export ENV_VAR_1='***********' && \
export ENV_VAR_2='***********' && \
docker build -t data_science_analysis . && \
docker-compose up
Por fim, eu vou no Jupyter Notebook no http://localhost:8888/
com a senha da minha escolha. Neste caso eu coloquei a senha do
Jupyter Notebook como root
apenas para fins de demonstração.
Considerações Finais
Pessoalmente eu penso no Docker como uma ferramenta que revolucionou a forma de fazer ciência de dados no que diz respeito a aspectos como reprodutibilidade e gerenciamento de ambientes.
A configuração acima não é definitiva, mas é a forma que eu encontrei para andar mais rápido em prototipações e análises e espero que ajude a quem estiver começando a incorporar o Docker nas análises de dados.
Referências
- Pedro Paulo dos Santos, UM DOCKERFILE!!!— Imagens Docker multi-stage para múltiplas arquiteturas
- Érick Barbosa, Ambiente de desenvolvimento com Docker e Jupyter Notebook
- Christian Costa, Docker: Do Zero a Marinheiro Entendendo o que são containers e primeiros comandos Docker!
- Hamel Husain, How Docker Can Help You Become A More Effective Data Scientist
- Danny Janz, Containerize your whole Data Science Environment (or anything you want) with Docker-Compose