Guide technique

Kubernetes pour le SaaS : quand c'est le bon choix, quand ECS gagne, et ce qu'on a choisi

Kubernetes vs ECS vs Lambda pour les plateformes SaaS. Isolation multi-tenant, stratégies de déploiement, networking, optimisation des coûts, et le framework de décision honnête après avoir géré les trois en production.

26 février 202618 min de lectureÉquipe d'Ingénierie Oronts

La décision Kubernetes

Chaque équipe d'ingénierie finit par se poser la question : est-ce qu'on devrait utiliser Kubernetes ? La réponse honnête : ça dépend de ce que tu fais tourner, combien de services tu as, et si tu peux te permettre l'overhead opérationnel.

On fait tourner trois stratégies de compute différentes en production. Une plateforme tourne sur Kubernetes (7+ services, Pimcore, OpenSearch, workers). Une autre tourne sur ECS Fargate + Lambda (serverless-first, event-driven). Une troisième utilise un mix des deux. Chacune était le bon choix pour son contexte.

Cet article couvre le framework de décision et les patterns d'implémentation pour chaque approche. Pour comment on gère l'Infrastructure as Code derrière ces déploiements, consulte notre guide IaC. Pour les architectures applicatives qui tournent dessus, consulte notre guide d'architecture système.

La comparaison honnête

CritèreKubernetes (EKS/AKS/GKE)ECS FargateLambda
Complexité opérationnelleHaute (mises à jour de cluster, networking, RBAC)Moyenne (définitions de tâches, service mesh)Basse (déploie juste des fonctions)
Cold startAucun (les pods tournent en permanence)Aucun (les tasks tournent en permanence)100ms-5s (dépend du runtime/package)
Vitesse de scalingMinutes (scheduling de pods + scaling de nodes)Secondes (lancement de tasks)Millisecondes (invocations concurrentes)
Coût au reposÉlevé (minimum 2-3 nodes qui tournent toujours)Moyen (paiement par task en cours d'exécution)Zéro (paiement par invocation)
Coût en chargeBas (packing efficace, spot instances)Moyen (packing moins efficace)Peut être élevé (facturation par invocation)
Workloads statefulBon (PVCs, StatefulSets)Limité (EFS uniquement)Non supporté
Processus longsIllimitéIllimité15 min max
ÉcosystèmeÉnorme (Helm, operators, service mesh)Natif AWSNatif AWS
Multi-cloudOui (mêmes manifests, providers différents)AWS uniquementAWS uniquement
Compétences requisesÉlevées (expertise K8s nécessaire)Moyennes (connaissances AWS)Basses (juste écrire des fonctions)
Idéal pourSystèmes multi-services complexes, workloads statefulMicroservices simples, containers sans overhead K8sEvent-driven, endpoints API, tâches planifiées

La vraie ventilation des coûts

Pour une plateforme SaaS typique avec 5 services :

ComposantKubernetes (EKS)ECS FargateLambda + API Gateway
Compute (mensuel)~600 $ (3 nodes t3.large + pods)~450 $ (5 services, 0,5 vCPU chacun)~50-500 $ (dépend du trafic)
Control plane73 $/mois (frais EKS)GratuitGratuit
Load balancer25 $/mois (ALB)25 $/mois (ALB)Inclus dans API GW
Networking (NAT)45 $/mois45 $/mois45 $/mois
Monitoring50-200 $/mois50-200 $/mois50-200 $/mois
Total (trafic faible)~800-1 000 $/mois~570-720 $/mois~200-800 $/mois
Total (trafic élevé)~1 500-3 000 $/mois~2 000-4 000 $/mois~3 000-10 000 $/mois

Kubernetes est le moins cher en charge (bin-packing efficace, spot instances, capacité réservée). Lambda est le moins cher avec peu de trafic (tu ne paies rien au repos). ECS Fargate est le compromis.

Quand choisir Kubernetes

Choisis Kubernetes quand tu as :

Des systèmes multi-services complexes. Si tu fais tourner 7+ services avec des interdépendances, de la configuration partagée, du service discovery et des déploiements coordonnés, Kubernetes orchestre ça bien. Les containers Docker individuels sur ECS deviennent difficiles à gérer à cette échelle.

Des workloads stateful. Les bases de données, moteurs de recherche (OpenSearch, MeiliSearch), message brokers (RabbitMQ) et clusters de cache (Redis) bénéficient tous des StatefulSets, PersistentVolumeClaims et operators de Kubernetes. Les faire tourner sur ECS nécessite des services managés externes pour chaque composant stateful.

Des besoins multi-cloud. Les manifests Kubernetes fonctionnent sur n'importe quel fournisseur cloud. ECS et Lambda sont exclusifs à AWS. Si tu dois tourner sur AWS et Azure (ou pourrais en avoir besoin à l'avenir), Kubernetes est le choix portable.

