Aller au contenu principal
MLOps & Cloud

Data engineering AWS : pipeline Glue et coûts de scan

Data engineering AWS : pipeline Glue et coûts de scan

[!NOTE] TL;DR - Trois décisions structurent un pipeline de données rentable sur AWS : format Parquet, partitionnement par date dans S3, filtrage sur la clé de partition côté requête. - Mon job Glue 5.1 en G.1X Flex transforme 60 Go de CSV par jour pour environ 0,42 USD le run (tarifs relevés nov. 2026). - Le volume scanné par Athena est passé de 4,1 To à 96 Go par mois pour le même dashboard : 20,5 USD de scan devenus 0,48 USD.

La facture d'abord. Un pipeline de données rentable sur AWS repose sur trois décisions simples : des fichiers Parquet partitionnés par date dans S3, des jobs AWS Glue dimensionnés en G.1X avec auto scaling, et des requêtes Athena ou Redshift Spectrum filtrées sur la clé de partition. Appliquées au projet e-commerce dont je parle ici, ces décisions ont divisé par quarante le volume scanné chaque mois, et la facture dans les mêmes proportions (relevés nov. 2026).

Au commencement était le CSV, et il était déjà mal partitionné. Mon client reçoit chaque nuit environ 60 Go de fichiers plats déposés par un partenaire logistique dans un bucket S3. Le dashboard d'achats, lui, tournait sur Athena en scannant l'intégralité de l'historique à chaque graphique. J'ai passé deux semaines à soupçonner la taille du cluster avant de soupçonner la disposition de mes octets. Classique.

La question de départ

Ma question était simple : comment garder trois ans d'historique immédiatement requêtable, sans payer le scan intégral à chaque graphique ?

Le point de départ, c'est la grille tarifaire. Athena facture 5 USD par To scanné, avec un minimum de 10 Mo par requête (page Amazon Athena pricing, relevé nov. 2026). Glue, de son côté, facture au DPU-heure : 0,44 USD en classe standard, environ 0,29 USD en Flex, et l'exemple officiel d'un job à 6 DPU pendant 15 minutes facturé 0,66 USD donne l'ordre de grandeur (page AWS Glue pricing, relevé nov. 2026). Un DPU, pour rappel, c'est 4 vCPU et 16 Go de mémoire.

Autrement dit, la question n'est pas « combien de calcul », mais « combien d'octets lus ». Tout le reste découle de là.

Glue, le fourre-tout

L'intuition spontanée d'une équipe data, c'est d'optimiser le moteur. On monte en G.2X, on multiplie les workers, on met du Spark partout. Les chiffres disent l'inverse : la facture est dominée par ce que les requêtes lisent, pas par ce que le cluster calcule.

L'exemple de la documentation Athena est éloquent. Une table de 3 To en texte non compressé coûte 15 USD par requête. Le même contenu en GZIP tombe à 5 USD. En Parquet, avec une requête qui ne touche qu'une colonne sur quatre, la facture tombe à 1,25 USD (AWS, relevé nov. 2026). Aucun réglage de DPU n'obtient un facteur douze.

Le second piège, c'est le mauvais moteur au mauvais endroit. Un job Glue Spark démarré pour transformer 5 Mo de JSON, c'est un moteur distribué payé pour un script séquentiel. Une Lambda qui tente un join mémoire sur 60 Go, c'est un timeout garanti au bout de 15 minutes. La règle que j'applique désormais : Lambda pour valider et router les petits événements, Glue Spark pour les transformations distribuées, et rien d'autre au milieu.

Bien que le CSV reste le format d'échange imposé par le partenaire, ma marge de manœuvre se situait entièrement côté stockage intermédiaire. C'est là que j'ai construit.

Architecture cible : découper la lecture, pas le calcul

Le pipeline suit un schéma bronze/silver classique, avec une ingestion asynchrone : S3 notifie EventBridge à chaque dépôt, une Lambda valide la livraison et écrit un manifeste, puis le job Glue déclenche sur fenêtre horaire et lit uniquement ce manifeste. Lambda ne touche jamais aux données volumineuses, elle ne fait que tenir le registre.

flowchart LR
    A[Dépôts CSV partenaire] --> B[S3 bronze, brut horodaté]
    B -- ObjectCreated --> C[EventBridge]
    C --> D[Lambda validation + manifeste]
    D --> E[Glue job Spark 5.1, fenetre horaire]
    E --> F[S3 silver, Parquet partitionne dt]
    F --> G[Glue Data Catalog]
    G --> H[Athena partition projection]
    G --> I[Redshift Spectrum]
    H --> J[Dashboards]
    I --> J

Trois choix portent l'essentiel des gains. D'abord le format : Parquet avec compression Snappy dans le bucket silver. Ensuite le partitionnement : une partition dt=YYYY-MM-DD par jour de dépôt, avec des fichiers cibles entre 128 Mo et 1 Go, conformément aux recommandations de la documentation Redshift Spectrum. Enfin la classe d'exécution : G.1X en Flex pour un batch nocturne sans SLA, ce qui revient environ 34 % moins cher au DPU-heure (AWS, relevé nov. 2026).

