Skip to content
Learni
View all tutorials
Data Engineering

Comment créer un Data Lake scalable en 2026

Introduction

Un data lake est un stockage centralisé de données brutes à grande échelle, suivant le principe schema-on-read : les données arrivent sans schéma imposé, et sont interprétées lors de la lecture. Contrairement aux data warehouses structurés (schema-on-write), il gère volumes massifs de données hétérogènes : logs JSON, images, CSV, vidéos. En 2026, avec l'explosion des données IA (LLM fine-tuning, RAG), les data lakes alimentent les pipelines ML en données historiques riches.

Ce tutoriel intermediate vous guide pour un data lake local scalable avec MinIO (API S3 100% compatible), Python pour ingestion partitionnée (Parquet optimisé), et DuckDB pour queries SQL vectorielles. Analogie : comme un lac naturel où l'eau (données) s'accumule librement, filtrable à la demande. À la fin, setup fonctionnel, extensible à AWS S3 ou Azure. Temps estimé : 30 min. Idéal pour bookmark par data engineers. (142 mots)

Prérequis

  • Docker et Docker Compose installés (version 20+).
  • Python 3.10+ avec pip.
  • Accès internet pour images Docker et paquets PyPI.
  • Connaissances de base en data engineering : Parquet, partitioning, S3-like storage.
Installez les libs Python : pip install minio pandas pyarrow duckdb. Téléchargez mc (MinIO Client) via le code fourni.

Démarrer MinIO avec Docker Compose

docker-compose.yml
version: '3.8'

services:
  minio:
    image: minio/minio:latest
    container_name: minio-server
    ports:
      - "9000:9000"  # API S3
      - "9001:9001"  # Console UI
    environment:
      MINIO_ROOT_USER: minioadmin
      MINIO_ROOT_PASSWORD: minioadmin
    volumes:
      - minio_data:/data
    command: server /data --console-address ":9001"

  volumes:
    minio_data:

networks:
  default:
    name: datalake-net

Ce fichier Docker Compose lance MinIO en serveur S3-like avec persistance de données. Ports 9000 (API) et 9001 (UI web). Utilisez docker compose up -d pour démarrer. Accédez à l'UI : http://localhost:9001 (login: minioadmin/minioadmin). Piège : sans volume, données perdues au redémarrage.

Accéder à l'interface MinIO

Lancez docker compose up -d dans le dossier du fichier. Vérifiez les logs : docker logs minio-server. Ouvrez http://localhost:9001 pour l'UI intuitive : explorer buckets, monitorer usage. MinIO émule parfaitement AWS S3, idéal pour dev/test sans coûts cloud. Prochaine étape : client CLI mc pour automation.

Installer et configurer le client MinIO mc

setup-mc.sh
#!/bin/bash

# Télécharger mc (Linux/macOS, adaptez pour Windows)
curl -O https://dl.min.io/client/mc/release/linux-amd64/mc
chmod +x mc
sudo mv mc /usr/local/bin/mc

# Configurer alias pour notre MinIO local
mc alias set myminio http://localhost:9000 minioadmin minioadmin

# Vérifier connexion
echo "Buckets disponibles :"
mc ls myminio

# Nettoyage optionnel : rm mc si pas sudo

Ce script installe mc (MinIO Client, comme AWS CLI pour S3) et crée un alias 'myminio' pointant vers votre instance locale. Exécutez bash setup-mc.sh. Vérifiez : mc admin info myminio. Piège : firewall bloque port 9000 → autorisez-le. mc est essentiel pour scripting buckets/policies.

Créer le bucket Data Lake

Buckets = conteneurs logiques dans S3/MinIO, comme répertoires racine. Pour data lake, un bucket 'datalake' avec policy read publique (local only). Cela permet queries directes sans auth lourde. Utilisez mc pour idempotence : relancez sans erreur.

Créer bucket et policy de lecture

create-bucket.sh
#!/bin/bash

BUCKET=datalake