Une équipe plateforme. Kubernetes nécessite une maintenance continue : mises à jour du cluster (tous les 3-4 mois pour les correctifs de sécurité), gestion des groupes de nodes, configuration réseau (ingress controllers, network policies) et gestion RBAC. Sans une personne ou une équipe dédiée pour gérer ça, l'overhead opérationnel ralentira toute l'organisation d'ingénierie.

Architecture Kubernetes pour une plateforme PIM/Commerce

┌─────────────────────────────────────────────────────────────┐
│                    Kubernetes Cluster                         │
│                                                              │
│  ┌─────────────┐  ┌─────────────┐  ┌─────────────────────┐ │
│  │ Ingress      │  │ Cert-Manager│  │ External DNS        │ │
│  │ (Nginx/Traefik)│ │ (Let's Encrypt)│ │ (Route53 sync)  │ │
│  └──────┬───────┘  └─────────────┘  └─────────────────────┘ │
│         │                                                     │
│  ┌──────▼──────────────────────────────────────────────────┐ │
│  │                    Namespaces                            │ │
│  │                                                          │ │
│  │  ┌──────────────────────────────────────────────────┐   │ │
│  │  │  production namespace                             │   │ │
│  │  │                                                    │   │ │
│  │  │  pimcore-web (2-4 réplicas)                       │   │ │
│  │  │  pimcore-worker (1-3 réplicas)                    │   │ │
│  │  │  pimcore-ops (1 réplica, maintenance)             │   │ │
│  │  │  frontend (2-3 réplicas)                          │   │ │
│  │  └──────────────────────────────────────────────────┘   │ │
│  │                                                          │ │
│  │  ┌──────────────────────────────────────────────────┐   │ │
│  │  │  data namespace                                   │   │ │
│  │  │                                                    │   │ │
│  │  │  mysql (StatefulSet, 1 réplica ou managé)         │   │ │
│  │  │  redis (StatefulSet, 1 réplica ou managé)         │   │ │
│  │  │  opensearch (StatefulSet, 2-3 réplicas)           │   │ │
│  │  │  rabbitmq (StatefulSet, 1-3 réplicas)             │   │ │
│  │  └──────────────────────────────────────────────────┘   │ │
│  │                                                          │ │
│  │  ┌──────────────────────────────────────────────────┐   │ │
│  │  │  flux-system namespace (contrôleur GitOps)        │   │ │
│  │  └──────────────────────────────────────────────────┘   │ │
│  └──────────────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────────┘

Stratégie de déploiement : GitOps avec Flux

On utilise Flux pour les déploiements basés sur GitOps. Le dépôt Git est la source unique de vérité. Flux synchronise l'état du cluster avec le dépôt toutes les minutes.

# flux-system/kustomization.yaml
apiVersion: kustomize.toolkit.fluxcd.io/v1
kind: Kustomization
metadata:
    name: platform
    namespace: flux-system
spec:
    interval: 1m
    sourceRef:
        kind: GitRepository
        name: infrastructure
    path: ./kubernetes/resources/overlay/prod
    prune: true  # Supprimer les ressources supprimées de Git
    healthChecks:
        - apiVersion: apps/v1
          kind: Deployment
          name: pimcore
          namespace: production

Les avantages par rapport à kubectl apply ou aux déploiements pilotés par CI :

  • Détection et correction de la dérive. Si quelqu'un modifie une ressource manuellement, Flux la restaure en moins d'1 minute.
  • Git comme piste d'audit. Chaque changement est un commit Git avec auteur, horodatage et diff.
  • Pas de credentials du cluster dans le CI. Flux tire depuis Git. Le CI pousse vers Git. Le pipeline CI n'a jamais besoin d'accès kubectl.
  • Le rollback, c'est git revert. Revert le commit, Flux synchronise, rollback terminé.

Kustomize pour les overlays d'environnement

kubernetes/resources/
├── base/
│   ├── deployments/
│   │   ├── pimcore.yaml
│   │   ├── frontend.yaml
│   │   └── worker.yaml
│   ├── services/
│   ├── configmaps/
│   └── kustomization.yaml
├── overlay/
│   ├── prod/
│   │   ├── patches/
│   │   │   ├── pimcore-replicas.yaml    # 4 réplicas
│   │   │   ├── resource-limits.yaml      # CPU/mémoire plus élevés
│   │   │   └── env-secrets.yaml          # Secrets de production
│   │   └── kustomization.yaml
│   ├── staging/
│   │   ├── patches/
│   │   │   ├── pimcore-replicas.yaml    # 1 réplica
│   │   │   └── resource-limits.yaml      # Limites plus basses
│   │   └── kustomization.yaml
│   └── dev/
│       └── kustomization.yaml

Les manifests de base définissent la structure commune. Les overlays patchent les différences spécifiques à chaque environnement (réplicas, limites de ressources, secrets, domaines). Même application, configuration différente par environnement.

Quand ECS Fargate gagne

On a choisi ECS Fargate + Lambda pour une plateforme commerce au lieu de Kubernetes. Les raisons :

Des opérations plus simples. Pas de mises à jour de cluster, pas de gestion de nodes, pas de configuration RBAC. ECS gère le scheduling, le scaling et les health checks. L'équipe se concentre sur le code applicatif, pas sur l'infrastructure.

Un scaling plus rapide. ECS Fargate lance de nouvelles tasks en quelques secondes. Kubernetes doit scheduler les pods, potentiellement attendre le scaling des nodes (minutes) et passer les health checks. Pour les pics de trafic, Fargate réagit plus vite.

Un meilleur coût pour les workloads variables. Paiement par task en cours d'exécution, pas par node. Si le trafic tombe à zéro la nuit, les coûts baissent proportionnellement. Les nodes Kubernetes continuent de tourner (et de coûter) quelle que soit la charge.

// Définition du service ECS (via CDK)
const service = new ecs.FargateService(this, 'ApiService', {
    cluster,
    taskDefinition,
    desiredCount: 2,
    assignPublicIp: false,
    vpcSubnets: { subnetType: ec2.SubnetType.PRIVATE_WITH_EGRESS },
    circuitBreaker: { rollback: true },  // Rollback automatique en cas d'échec de déploiement
    capacityProviderStrategies: [
        { capacityProvider: 'FARGATE_SPOT', weight: 2 },  // 66% spot
        { capacityProvider: 'FARGATE', weight: 1 },        // 33% on-demand
    ],
});

// Auto-scaling
const scaling = service.autoScaleTaskCount({ minCapacity: 2, maxCapacity: 10 });
scaling.scaleOnCpuUtilization('CpuScaling', { targetUtilizationPercent: 70 });
scaling.scaleOnRequestCount('RequestScaling', {
    targetGroup,
    requestsPerTarget: 1000,
});

Lambda pour les workloads event-driven

Les fonctions Lambda gèrent les workloads event-driven qui ne justifient pas un service persistant :

// Lambda pour le traitement des webhooks
const webhookHandler = new lambda.Function(this, 'WebhookHandler', {
    runtime: lambda.Runtime.NODEJS_20_X,
    handler: 'webhook.handler',
    timeout: cdk.Duration.seconds(30),
    memorySize: 256,
    environment: {
        TABLE_NAME: table.tableName,
        QUEUE_URL: queue.queueUrl,
    },
});

// API Gateway déclenche Lambda
const api = new apigateway.RestApi(this, 'WebhookApi');
api.root.addResource('webhook').addMethod('POST',
    new apigateway.LambdaIntegration(webhookHandler)
);

L'hybride : ECS + Lambda

L'architecture qu'on utilise le plus souvent pour les plateformes commerce :

ComposantTourne surPourquoi
Commerce API (Vendure)ECS FargateLong-running, sessions stateful
Service workerECS FargateConsommateur de file d'attente persistant
Handlers de webhookLambdaEvent-driven, trafic sporadique
Tâches planifiéesLambda + EventBridgeType cron, pas besoin de processus persistant
Traitement d'imagesLambdaCPU-intensif, parallélisable
Indexation de rechercheLambda + SQSEvent-driven, par rafales
Dashboard adminECS Fargate ou S3+CloudFrontAssets statiques ou SSR

L'API commerce et les workers tournent sur Fargate (persistant, long-running). Tout ce qui est event-driven tourne sur Lambda (paiement à l'usage, auto-scaling). La combinaison est moins chère que tout faire tourner sur Kubernetes et plus simple que tout faire tourner sur Lambda.

