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.
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ère | Kubernetes (EKS/AKS/GKE) | ECS Fargate | Lambda |
|---|---|---|---|
| Complexité opérationnelle | Haute (mises à jour de cluster, networking, RBAC) | Moyenne (définitions de tâches, service mesh) | Basse (déploie juste des fonctions) |
| Cold start | Aucun (les pods tournent en permanence) | Aucun (les tasks tournent en permanence) | 100ms-5s (dépend du runtime/package) |
| Vitesse de scaling | Minutes (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 charge | Bas (packing efficace, spot instances) | Moyen (packing moins efficace) | Peut être élevé (facturation par invocation) |
| Workloads stateful | Bon (PVCs, StatefulSets) | Limité (EFS uniquement) | Non supporté |
| Processus longs | Illimité | Illimité | 15 min max |
| Écosystème | Énorme (Helm, operators, service mesh) | Natif AWS | Natif AWS |
| Multi-cloud | Oui (mêmes manifests, providers différents) | AWS uniquement | AWS uniquement |
| Compétences requises | Élevées (expertise K8s nécessaire) | Moyennes (connaissances AWS) | Basses (juste écrire des fonctions) |
| Idéal pour | Systèmes multi-services complexes, workloads stateful | Microservices simples, containers sans overhead K8s | Event-driven, endpoints API, tâches planifiées |
La vraie ventilation des coûts
Pour une plateforme SaaS typique avec 5 services :
| Composant | Kubernetes (EKS) | ECS Fargate | Lambda + API Gateway |
|---|---|---|---|
| Compute (mensuel) | ~600 $ (3 nodes t3.large + pods) | ~450 $ (5 services, 0,5 vCPU chacun) | ~50-500 $ (dépend du trafic) |
| Control plane | 73 $/mois (frais EKS) | Gratuit | Gratuit |
| Load balancer | 25 $/mois (ALB) | 25 $/mois (ALB) | Inclus dans API GW |
| Networking (NAT) | 45 $/mois | 45 $/mois | 45 $/mois |
| Monitoring | 50-200 $/mois | 50-200 $/mois | 50-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 :
| Composant | Tourne sur | Pourquoi |
|---|---|---|
| Commerce API (Vendure) | ECS Fargate | Long-running, sessions stateful |
| Service worker | ECS Fargate | Consommateur de file d'attente persistant |
| Handlers de webhook | Lambda | Event-driven, trafic sporadique |
| Tâches planifiées | Lambda + EventBridge | Type cron, pas besoin de processus persistant |
| Traitement d'images | Lambda | CPU-intensif, parallélisable |
| Indexation de recherche | Lambda + SQS | Event-driven, par rafales |
| Dashboard admin | ECS Fargate ou S3+CloudFront | Assets 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égie | Niveau d'isolation | Impact sur le coût |
|---|---|---|
| Nodes partagés, resource quotas | Souple (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és | Complet (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ôleur | Idéal pour | Complexité |
|---|---|---|
| Nginx Ingress | Usage général, le plus répandu | Basse |
| Traefik | Auto-discovery, Let's Encrypt intégré | Basse |
| AWS ALB Ingress | Natif AWS, intégration WAF | Moyenne |
| Istio Gateway | Service mesh, mTLS, gestion du trafic | Haute |
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
-
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.
-
Pas de GitOps.
kubectl applydepuis 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. -
Cluster partagé sans resource quotas. Un tenant ou un pod qui s'emballe consomme toutes les ressources. Chaque namespace a besoin de resource quotas.
-
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.
-
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.
-
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.
-
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é.
-
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.
-
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.
-
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
Guides connexes
Guide Entreprise des Systèmes d'IA Agentiques
Guide technique des systemes d'IA agentiques en entreprise. Decouvre l'architecture, les capacites et les applications des agents IA autonomes.
Lire le guideCommerce Agentique : Comment laisser les agents IA acheter en toute securite
Comment concevoir un commerce agentique gouverne. Moteurs de politiques, portes d'approbation HITL, reçus HMAC, idempotence, isolation multi-tenant et le protocole Agentic Checkout complet.
Lire le guideLes 9 endroits où ton système IA laisse fuir des données (et comment colmater chacun)
Cartographie systématique de chaque point de fuite de données dans les systèmes IA. Prompts, embeddings, logs, appels d'outils, mémoire d'agent, messages d'erreur, cache, données de fine-tuning et transferts entre agents.
Lire le guidePrê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