Docker Compose vs Kubernetes : différences, même app, quand basculer
À la fin de ce tutoriel, vous saurez comparer Docker Compose et Kubernetes sur la même app (3 services), mapper chaque concept Compose vers les objets Kubernetes, migrer via Kompose ou Helm/Kustomize, et décider quand rester sur Compose, quand basculer, ou utiliser les deux.
Niveau : Intermédiaire · Temps estimé : 45–60 min · Versions testées : Docker Engine 27+ · Compose v2 (plugin) · Kubernetes 1.31+ · Dernière vérification : 2026-09-11
Prérequis
- Docker Compose v2 :
compose.yamlsans cléversion:, plugindocker compose - Bases Kubernetes : Pods, Deployments, Services (tutoriel Kubernetes débutant)
- Docker Engine 27+ (
docker compose versionOK) ; cluster local (kind/minikube) +kubectlpour la partie K8s - Terminal Linux / WSL2 / macOS · Coût : 0 € (lab local)
Réécriture FR originale 2026 de la page EN #4253 : même intention, contenu enrichi (mapping, pièges, FAQ, quiz).
Ce que nous allons construire
Lab « docker-compose-vs-kubernetes »
├── Rôles : Compose (1 hôte) vs Kubernetes (cluster)
├── Tableau comparatif « en un coup d’œil »
├── App 3 services : API + PostgreSQL + Redis (Compose)
├── Équivalent K8s : Deployment, StatefulSet, Service, ConfigMap, Secret
├── Mapping Compose → objets Kubernetes
├── Migration : Kompose · Helm/Kustomize · Compose pour le dev
└── Guide de décision + FAQ + quiz
(Schéma à remplacer par une image locale Excalidraw / draw.io, alt : « Même app API + Postgres + Redis : à gauche compose.yaml sur un hôte Docker ; à droite Deployments/StatefulSet/Services sur un cluster Kubernetes ».)
Compose et Kubernetes ne résolvent pas le même problème : souvent Compose en dev/CI, Kubernetes en prod.
Rôles : portée de chaque outil
Docker Compose : app multi-conteneurs sur une machine. Un YAML, docker compose up, démarrage rapide — dev local, CI, petit mono-serveur.
Kubernetes orchestre un cluster : self-healing, HPA, rolling updates, CNI, stockage dynamique. Courbe et charge ops plus élevées — ou service managé (EKS, GKE, AKS).
La question n’est pas « lequel est meilleur ? », mais quel problème — et comment les deux coexistent.
Tableau : en un coup d’œil
| Critère | Docker Compose | Kubernetes |
|---|---|---|
| Portée | Un hôte (Docker Engine) | Cluster de nœuds |
| Fichier | compose.yaml (~50 lignes) |
Manifests (Deployment, Service, ConfigMap, Secret, Ingress, PVC…) ou Helm |
| Self-healing | restart: sur le même hôte |
Reschedule sur nœuds sains ; remplace pods unhealthy |
| Scaling | --scale sur une machine |
HPA + Cluster Autoscaler |
| Rolling updates | Recreate (bref downtime) | RollingUpdate + readiness, zéro downtime |
| Réseau | Bridge, nom = DNS | CNI, Services, Ingress / Gateway API |
| Config / secrets | env, fichiers secrets |
ConfigMaps, Secrets, opérateurs externes |
| Stockage | Volumes nommés, bind mounts | PVC + StorageClasses |
| Courbe | Heures | Semaines |
| Charge ops | Quasi nulle | Upgrades, RBAC, observabilité (ou managé) |
| Usage typique | Dev, CI, petit serveur | Prod, microservices, multi-équipes |
App échantillon : 3 services (Compose)
API (ghcr.io/example/api:1.4.0, port 8000) + PostgreSQL (volume) + Redis. Mot de passe DB via secret ; feature flag en config. L’image API est un placeholder : tout HTTP exposant /healthz convient.
compose.yaml moderne (sans version:)
services:
api:
image: ghcr.io/example/api:1.4.0
ports: ["8080:8000"]
environment:
DATABASE_URL: postgres://app@db:5432/app
REDIS_URL: redis://cache:6379
FEATURE_NEW_CHECKOUT: "true"
secrets: [db_password]
depends_on:
db: { condition: service_healthy }
cache: { condition: service_started }
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:8000/healthz"]
interval: 10s
retries: 3
deploy:
resources:
limits: { memory: 256M }
restart: unless-stopped
db:
image: postgres:17-alpine
environment:
POSTGRES_USER: app
POSTGRES_DB: app
POSTGRES_PASSWORD_FILE: /run/secrets/db_password
secrets: [db_password]
volumes: [pgdata:/var/lib/postgresql/data]
healthcheck:
test: ["CMD-SHELL", "pg_isready -U app"]
interval: 5s
retries: 10
cache:
image: redis:7.4-alpine
command: ["redis-server", "--maxmemory", "64mb", "--maxmemory-policy", "allkeys-lru"]
volumes:
pgdata:
secrets:
db_password:
file: ./secrets/db_password.txt # .gitignore — ne jamais committer
mkdir -p secrets && openssl rand -base64 24 > secrets/db_password.txt
docker compose up -d && docker compose ps
curl -s localhost:8080/healthz
docker compose up -d --scale api=3 # port hôte : un seul mapping
docker compose pull && docker compose up -d
docker compose down # -v pour effacer les volumes
~50 lignes, une commande, tout sur le laptop. Limites : scale API sans reverse proxy (un seul conteneur possède 8080) ; update = stop/start.
Équivalent Kubernetes
Même app, plusieurs objets. Postgres en StatefulSet (+ Service headless + PVC) ou base managée ; Redis en Deployment + Service ; API en Deployment + Service (+ Ingress).
# k8s/api.yaml — ConfigMap + Deployment + Service (extrait)
apiVersion: v1
kind: ConfigMap
metadata: { name: api-config }
data:
DATABASE_URL: postgres://app@db:5432/app
REDIS_URL: redis://cache:6379
FEATURE_NEW_CHECKOUT: "true"
---
apiVersion: apps/v1
kind: Deployment
metadata: { name: api }
spec:
replicas: 3
selector: { matchLabels: { app: api } }
strategy:
rollingUpdate: { maxSurge: 1, maxUnavailable: 0 }
template:
metadata: { labels: { app: api } }
spec:
initContainers:
- name: wait-for-db
image: postgres:17-alpine
command: ["sh", "-c", "until pg_isready -h db -U app; do sleep 2; done"]
containers:
- name: api
image: ghcr.io/example/api:1.4.0
ports: [{ containerPort: 8000 }]
envFrom: [{ configMapRef: { name: api-config } }]
env:
- name: DB_PASSWORD
valueFrom: { secretKeyRef: { name: db-credentials, key: password } }
readinessProbe:
httpGet: { path: /healthz, port: 8000 }
periodSeconds: 5
livenessProbe:
httpGet: { path: /healthz, port: 8000 }
periodSeconds: 10
resources:
requests: { cpu: 100m, memory: 128Mi }
limits: { memory: 256Mi }
---
apiVersion: v1
kind: Service
metadata: { name: api }
spec:
selector: { app: api }
ports: [{ port: 80, targetPort: 8000 }]
kubectl create namespace shop
kubectl -n shop create secret generic db-credentials
--from-literal=password="$(openssl rand -base64 24)"
kubectl -n shop apply -f k8s/db.yaml -f k8s/api.yaml
kubectl -n shop port-forward svc/api 8080:80 &
curl -s localhost:8080/healthz
kubectl -n shop scale deployment/api --replicas=6
kubectl -n shop set image deployment/api api=ghcr.io/example/api:1.5.0
kubectl -n shop rollout undo deployment/api
kubectl -n shop autoscale deployment/api --min=3 --max=20 --cpu-percent=70
~150 lignes : 3 réplicas, update sans perte de requête, remplacement auto si un nœud tombe, autoscaling CPU.
Mapping concepts Compose → objets Kubernetes
| Compose | Kubernetes |
|---|---|
services.api (stateless) |
Deployment + Service |
services.db (stateful) |
StatefulSet + Service headless + PVC (ou DB managée) |
ports: "8080:8000" |
Service ClusterIP + Ingress / Gateway |
environment |
env ou ConfigMap (envFrom) |
secrets |
Secret (fichier ou env) |
volumes: pgdata |
PVC + StorageClass |
healthcheck |
livenessProbe + readinessProbe |
deploy.resources |
resources.requests / limits |
depends_on |
Pas d’équivalent : probes + init + retries app |
restart |
Toujours, via le controller |
| Nom service = DNS | <svc>.<ns>.svc.cluster.local |
Migration : de Compose vers Kubernetes
Option A — Kompose (premier jet)
kompose convert -f compose.yaml -o k8s/
kompose convert -f compose.yaml --chart -o chart/
Manifests valides rapidement, mais point de départ seulement : Deployments là où il faudrait un StatefulSet, depends_on ignoré, pas de probes/requests inventés.
Option B — Helm ou Kustomize à la main
Pour la prod, écrivez les manifests, puis paramétrez avec Helm ou Kustomize. Préférez les services managés pour le stateful (RDS, ElastiCache) si possible.
Garder Compose pour le développement
Migrer la prod ne veut pas dire abandonner Compose. Conservez compose.yaml pour le local et la CI. Tilt, Skaffold ou DevSpace offrent une boucle contre un cluster local si vous visez la parité.
Approfondir la migration (HowTo)
Convertissez par couche : Deployments/Services API+Redis (stateless) d’abord — probes et resources.requests — puis StatefulSet Postgres ou base managée (RDS). Clarifie le remplacement de depends_on, volumes et réseaux Compose.
En local, gardez compose.yaml v2 ; les mêmes images circulent vers kind/minikube. Sans équipe plateforme : Swarm ou k3s avant un EKS — Kubernetes débutant, Deployment.
Helm : factorisez replicaCount / image.tag / resources. Kustomize convient souvent mieux (overlays dev/prod). Documentez le mapping Compose → objets dans le README.
Terrain du milieu
- Docker Swarm : Compose +
deploy:multi-hôtes, rolling updates. Courbe minimale ; écosystème plus petit. - k3s : Kubernetes ~70 Mo ; 1–5 serveurs sans gros setup.
- Plateformes managées (ECS/Fargate, Container Apps, Cloud Run) : scaling/updates sans exposer K8s.
- Compose en CI : utile pour les tests d’intégration même si la prod est sur Kubernetes.
Guide de décision
| Situation | Choix |
|---|---|
| Un serveur, downtime OK, petite équipe, pas d’autoscaling | Compose seul |
| Multi-machines, zero-downtime, recovery nœud, autoscaling, multi-équipes ou K8s déjà payé | Basculer vers Kubernetes |
| Plus d’un hôte, pas d’équipe plateforme | Swarm, k3s ou conteneur managé |
| Prod K8s + itération locale/CI rapide | Les deux (Compose en dev, mêmes images) |
Quoi que vous choisissiez en production, gardez Compose pour le local.
Critères décision (checklist 2026)
- Hôtes / SLO : un VPS → Compose ; multi-nœuds + zero-downtime → Kubernetes (ou managé).
- Ops : pas d’astreinte plateforme → Swarm/k3s/managé avant un cluster vanilla.
- Stateful : StatefulSet + backups/PVC, sinon RDS/équivalent.
- Parité images : Compose local reste le plus rapide pour onboarding — Docker débutant.
- Coût caché : control plane, CNI, observabilité (Prometheus/Grafana) > YAML initial.
Erreurs fréquentes
| Piège | Cause | Correction |
|---|---|---|
| Crash-loop au démarrage K8s | Ordre depends_on |
Retries app + readiness ; init en convenance |
| Mauvais placement pods | Pas de resources.requests |
Toujours déclarer requests (+ limits mémoire) |
| DB instable / données perdues | Postgres en Deployment | StatefulSet + PVC, ou DB managée |
| Secrets dans ConfigMap / image | Mauvais objet | Secret + gestionnaire externe |
| Tout en NodePort | Exposition brute | Ingress ou Gateway API |
| Kompose tel quel en prod | Jet non revu | Revoir StatefulSet, probes, PSA, resources |
2026 : Pod Security Admission (PSA), pas PSP ; images composants via registry.k8s.io.
FAQ
Compose remplace-t-il Kubernetes ?
Non. Portées différentes, souvent complémentaires (Compose = 1 hôte ; K8s = cluster).
Abandonner Compose en passant en prod K8s ?
Non. Gardez-le pour le dev et la CI ; les images restent les mêmes.
Kompose suffit-il pour la production ?
Non. Premier jet seulement ; peaufinez puis Helm/Kustomize.
StatefulSet ou base managée pour Postgres ?
Lab : StatefulSet + PVC. Prod : base managée (RDS, Cloud SQL…).
Compose v2 change-t-il le débat vs Kubernetes ?
Non. docker compose et la fin de version: modernisent le fichier ; la portée reste un hôte. K8s reste l’orchestrateur cluster — Compose v2.
Apprendre Swarm avant Kubernetes ?
Utile pour multi-hôtes sans équipe plateforme. Swarm est plus simple ; le marché favorise K8s — Swarm.
Secrets Compose → K8s ?
Lab Compose : .env gitignoré. K8s : Secret + (prod) gestionnaire externe ; jamais en ConfigMap ni dans l’image.
CI : Compose ou kind ?
Compose pour intégration API+DB (rapide). kind/minikube pour probes/Ingress/policies. Souvent Compose en CI courante, job K8s sur main.
Quiz (5 questions)
1. Quelle affirmation décrit le mieux Compose vs Kubernetes ?
– A. Compose gère un cluster multi-région ; Kubernetes est réservé au laptop
– B. Compose décrit une stack sur un hôte ; Kubernetes orchestre un cluster (self-healing, scaling)
– C. Les deux font exactement la même chose
2. Quel objet remplace typiquement un service Compose stateful (PostgreSQL) ?
– A. Un Job unique
– B. Un StatefulSet + Service headless + PVC (ou une base managée)
– C. Un ConfigMap seul
3. Bonne posture après une conversion Kompose ?
– A. Déployer tels quels en production
– B. Traiter comme un premier jet, puis corriger StatefulSets, probes et resources
– C. Supprimer Compose du dépôt
4. Pourquoi déclarer resources.requests sur les pods ?
– A. C’est purement cosmétique
– B. Pour le scheduling et éviter la contention silencieuse sur le nœud
– C. Pour remplacer Ingress
5. Bonne pratique secrets Compose → Kubernetes ?
– A. Mettre les mots de passe dans un ConfigMap partagé
– B. Secret K8s (+ gestionnaire externe en prod), jamais dans l’image
– C. Committer le .env pour la parité
Réponses : 1‑B · 2‑B · 3‑B · 4‑B · 5‑B
Pour aller plus loin
- Docker Compose v2 · Swarm cluster & services
- Deployment · Services · Helm
- Tutoriel Docker débutant · Tutoriel Kubernetes débutant
Maillage série (P3)
| ← Précédent | Docker Compose v2 (docker-compose-v2) |
| → Latéral | Docker Swarm cluster & services (docker-swarm-cluster-services) |
| Aussi | Deployment · Services · Helm · Tutoriel Docker débutant · Tutoriel Kubernetes débutant |
Retour parcours Kubernetes — hub de la série et leçons sœurs.