Isolation multi-tenant sur Kubernetes

Si tu fais tourner un SaaS multi-tenant sur Kubernetes, l'isolation des tenants nécessite une configuration explicite :

Isolation par namespace

# Network policy : les pods dans le namespace tenant-a ne peuvent communiquer qu'entre eux
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
    name: tenant-isolation
    namespace: tenant-a
spec:
    podSelector: {}
    policyTypes:
        - Ingress
        - Egress
    ingress:
        - from:
            - namespaceSelector:
                matchLabels:
                    tenant: tenant-a
    egress:
        - to:
            - namespaceSelector:
                matchLabels:
                    tenant: tenant-a
        - to:  # Autoriser la résolution DNS
            - namespaceSelector: {}
              podSelector:
                matchLabels:
                    k8s-app: kube-dns
            ports:
                - port: 53
                  protocol: UDP

Resource quotas

Empêche un tenant de consommer toutes les ressources du cluster :

apiVersion: v1
kind: ResourceQuota
metadata:
    name: tenant-a-quota
    namespace: tenant-a
spec:
    hard:
        requests.cpu: "4"          # Max 4 cœurs CPU
        requests.memory: "8Gi"     # Max 8 Go de RAM
        limits.cpu: "8"
        limits.memory: "16Gi"
        pods: "20"                 # Max 20 pods
        services: "10"
        persistentvolumeclaims: "5"