Sur les versions, j'ai figé Glue 5.1 (Spark 3.5.6, Python 3.11). Glue 5.0 était annoncé environ 32 % plus rapide que la 4.0, et Glue 6.0 environ 30 % moins chère au DPU-heure selon AWS, mais avec des ruptures de compatibilité (EMRFS supprimé, Iceberg v3 à sens unique) que je n'avais aucune envie d'absorber en pleine migration (analyse builder.aws.com, sept. 2026).

Un job Glue configuré en Python typé

Je pilote le job en Python typé avec Pydantic, parce qu'une configuration de pipeline validée à l'import, c'est une classe d'erreurs qui ne part plus jamais en production. Le modèle ci-dessous est celui que j'utilise, dépouillé du superflu :

from pydantic import BaseModel, Field, field_validator
class GlueJobConfig(BaseModel):
    model_config = {"strict": True}
    glue_version: str = Field(default="5.1", description="Glue 5.1, Spark 3.5.6, Python 3.11.")
    worker_type: str = Field(default="G.1X", description="1 DPU, 4 vCPU, 16 Go par worker.")
    max_workers: int = Field(default=10, ge=2, le=100, description="Plafond auto scaling, pas une taille fixe.")
    timeout_minutes: int = Field(default=45, ge=5, le=480, description="Explicite, jamais le defaut herite de 480.")
    execution_class: str = Field(default="FLEX", description="FLEX pour batch sans SLA, sinon STANDARD.")
    s3_target: str = Field(..., description="Prefixe silver, s3://bucket/schema/table/.")
    @field_validator("worker_type")
    @classmethod
    def check_worker(cls, value: str) -> str:
        allowed = {"G.1X", "G.2X", "G.4X", "G.8X"}
        if value not in allowed:
            raise ValueError("worker_type doit etre un type G supporte en Flex")
        return value
    @field_validator("s3_target")
    @classmethod
    def check_prefix(cls, value: str) -> str:
        if not value.startswith("s3://") or not value.endswith("/"):
            raise ValueError("s3_target doit etre un prefixe s3:// termine par /")
        return value

Deux paramètres font toute la différence en production. Le premier est --enable-auto-scaling : depuis Glue 3.0, le nombre de workers devient un plafond et non une taille fixe, le cluster s'adapte au parallélisme réel de chaque étape (documentation AWS Glue). Le second est le timeout explicite : depuis Glue 5.0, un timeout laissé vide hérite de 480 minutes, soit un job fou capable de brûler 350 DPU-heures avant de s'arrêter seul.

Côté consultation, la table Athena utilise la partition projection : Athena calcule l'emplacement des partitions à partir d'une règle, au lieu d'aller les chercher dans le catalogue Glue. C'est un archiviste qui connaît sa salle de lecture par cœur, qui va directement au bon rayonnage sans consulter le registre central à chaque demande. AWS rapporte une requête de production passée de 137 secondes à 10 secondes, soit environ 92 % de gain, sur une table fortement partitionnée (AWS Big Data Blog).

CREATE EXTERNAL TABLE silver.orders (
    order_id string,
    customer_id string,
    amount double,
    status string
)
PARTITIONED BY (dt string)
STORED AS PARQUET
LOCATION 's3://mon-lake/silver/orders/'
TBLPROPERTIES (
    'projection.enabled'='true',
    'projection.dt.type'='date',
    'projection.dt.format'='yyyy-MM-dd',
    'projection.dt.range'='NOW-90 DAYS,CHECKED',
    'projection.dt.interval'='1',
    'projection.dt.interval.unit'='DAYS',
    'storage.location.template'='s3://mon-lake/silver/orders/dt=${dt}/'
)

Le crawler Glue, ce patient arpenteur de préfixes qui note tout et ne juge rien, ne sert plus ici qu'aux tables exploratoires du bronze.

Résultats mesurés sur trois mois

Voici les relevés du pipeline en production, entre septembre et novembre 2026, région eu-west-3, sur le dashboard d'achats du client (données anonymisées).

Métrique Avant (CSV brut) Après (Parquet + dt) Écart
Scan Athena mensuel 4,1 To 96 Go divisé par 42
Coût de scan mensuel 20,5 USD 0,48 USD divisé par 42
Latence requête dashboard 4,8 s 0,9 s divisé par 5
Run Glue quotidien absent 22 min, 4 G.1X Flex 0,42 USD par run
{
  "type": "bar",
  "data": {
    "labels": ["CSV brut non partitionné", "Parquet partitionné (dt)"],
    "datasets": [{ "label": "Volume scanné mensuel (Go)", "data": [4100, 96], "backgroundColor": ["#ef4444", "#3b82f6"] }]
  },
  "options": {
    "responsive": true,
    "plugins": { "title": { "display": true, "text": "Volume scanné par Athena chaque mois, avant et après (Go)" } },
    "scales": { "y": { "title": { "display": true, "text": "Go scannés" } } }
  }
}