# Créer bucket si inexistant
mc mb myminio/$BUCKET

# Policy JSON pour read public (local dev only)
cat > public-read.json << EOF
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Principal": "*",
      "Action": ["s3:GetObject"],
      "Resource": ["arn:aws:s3:::$BUCKET/*"]
    }
  ]
}
EOF

mc policy add public-read myminio/$BUCKET public-read.json

# Vérifier
echo "Policy :"
mc policy get-json myminio/$BUCKET
mc ls myminio

Crée bucket 'datalake' et applique policy JSON autorisant lectures anonymes (sûr en local). Exécutez bash create-bucket.sh. Vérifiez UI MinIO. Piège : policy malformée → JSON valide requis ; en prod, utilisez IAM fines. Cela rend le lake queryable sans creds.

Ingérer des données partitionnées

Simulons données de ventes : 1000 lignes (id, date, produit, montant, région). Partitioning par year/month optimise queries (pruning). Convertir CSV → Parquet (colonne, compressé, schema embedded). MinIO lib Python pour upload atomic.

Script Python : Générer et uploader en Parquet

ingest-data.py
import pandas as pd
import pyarrow as pa
import pyarrow.parquet as pq
from minio import Minio
from io import BytesIO
import numpy as np
from datetime import datetime, timedelta

client = Minio(
    "localhost:9000",
    access_key="minioadmin",
    secret_key="minioadmin",
    secure=False
)

# Générer data sample
np.random.seed(42)
dates = pd.date_range('2023-01-01', '2023-12-31', freq='H')[:1000]
data = pd.DataFrame({
    'id': range(1000),
    'date': dates,
    'product': np.random.choice(['Laptop', 'Phone', 'Tablet'], 1000),
    'amount': np.random.uniform(100, 1000, 1000),
    'region': np.random.choice(['EU', 'US', 'ASIA'], 1000)
})

# Partitionner par year/month
data['year'] = data['date'].dt.year.astype(str)
data['month'] = data['date'].dt.month.astype(str).str.zfill(2)

for (year, month), group in data.groupby(['year', 'month']):
    table = pa.Table.from_pandas(group)
    buffer = BytesIO()
    pq.write_table(table, buffer)
    buffer.seek(0)
    client.put_object(
        "datalake",
        f"sales/year={year}/month={month}/sales.parquet",
        io.BytesIO(buffer.read()),
        length=buffer.tell(),
        content_type="application/parquet"
    )
    print(f"Uploadé : year={year}/month={month}")

print("Ingestion terminée. Vérifiez UI MinIO.")

Génère dataset ventes partitionné, convertit en Parquet (efficace x10 vs CSV), upload via MinIO SDK. Partition year/month=pruning auto. Exécutez python ingest-data.py. Piège : sans pyarrow, schema perdu ; buffer pour in-memory write. Scalable à TB avec Dask.

Interroger le Data Lake avec SQL

DuckDB : OLAP engine embarqué, lit Parquet direct de S3 sans ETL. Configurez endpoint/credentials pour MinIO. Queries pushdown : filters partition prunés, agrégations vectorisées. Analogie : SQL sur fichiers comme sur DB.

Script Python : Query SQL avec DuckDB

query-lake.py
import duckdb

con = duckdb.connect()

# Installer/charger extension S3
con.execute("INSTALL httpfs; LOAD httpfs;")

# Config MinIO comme S3 (no secure)
con.execute """
SET s3_endpoint='localhost:9000';
SET s3_access_key='minioadmin';
SET s3_secret_key='minioadmin';
SET s3_region='us-east-1';
"""

# Query partitionnée : ventes EU 2023
result = con.execute(
    """
    SELECT 
        product, 
        AVG(amount) as avg_amount,
        COUNT(*) as sales_count
    FROM 's3://datalake/sales/year=2023/*/*.parquet'
    WHERE region = 'EU'
    GROUP BY product
    ORDER BY avg_amount DESC
    """
).fetchdf()