Le problème du voisin bruyant

Même avec des resource quotas, un workload I/O-intensif d'un tenant peut affecter les autres sur le même node. Les solutions :

StratégieNiveau d'isolationImpact sur le coût
Nodes partagés, resource quotasSouple (CPU/mémoire limités, I/O partagé)Le plus bas
Node affinity (pools de nodes dédiés)Moyen (nodes dédiés par tenant)Plus élevé
Clusters dédiésComplet (infrastructure complètement séparée)Le plus élevé

Pour la plupart des applications SaaS, les nodes partagés avec resource quotas suffisent. Réserve des pools de nodes dédiés pour les tenants enterprise avec des exigences strictes d'isolation. Pour les patterns d'isolation au niveau applicatif (middleware API, filtres de requêtes, policies), consulte notre guide de conception multi-tenant.

Optimisation des coûts

Spot instances (Kubernetes)

Les spot instances sont 60 à 90 % moins chères que le on-demand. Utilise-les pour les workloads stateless qui tolèrent l'interruption :

# EKS managed node group avec spot instances
apiVersion: eksctl.io/v1alpha5
kind: ClusterConfig
metadata:
    name: production
    region: eu-central-1
managedNodeGroups:
    - name: spot-workers
      instanceTypes: ["t3.large", "t3.xlarge", "m5.large"]
      spot: true
      minSize: 2
      maxSize: 10
      desiredCapacity: 3
      labels:
          node-type: spot
    - name: on-demand-workers
      instanceTypes: ["t3.large"]
      minSize: 1
      maxSize: 3
      desiredCapacity: 1
      labels:
          node-type: on-demand

