Docker para Python e Projetos de Dados
Gerenciar dependências para projetos de dados em Python pode se tornar complicado rapidamente. O Docker ajuda a criar ambientes consistentes que podem ser construídos, compartilhados e implantados com facilidade. Entre as versões do Python, ambientes virtuais, pacotes de sistema e diferenças de sistema operacional, pode levar mais tempo para executar o código de outra pessoa em sua máquina do que entender o próprio código. O Docker resolve esse problema embalando o código e todo o ambiente – versão do Python, dependências, bibliotecas de sistema – em um único artefato chamado de imagem. A partir da imagem, é possível iniciar contêineres que executam de forma idêntica em seu laptop, na máquina de um colega de trabalho e em um servidor de nuvem. Você deixa de depurar ambientes e começa a entregar trabalho.
Neste artigo, você aprenderá sobre o Docker por meio de exemplos práticos com foco em projetos de dados: contêinerizando um script, servindo um modelo de aprendizado de máquina com o FastAPI, conectando um pipeline de vários serviços com o Docker Compose e agendando um trabalho com um contêiner cron. Antes de trabalhar com os exemplos, você precisará ter o Docker instalado em sua máquina. Se você quiser uma rápida atualização, há alguns artigos que podem ajudá-lo a se atualizar. Você não precisa ter conhecimentos profundos sobre o Docker para seguir em frente. Cada exemplo explica o que está acontecendo à medida que você avança.
Vamos começar com o caso de uso mais comum: você tem um script em Python e um arquivo requirements.txt, e deseja que ele execute de forma confiável em qualquer lugar. Construiremos um script de limpeza de dados que lê um arquivo CSV de vendas brutas, remove duplicatas, preenche valores ausentes e escreve uma versão limpa no disco. O projeto é organizado da seguinte forma: o script de limpeza de dados usa a biblioteca Pandas para realizar o trabalho pesado. É importante fixar as versões exatas das dependências. Sem isso, o comando pip install pandas pode instalar versões diferentes em máquinas diferentes. As versões fixadas garantem que todos obtenham o mesmo comportamento. Você pode definir as versões exatas no arquivo requirements.txt da seguinte forma:
O arquivo Dockerfile constrói uma imagem minimalista e amigável ao cache para o script de limpeza: Há algumas coisas dignas de nota aqui. Usamos python:3.11-slim em vez da imagem completa do Python porque é significativamente menor e remove pacotes que você não precisa. Copiamos o arquivo requirements.txt antes de copiar o resto do código, e isso é intencional. O Docker constrói imagens em camadas e cacheia cada uma delas. Se você mudar apenas o arquivo clean_data.py, o Docker não reinstalará todas as suas dependências na próxima construção. Ele reutiliza a camada cacheada do pip e pula diretamente para copiar o script atualizado. Essa pequena decisão de ordenação pode economizar minutos de tempo de reconstrução.
Com a imagem construída, você pode executar o contêiner e montar a pasta de dados local: A flag -v $(pwd)/data:/app/data monta a pasta local data/ no contêiner em /app/data. É assim que o script lê o arquivo CSV e como a saída limpa é escrita de volta para a máquina. Nada é embutido na imagem, e os dados ficam no sistema de arquivos. A flag –rm remove automaticamente o contêiner após ele terminar. Como este é um script de uso único, não há motivo para manter um contêiner parado ao redor.
Você treinou um modelo e deseja torná-lo disponível via HTTP para que outros serviços possam enviar dados e obter previsões de volta. O FastAPI funciona bem para isso: é rápido, leve e lida com a validação de entrada com o Pydantic. O projeto separa o artefato do modelo do código da aplicação: O seguinte aplicativo carrega o modelo uma vez no início e expõe um endpoint /predict: A classe PredictRequest faz a validação de entrada para você. Se alguém enviar uma solicitação com um campo ausente ou uma string onde um número é esperado, o FastAPI rejeita com uma mensagem de erro clara antes que o código do modelo seja executado. O modelo é carregado uma vez no início – e não a cada solicitação – o que mantém os tempos de resposta rápidos. O endpoint /health é uma pequena mas importante adição: o Docker, os balanceadores de carga e as plataformas de nuvem usam para verificar se o serviço está realmente funcionando e pronto.
Este Dockerfile embute o modelo diretamente na imagem, de modo que o contêiner é completamente autossuficiente: O modelo.pkl é embutido na imagem no momento da construção. Isso significa que o contêiner é completamente autossuficiente, e você não precisa montar nada ao executá-lo. A flag –host 0.0.0.0 diz ao Uvicorn para ouvir em todas as interfaces de rede dentro do contêiner, e não apenas no localhost. Sem isso, você não poderá alcançar a API de fora do contêiner. Construa a imagem e inicie o servidor API: Teste com o comando curl:
Projetos de dados reais raramente envolvem apenas um processo. Você pode precisar de um banco de dados, um script que carrega dados nele e um painel que lê dele – tudo executando juntos. O Docker Compose permite definir e executar vários contêineres como um único aplicativo. Cada serviço tem seu próprio contêiner, mas todos compartilham uma rede privada, para que possam se comunicar. O pipeline divide cada serviço em seu próprio subdiretório: Este arquivo Compose declara os três serviços e os conecta com verificações de saúde e variáveis de ambiente de URL compartilhadas: Este script espera brevemente pelo banco de dados e, em seguida, carrega um CSV na tabela de vendas usando o SQLAlchemy: Vamos dar uma olhada mais de perto no arquivo Compose. Cada serviço é executado em seu próprio contêiner, mas todos estão na mesma rede gerenciada pelo Docker, para que possam se alcançar usando o nome do serviço como nome de host. O carregador se conecta a db:5432 – e não ao localhost – porque db é o nome do serviço, e o Docker lida com a resolução de DNS automaticamente. A verificação de saúde no serviço PostgreSQL é importante. A dependência sozinha apenas aguarda o contêiner iniciar, e não para o PostgreSQL estar pronto para aceitar conexões. A verificação de saúde usa pg_isready para confirmar que o banco de dados está realmente funcionando antes que o carregador tente se conectar. O volume pgdata persiste o banco de dados entre as execuções; parar e reiniciar o pipeline não apagará seus dados.
Levante todos os serviços com um único comando: Para parar tudo, execute: Às vezes, você precisa que um script seja executado em um cronograma. Talvez ele obtenha dados de um endpoint de API a cada hora e os escreva em um banco de dados ou arquivo. Você não deseja configurar um sistema de orquestração completo, como o Airflow, para algo tão simples. Um contêiner cron faz o trabalho de forma limpa. O projeto inclui um arquivo crontab ao lado do script e do Dockerfile: Este script usa a biblioteca Requests para atingir um endpoint de API e salva os resultados como um arquivo CSV com data e hora: O arquivo crontab agenda o script para ser executado a cada hora e redireciona toda a saída para um arquivo de log: A parte >> /var/log/fetch.log 2>&1 redireciona tanto a saída padrão quanto a saída de erro para um arquivo de log. É assim que você inspeciona o que aconteceu após o fato.
Este Dockerfile instala o cron, registra o cronograma e mantém o cron rodando em primeiro plano: A flag cron -f é importante aqui. O Docker mantém um contêiner vivo enquanto o processo principal estiver em execução. Se o cron executasse em segundo plano (sua configuração padrão), o processo principal terminaria imediatamente, e o Docker pararia o contêiner. A flag -f mantém o cron em execução em primeiro plano, de modo que o contêiner permanece vivo. Construa a imagem e inicie o contêiner em modo desanexado: Verifique os logs a qualquer momento: A pasta de saída é montada a partir da sua máquina local, então os arquivos CSV aterrissam no seu sistema de arquivos, mesmo que o script execute dentro do contêiner.
Espero que você tenha achado este artigo sobre o Docker útil. O Docker não precisa ser complicado. Comece com o primeiro exemplo, substitua seu próprio script e dependências, e se familiarize com o ciclo de construção-execução. Uma vez que você tenha feito isso, os outros padrões seguem naturalmente. O Docker é uma boa escolha quando: No entanto, você nem sempre precisa usar o Docker para todo o seu trabalho em Python. É provavelmente exagero quando: Se você estiver interessado em ir mais longe, confira os 5 Passos Simples para Dominar o Docker para a Ciência de Dados. Feliz programação!
Bala Priya C é uma desenvolvedora e escritora técnica da Índia. Ela gosta de trabalhar na interseção da matemática, programação, ciência de dados e criação de conteúdo. Suas áreas de interesse e especialização incluem DevOps, ciência de dados e processamento de linguagem natural. Ela gosta de ler, escrever, codificar e café! Atualmente, ela está trabalhando em aprender e compartilhar seu conhecimento com a comunidade de desenvolvedores, autorando tutoriais, guias de como fazer, artigos de opinião e mais. Bala também cria visões gerais de recursos e tutoriais de codificação. Obtenha o ebook gratuito ‘Dicionário de Bolso de Inteligência Artificial do KDnuggets’ junto com o boletim informativo líder em Ciência de Dados, Aprendizado de Máquina, IA e Análise, diretamente em sua caixa de entrada. Ao se inscrever, você aceita a Política de Privacidade do KDnuggets.