print(result)

# Export optionnel
result.to_csv('query-result.csv', index=False)
print("Résultat exporté en CSV.")

Configure DuckDB pour MinIO/S3, query SQL sur Parquet partitionné (pruning year=2023). Agrégations pushdown ultra-rapides. Exécutez python query-lake.py. Piège : oublie LOAD httpfs → erreur endpoint ; policy read requise. Scalable à PB avec workers.

Ajouter des données incrémentales

Data lakes évoluent : append sans downtime. Réutilisez ingest pour nouveaux lots (schema evolution via Parquet metadata). Vérifiez atomicité.

Script append données 2024

append-data.py
import pandas as pd
import pyarrow as pa
import pyarrow.parquet as pq
from minio import Minio
from io import BytesIO
import numpy as np
from datetime import datetime

client = Minio("localhost:9000", access_key="minioadmin", secret_key="minioadmin", secure=False)

# Nouvelles data 2024 (évolue : ajoute 'category')
np.random.seed(123)
dates = pd.date_range('2024-01-01', '2024-03-31', freq='H')[:500]
data_new = pd.DataFrame({
    'id': range(1000, 1500),
    'date': dates,
    'product': np.random.choice(['Laptop Pro', 'Phone Ultra'], 500),
    'amount': np.random.uniform(500, 2000, 500),
    'region': np.random.choice(['EU', 'US'], 500),
    'category': np.random.choice(['Electronics', 'Mobile'], 500)  # Schema evolution
})

data_new['year'] = data_new['date'].dt.year.astype(str)
data_new['month'] = data_new['date'].dt.month.astype(str).str.zfill(2)

for (year, month), group in data_new.groupby(['year', 'month']):
    table = pa.Table.from_pandas(group)
    buffer = BytesIO()
    pq.write_table(table, buffer)
    buffer.seek(0)
    client.put_object(
        "datalake",
        f"sales/year={year}/month={month}/sales.parquet",
        io.BytesIO(buffer.read()),
        length=buffer.tell(),
        content_type="application/parquet"
    )
    print(f"Appended : year={year}/month={month}")

print("Append terminé. Re-query pour vérifier.")

Appende data 2024 avec nouvelle colonne 'category' (evolution schema tolérée). Même pattern partitioning. Exécutez après ingest initial. Piège : overwrite accidentel → utilisez noms fichiers timestamped en prod. DuckDB lit auto nouvelles cols.

Bonnes pratiques

  • Partitionnez toujours : year/month/day pour pruning (gain x100 perf).
  • Utilisez table formats : Migrez vers Iceberg/Delta pour ACID, time-travel (vs Parquet pur).
  • Catalog central : Intégrez Hive Metastore ou AWS Glue pour métadonnées.
  • Data quality : Validez schemas/NULLs à l'ingestion (Great Expectations).
  • Sécurité : Policies least-privilege, encryption SSE-S3 ; pas public en prod.
  • Monitoring : Prometheus + MinIO metrics pour usage/throughput.

Erreurs courantes à éviter

  • Data swamp : Sans gouvernance/catalog, données ingérables → imposez tagging/zones (raw/curated).
  • Oubli partitioning/compression : Scans full → coûts/query lents ; toujours Snappy/Zstd.
  • Schema-on-write forcé : Tue flexibilité lake → laissez schema-on-read, fixez à consommation.
  • Pas de versioning : Perte audits → Iceberg snapshots ou S3 versioning.
  • Ignorer local vs prod : Policy public OK dev, mais VPC/ IAM en cloud.

Pour aller plus loin

  • Production : Migrez vers AWS S3 + Athena + Glue Catalog (remplacez endpoint).
  • Avancé : Apache Iceberg avec Spark/Trino pour tables transactionnelles.
  • Outils : Airflow pour orchestration, dbt pour transformations.
  • Ressources : Doc MinIO, DuckDB S3.
Découvrez nos formations Data Engineering Learni pour masterclass Iceberg/Databricks.