Fais tourner les services stateless (serveurs web, workers) sur spot. Fais tourner les services stateful (bases de données, moteurs de recherche) sur on-demand. Utilise le pod anti-affinity pour répartir les réplicas sur les nodes afin qu'une interruption spot ne fasse pas tomber tous les réplicas.

Dimensionnement correct

La plupart des équipes sur-provisionnent. Un service demandant 1 CPU et 2 Go de RAM n'utilise en réalité peut-être que 0,2 CPU et 400 Mo. Le sur-provisionnement gaspille de l'argent. Le sous-provisionnement provoque des OOM kills.

# Vérifier l'utilisation réelle des ressources vs les demandes
kubectl top pods -n production
# Comparer avec les demandes de ressources dans les manifests de déploiement
kubectl get pods -n production -o jsonpath='{range .items[*]}{.metadata.name}{"\t"}{.spec.containers[0].resources.requests}{"\n"}{end}'

Utilise le Vertical Pod Autoscaler (VPA) en mode recommandation pour voir ce dont tes pods ont réellement besoin :

apiVersion: autoscaling.k8s.io/v1
kind: VerticalPodAutoscaler
metadata:
    name: pimcore-vpa
spec:
    targetRef:
        apiVersion: apps/v1
        kind: Deployment
        name: pimcore
    updatePolicy:
        updateMode: "Off"  # Recommandation uniquement, ne pas appliquer automatiquement

Autoscaling

Le Horizontal Pod Autoscaler (HPA) scale en fonction des métriques :

apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
    name: pimcore-hpa
spec:
    scaleTargetRef:
        apiVersion: apps/v1
        kind: Deployment
        name: pimcore
    minReplicas: 2
    maxReplicas: 8
    metrics:
        - type: Resource
          resource:
              name: cpu
              target:
                  type: Utilization
                  averageUtilization: 70
        - type: Resource
          resource:
              name: memory
              target:
                  type: Utilization
                  averageUtilization: 80
    behavior:
        scaleDown:
            stabilizationWindowSeconds: 300  # Attendre 5 min avant de réduire
            policies:
                - type: Pods
                  value: 1
                  periodSeconds: 60  # Retirer max 1 pod par minute

Les stabilizationWindowSeconds empêchent le flapping (scale up, scale down, scale up). Les scaleDown.policies empêchent un scale-down agressif qui pourrait causer des problèmes de capacité lors du prochain pic de trafic.

Le champ de mines du networking

Le networking Kubernetes, c'est là que la plupart des équipes restent bloquées.

Ingress controllers

ContrôleurIdéal pourComplexité
Nginx IngressUsage général, le plus répanduBasse
TraefikAuto-discovery, Let's Encrypt intégréBasse
AWS ALB IngressNatif AWS, intégration WAFMoyenne
Istio GatewayService mesh, mTLS, gestion du traficHaute

