Como construir um data lake simples com MinIO e dbt
1. Introdução ao conceito de Data Lake com ferramentas modernas
Um data lake é um repositório centralizado que armazena dados em seu formato bruto, permitindo análises flexíveis sem a rigidez de esquemas predefinidos. Neste artigo, construiremos um data lake simples utilizando MinIO como armazenamento de objetos compatível com S3 e dbt como ferramenta de transformação de dados.
A combinação MinIO + dbt oferece uma stack leve, open source e extremamente funcional para projetos de dados de pequeno e médio porte. MinIO simula o serviço S3 da AWS localmente, enquanto o dbt permite transformar dados usando SQL puro, com versionamento e documentação automáticos.
Pré-requisitos:
- Docker e Docker Compose instalados
- Python 3.8+ com pip
- Conhecimentos básicos de SQL e linha de comando
2. Configurando o ambiente com MinIO
Vamos iniciar configurando o MinIO via Docker Compose. Crie um arquivo docker-compose.yml:
version: '3.8'
services:
minio:
image: minio/minio:latest
container_name: minio-datalake
ports:
- "9000:9000"
- "9001:9001"
environment:
MINIO_ROOT_USER: admin
MINIO_ROOT_PASSWORD: admin123
command: server /data --console-address ":9001"
volumes:
- ./minio-data:/data
Execute o comando para iniciar o serviço:
docker-compose up -d
Acesse o console do MinIO em http://localhost:9001 com usuário admin e senha admin123. Crie um bucket chamado datalake e gere as Access Keys (vamos usar minioadmin e minioadmin para simplificar).
Para testar, faça upload manual de um arquivo CSV de exemplo:
# Exemplo de dados de vendas (vendas.csv)
id,produto,quantidade,preco,data
1,Notebook,2,3500.00,2024-01-15
2,Mouse,5,150.00,2024-01-16
3,Teclado,3,250.00,2024-01-17
3. Estruturando o data lake em camadas
Um data lake bem organizado segue o padrão de zonas. Vamos definir três camadas principais:
- raw/: Dados brutos como foram ingeridos, sem transformações
- staging/: Dados limpos e tipados, prontos para análise
- analytics/: Agregações e visões de negócio
Estrutura de pastas no MinIO:
datalake/
├── raw/
│ └── vendas/
│ └── ano=2024/
│ └── mes=01/
│ └── vendas.csv
├── staging/
│ └── vendas_limpas/
│ └── ano=2024/
│ └── vendas_limpas.parquet
└── analytics/
└── vendas_agregadas/
└── ano=2024/
└── vendas_agregadas.parquet
Boas práticas:
- Use partições por data para facilitar consultas
- Nomeie arquivos com prefixos que indiquem data/hora
- Versionie esquemas com metadados (arquivo _SUCCESS para indicar completude)
4. Conectando o dbt ao MinIO
Instale o dbt e o adaptador DuckDB (que suporta leitura/escrita em S3):
pip install dbt-core dbt-duckdb
Configure o perfil de conexão em ~/.dbt/profiles.yml:
minio_datalake:
target: dev
outputs:
dev:
type: duckdb
path: md:my_database
external_root: s3://datalake/
s3_region: us-east-1
s3_endpoint: localhost:9000
s3_access_key_id: minioadmin
s3_secret_access_key: minioadmin
s3_use_ssl: false
s3_url_style: path
Crie um projeto dbt:
dbt init datalake_project
cd datalake_project
Teste a conexão:
dbt debug
5. Criando o primeiro pipeline de transformação com dbt
Vamos criar modelos que leem dados do MinIO e escrevem resultados de volta. Primeiro, um modelo para ler o CSV bruto (models/raw/vendas_raw.sql):
{{ config(
materialized='external',
location='raw/vendas/ano=2024/mes=01/vendas.parquet',
format='parquet'
) }}
SELECT * FROM read_csv_auto('s3://datalake/raw/vendas/ano=2024/mes=01/vendas.csv')
Agora, um modelo de staging que limpa e tipa os dados (models/staging/vendas_staging.sql):
{{ config(
materialized='table',
schema='staging'
) }}
SELECT
id::INTEGER AS id_venda,
produto,
quantidade::INTEGER AS quantidade,
preco::DECIMAL(10,2) AS preco_unitario,
data::DATE AS data_venda,
quantidade * preco AS valor_total
FROM {{ ref('vendas_raw') }}
WHERE quantidade > 0
Por fim, uma agregação analítica (models/analytics/vendas_agregadas.sql):
{{ config(
materialized='external',
location='analytics/vendas/ano=2024/vendas_agregadas.parquet',
format='parquet'
) }}
SELECT
produto,
COUNT(*) AS total_vendas,
SUM(quantidade) AS total_quantidade,
SUM(valor_total) AS receita_total,
AVG(preco_unitario) AS preco_medio
FROM {{ ref('vendas_staging') }}
GROUP BY produto
Execute o pipeline:
dbt run
6. Orquestração e automação do pipeline
Crie um script Python para execução agendada (run_pipeline.py):
import os
import subprocess
from datetime import datetime
# Configurações via variáveis de ambiente
os.environ['MINIO_ENDPOINT'] = 'localhost:9000'
os.environ['MINIO_ACCESS_KEY'] = 'minioadmin'
os.environ['MINIO_SECRET_KEY'] = 'minioadmin'
def run_dbt():
print(f"Iniciando pipeline em {datetime.now()}")
# Executa dbt run
result = subprocess.run(['dbt', 'run'], capture_output=True, text=True)
if result.returncode == 0:
print("Pipeline executado com sucesso!")
print(result.stdout)
else:
print("Erro na execução:")
print(result.stderr)
# Gera documentação
subprocess.run(['dbt', 'docs', 'generate'])
if __name__ == "__main__":
run_dbt()
Para automação, adicione ao cron (Linux) ou Agendador de Tarefas (Windows):
# Executar diariamente às 2h da manhã
0 2 * * * cd /caminho/projeto && python run_pipeline.py
7. Consultas e análises ad hoc sobre o data lake
Com DuckDB, podemos consultar diretamente os arquivos Parquet no MinIO:
# Conectar ao DuckDB
duckdb
# Consultar dados analíticos
SELECT * FROM read_parquet('s3://datalake/analytics/vendas/ano=2024/vendas_agregadas.parquet');
# Análise agregada
SELECT
produto,
receita_total,
total_quantidade,
receita_total / total_quantidade AS ticket_medio
FROM read_parquet('s3://datalake/analytics/vendas/ano=2024/vendas_agregadas.parquet')
ORDER BY receita_total DESC;
Comparação de performance entre formatos:
-- CSV (mais lento, sem compressão)
.timer on
SELECT COUNT(*) FROM read_csv_auto('s3://datalake/raw/vendas/ano=2024/mes=01/vendas.csv');
-- Parquet (mais rápido, compressão automática)
SELECT COUNT(*) FROM read_parquet('s3://datalake/raw/vendas/ano=2024/mes=01/vendas.parquet');
8. Considerações finais e próximos passos
Esta abordagem simples com MinIO e dbt é ideal para:
- Prototipagem rápida de pipelines de dados
- Projetos pessoais ou pequenas equipes
- Ambientes de desenvolvimento e teste
Limitações:
- Escala limitada (MinIO single-node para grandes volumes)
- Sem concorrência avançada (dbt executa transformações sequenciais)
- Governança básica (sem catálogo de dados integrado)
Evoluções possíveis:
- Adicionar Apache Iceberg para versionamento de tabelas
- Implementar catálogo de dados com Apache Atlas ou Amundsen
- Substituir DuckDB por Spark para processamento distribuído
- Adicionar camada de orquestração com Airflow ou Prefect
Referências
- Documentação oficial do MinIO — Guia completo de instalação, configuração e operação do MinIO via Docker
- Documentação do dbt — Referência oficial para criação de projetos, modelos e perfis de conexão
- dbt-duckdb adapter documentation — Repositório oficial com instruções de instalação e configuração do adaptador DuckDB para dbt
- DuckDB S3 Extension Guide — Tutorial sobre como conectar DuckDB ao S3 (e MinIO) para leitura/escrita de arquivos
- Best Practices for Data Lake Architecture — Artigo da Databricks sobre boas práticas de organização de data lakes em camadas