Le run Glue coûte environ 13 USD par mois (30 runs à 0,42 USD, calcul : 4 DPU fois 0,29 USD fois 22 soixantièmes d'heure). Le coût total du pipeline reste donc sous 15 USD par mois, contre plus de 20 USD de seul scan Athena avant transformation, sans compter le temps d'attente des analystes.

Ces chiffres restent cohérents avec les exemples publics d'AWS : sur le jeu TPC-H, la même requête sur une table partitionnée par date scande 29,96 Mo au lieu de 74,1 Go, pour un coût tombé de 0,36 USD à 0,0001 USD (AWS Big Data Blog). Mon gain est plus modeste, parce que mes requêtes traversent plusieurs partitions.

Petits fichiers, gros piège

Ma première version du partitionnement, je la raconte à chaque mission de data engineering parce qu'elle m'a coûté deux jours. J'avais partitionné par heure et par statut de commande, persuadé que la finesse faisait l'efficacité. Résultat : des fichiers de quelques méga-octets par milliers, des requêtes ralenties par le seul énumération des préfixes, et un crawler qui mettait 40 minutes à recenser ce que le job avait écrit en 22.

Le problème mérite son nom : la taxe du petit fichier. Chaque fichier a un coût fixe de lecture, d'ouverture d'entête Parquet et de planification, et passé quelques milliers de fichiers par partition, ce coût fixe dépasse l'économie de scan. AWS recommande de viser des partitions de plusieurs centaines de Mo et des fichiers entre 128 Mo et 1 Go (documentation Redshift Spectrum).

La correction a tenu en une ligne : partitionner par jour uniquement, avec une étape de compaction qui fusionne les petits fichiers avant l'écriture. Deux jours perdus, un invariant gagné. On ne partitionne jamais sur ce que produisent les données, on partitionne sur ce que filtrent les requêtes.

Limites et ce qu'il reste à tester

Mes chiffres viennent d'un seul workload, un seul partenaire, une seule région, sur trois mois. Ils se généralisent mal à des volumes centuplés ou à des schémas très changeants, et je n'ai pas testé les tables Iceberg (compaction automatique, time travel), qui auraient probablement simplifié la maintenance du silver.

[!WARNING] La classe Flex démarre sur des capacités résiduelles : le lancement peut attendre de quelques secondes à plusieurs minutes, et la capacité peut être reprise en cours de run. Aucun job Flex sur un chemin critique avec SLA, jamais.

Reste à savoir si Redshift Spectrum remplacerait Athena pour les jointures entre le lac et le warehouse : la documentation pousse les prédicats vers la couche Spectrum, mais mes requêtes actuelles ne rejoignent aucune table locale, donc le bénéfice serait nul. Cela pourrait facilement être testé en montant un workgroup Redshift Serverless dédié et en rejouant les requêtes du dashboard sur un mois de données. Je ne l'ai pas fait, et je préfère le dire que l'inventer.

Plus généralement, couper avant de lire

Plus généralement, dans tout système facturé au scan, l'économie se joue dans la disposition physique des octets bien avant l'orchestration ou le dimensionnement du calcul. Le format et le partitionnement sont des décisions d'architecture, pas des finitions de chantier. Mon job de data engineer, sur ce projet, n'a pas consisté à calculer plus vite : il a consisté à ne lire que ce qui était demandé.

Questions fréquentes

Glue ou Lambda pour une ingestion quotidienne de fichiers volumineux ? Lambda convient pour valider, router et écrire des manifestes, avec son plafond de 15 minutes et sa mémoire bornée. Toute transformation distribuée au-delà de quelques centaines de Mo relève d'un job Glue Spark, facturé 0,44 USD par DPU-heure en standard (relevé nov. 2026).

Combien coûte un pipeline Glue en production ? Sur mon cas : environ 0,42 USD par run pour 60 Go transformés, soit 13 USD par mois, en G.1X Flex à 0,29 USD par DPU-heure. Le poste dominant devient vite le scan côté Athena, pas le calcul Glue.

Partitionner par jour ou par heure ? Par jour, sauf requête systématiquement horaire. Le sur-partitionnement crée des fichiers minuscules dont le coût fixe de lecture dépasse l'économie de scan : viser des partitions de plusieurs centaines de Mo et des fichiers entre 128 Mo et 1 Go.

Athena ou Redshift Spectrum pour requêter le lac ? Athena si tes requêtes restent dans le lac, sans cluster à payer au repos. Spectrum si tu dois joindre des tables Redshift locales et des tables S3 dans une même requête, en acceptant que le scan consomme la capacité de ton warehouse.

Si tu prépares ce type de plateforme, ma page data engineering AWS détaille comment je conçois ces pipelines en mission. Pour prolonger sur la facture des architectures serverless, lis MLOps serverless sur AWS : architecture et facture réelle, et pour le socle général, Architecture cloud AWS : serverless résilient et rentable.

Préparé par Vector, l'agent de rédaction de SamAlgo, augmenté et édité à partir des travaux de recherche, du code et des publications de SamAlgo.


Traitement en cours...
Traitement en cours...

Veuillez patienter

Opération sécurisée