Pour la plupart des plateformes SaaS, Nginx Ingress + cert-manager (Let's Encrypt) suffit. Ajoute un service mesh (Istio, Linkerd) uniquement si tu as besoin de mTLS entre services, de routage de trafic avancé (canary deployments, traffic splitting), ou d'observabilité détaillée service-à-service.

Problèmes de résolution DNS

Un problème fréquent en production : les pods n'arrivent pas à résoudre les noms d'hôtes externes parce que la configuration DNS est incorrecte.

# Trouver la bonne IP du service DNS dans ton cluster
kubectl get svc -n kube-system kube-dns -o jsonpath='{.spec.clusterIP}'

# Si les configs nginx référencent un resolver, utilise cette IP
# Erreur courante : utiliser 10.0.0.10 alors que le DNS réel est à 10.2.0.10

Si ton sidecar nginx fait proxy des requêtes vers des services externes (stockage cloud, APIs externes), la directive resolver doit pointer vers l'IP kube-dns du cluster, pas vers une valeur codée en dur.

Erreurs courantes

  1. Choisir Kubernetes parce que "tout le monde l'utilise." Si tu as 3 services et une petite équipe, ECS Fargate est plus simple et moins cher. Kubernetes a du sens à partir de 7+ services avec une équipe plateforme.

  2. Pas de GitOps. kubectl apply depuis le portable d'un développeur, ce n'est pas une stratégie de déploiement. Utilise Flux ou ArgoCD pour des déploiements basés sur la réconciliation.

  3. Cluster partagé sans resource quotas. Un tenant ou un pod qui s'emballe consomme toutes les ressources. Chaque namespace a besoin de resource quotas.

  4. Tous les pods sur des instances on-demand. Les spot instances sont 60 à 90 % moins chères pour les workloads stateless. Utilise-les pour les serveurs web et les workers.

  5. Sur-provisionner les ressources. Des pods qui demandent 2 CPU et utilisent 0,2 CPU gaspillent de l'argent. Utilise les recommandations VPA pour dimensionner correctement.

  6. Autoscaling agressif. Réduire trop vite cause des problèmes de capacité au prochain pic. Utilise des fenêtres de stabilisation et des politiques de scale-down progressives.

  7. Pas de network policies. Sans elles, n'importe quel pod peut communiquer avec n'importe quel autre pod dans le cluster. Dans un setup multi-tenant, c'est un problème de sécurité.

  8. Ignorer les mises à jour du cluster. Les versions de Kubernetes arrivent en fin de vie tous les 12-15 mois. Planifie des fenêtres de mise à jour trimestrielles. Prendre du retard crée des vulnérabilités de sécurité et bloque les nouvelles fonctionnalités.

  9. Mélanger stateful et stateless sur les mêmes nodes. Un pod OpenSearch et un pod serveur web en compétition pour les I/O sur le même node dégrade les deux. Utilise la node affinity pour les séparer.

  10. Pas de sealed secrets. Commiter des secrets en clair dans Git, c'est une faille de sécurité en attente. Utilise Sealed Secrets, External Secrets Operator, ou AWS Secrets Manager.

Points clés à retenir

  • Kubernetes pour les plateformes multi-services complexes. 7+ services, workloads stateful, besoins multi-cloud, et une équipe capable de gérer l'overhead opérationnel.

  • ECS Fargate pour les workloads conteneurisés plus simples. Mêmes containers, moins de complexité opérationnelle. Mieux adapté aux équipes sans expertise Kubernetes.

  • Lambda pour les workloads event-driven. Webhooks, tâches planifiées, traitement d'images, et tout workload par rafales et de courte durée. Zéro coût au repos.

  • L'hybride (ECS + Lambda) est souvent la meilleure réponse. Services persistants sur Fargate, travail event-driven sur Lambda. Moins cher que tout sur Kubernetes, plus simple que tout sur Lambda.

  • GitOps avec Flux offre une vraie réconciliation. Pas juste de l'automatisation de déploiement. Détection de dérive, piste d'audit, et rollback via git revert.

  • Les spot instances économisent 60 à 90 % sur les workloads stateless. Fais tourner les serveurs web et workers sur spot. Les bases de données et moteurs de recherche sur on-demand.

  • L'isolation multi-tenant nécessite des network policies et des resource quotas. L'isolation par namespace seule ne suffit pas. Impose des frontières réseau et des limites de ressources par tenant.

On déploie et gère l'infrastructure Kubernetes, ECS et Lambda dans le cadre de nos services cloud. Si tu as besoin d'aide pour choisir une stratégie de compute ou optimiser ton déploiement actuel, parle à notre équipe ou demande un devis. Consulte aussi notre guide de migration Pimcore pour les patterns de déploiement Kubernetes spécifiques à Pimcore.

Sujets couverts

Kubernetes SaaSK8s productionKubernetes vs ECSECS Fargateingénierie de plateforme K8sKubernetes multi-tenantoptimisation coûts Kubernetes

Prêt à construire des systèmes IA prêts pour la production ?

Notre équipe est spécialisée dans les systèmes IA prêts pour la production. Discutons de comment nous pouvons aider.

Démarrer une conversation