Compose et Kubernetes peuvent tous les deux exécuter les mêmes trois processus : une interface web, une API et Postgres. Compose est un projet sur un seul moteur. Kubernetes est un ensemble d’objets API qu’un plan de contrôle peut réorganiser. Ci-dessous se trouve un compose.yaml, les manifests qui le correspondent, et le moment où une équipe devrait changer d’outil.

Réponse courte

Choisissez Compose quand perdre la machine signifie que l’application est down, et que c’est acceptable. Choisissez Kubernetes quand un nœud peut disparaître et le Pod devrait démarrer ailleurs, ou quand vous exploitez déjà un cluster. Les deux outils lancent les conteneurs web, API et base de données. Seul Kubernetes a un scheduler, un Service qui suit les Pods prêts, et un historique de déploiement.

Les noms de service Compose sont des noms DNS sur le réseau projet. Les noms de Service Kubernetes sont des noms DNS à l’intérieur du namespace (api et api.demo.svc.cluster.local). http://api:3000 fonctionne dans les deux fichiers si le Service s’appelle api et écoute le port 3000.

Côte à côte

Élément compose.yaml Kubernetes
Processus web, API, base de données services.web, services.api, services.postgres Déploiement + modèle Pod
Port UI publié ports: ["8080:80"] sur web Service ClusterIP, puis kubectl port-forward ou un Ingress
Adresse API http://api:3000 Service api port 3000
Paramètres non-secrets environment ConfigMap app-config
Mot de passe variable .env Secret app-secret
Disque de base de données volume nommé pgdata PersistentVolumeClaim pgdata
Ordre de démarrage depends_on avec service_healthy readinessProbe ; les clients réessaient
Vérification de santé healthcheck à l’intérieur du conteneur readinessProbe et livenessProbe, exécutées par le kubelet
Redémarrage restart: unless-stopped Le Déploiement recrée le Pod (restartPolicy: Always)
Copies supplémentaires de l’API docker compose up --scale api=3 spec.replicas
Build docker build, puis un tag d’image même référence d’image ; le cluster ne construit pas

Le fichier Compose

change-me se substitue à un mot de passe. Mettez-le dans un fichier .env à côté du fichier Compose et laissez ce fichier en dehors de git. Compose lit .env automatiquement. Les détails de commande se trouvent dans la feuille de référence Docker Compose.

services:
  web:
    image: example/web:1.4.0
    ports:
      - "8080:80"
    environment:
      API_URL: http://api:3000
    depends_on:
      api:
        condition: service_healthy
    restart: unless-stopped
  api:
    image: example/api:1.4.0
    environment:
      DATABASE_URL: postgres://app:${POSTGRES_PASSWORD}@postgres:5432/app
    depends_on:
      postgres:
        condition: service_healthy
    healthcheck:
      test: ["CMD", "wget", "-qO-", "http://127.0.0.1:3000/health"]
      interval: 10s
      timeout: 3s
      retries: 5
      start_period: 10s
    restart: unless-stopped
  postgres:
    image: postgres:16
    environment:
      POSTGRES_USER: app
      POSTGRES_PASSWORD: ${POSTGRES_PASSWORD}
      POSTGRES_DB: app
    volumes:
      - pgdata:/var/lib/postgresql/data
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U app -d app"]
      interval: 5s
      timeout: 3s
      retries: 10
    restart: unless-stopped
volumes:
  pgdata:

Cette vérification wget s’exécute à l’intérieur de l’image, donc le binaire doit y exister. Construisez des tags avec la feuille de référence Dockerfile.

# --wait retourne un code non-zéro si une vérification de santé n'est jamais réussie
docker compose up -d --wait
docker compose ps
docker compose down -v

down -v supprime le volume pgdata. Utilisez-le en CI. Ne l’utilisez pas sur une base de données que vous aviez l’intention de conserver.

Les manifests Kubernetes

Enregistrez ceci sous shop.yaml et appliquez-le avec kubectl. La valeur du Secret doit correspondre à POSTGRES_PASSWORD ou l’API ne peut pas se connecter. Sur un cluster partagé, créez ce Secret en dehors du dépôt git et supprimez le bloc stringData.

apiVersion: v1
kind: Namespace
metadata:
  name: demo
---
apiVersion: v1
kind: ConfigMap
metadata:
  name: app-config
  namespace: demo
data:
  API_URL: "http://api:3000"
  POSTGRES_USER: app
  POSTGRES_DB: app
---
apiVersion: v1
kind: Secret
metadata:
  name: app-secret
  namespace: demo
type: Opaque
stringData:
  POSTGRES_PASSWORD: change-me
  DATABASE_URL: postgres://app:change-me@postgres:5432/app
---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: pgdata
  namespace: demo
spec:
  accessModes: ["ReadWriteOnce"]
  resources:
    requests:
      storage: 5Gi
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: postgres
  namespace: demo
spec:
  replicas: 1
  selector:
    matchLabels:
      app: postgres
  template:
    metadata:
      labels:
        app: postgres
    spec:
      securityContext:
        fsGroup: 999
      containers:
        - name: postgres
          image: postgres:16
          ports:
            - containerPort: 5432
          env:
            - name: POSTGRES_USER
              valueFrom:
                configMapKeyRef: {name: app-config, key: POSTGRES_USER}
            - name: POSTGRES_DB
              valueFrom:
                configMapKeyRef: {name: app-config, key: POSTGRES_DB}
            - name: POSTGRES_PASSWORD
              valueFrom:
                secretKeyRef: {name: app-secret, key: POSTGRES_PASSWORD}
          readinessProbe:
            exec:
              command: ["pg_isready", "-U", "app", "-d", "app"]
            periodSeconds: 5
          livenessProbe:
            exec:
              command: ["pg_isready", "-U", "app", "-d", "app"]
            initialDelaySeconds: 15
            periodSeconds: 10
          volumeMounts:
            - name: data
              mountPath: /var/lib/postgresql/data
      volumes:
        - name: data
          persistentVolumeClaim:
            claimName: pgdata
---
apiVersion: v1
kind: Service
metadata:
  name: postgres
  namespace: demo
spec:
  selector:
    app: postgres
  ports:
    - port: 5432
      targetPort: 5432
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: api
  namespace: demo
spec:
  replicas: 1
  selector:
    matchLabels:
      app: api
  template:
    metadata:
      labels:
        app: api
    spec:
      containers:
        - name: api
          image: example/api:1.4.0
          imagePullPolicy: IfNotPresent
          ports:
            - containerPort: 3000
          env:
            - name: DATABASE_URL
              valueFrom:
                secretKeyRef: {name: app-secret, key: DATABASE_URL}
          readinessProbe:
            httpGet: {path: /health, port: 3000}
            periodSeconds: 10
          resources:
            requests: {cpu: "100m", memory: 128Mi}
            limits: {memory: 256Mi}
---
apiVersion: v1
kind: Service
metadata:
  name: api
  namespace: demo
spec:
  selector:
    app: api
  ports:
    - port: 3000
      targetPort: 3000
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: web
  namespace: demo
spec:
  replicas: 1
  selector:
    matchLabels:
      app: web
  template:
    metadata:
      labels:
        app: web
    spec:
      containers:
        - name: web
          image: example/web:1.4.0
          imagePullPolicy: IfNotPresent
          ports:
            - containerPort: 80
          env:
            - name: API_URL
              valueFrom:
                configMapKeyRef: {name: app-config, key: API_URL}
          readinessProbe:
            httpGet: {path: /, port: 80}
            periodSeconds: 10
---
apiVersion: v1
kind: Service
metadata:
  name: web
  namespace: demo
spec:
  selector:
    app: web
  ports:
    - port: 80
      targetPort: 80

Les probes httpGet s’exécutent sur le kubelet, donc ces images n’ont pas besoin de wget. fsGroup: 999 permet à l’image officielle Postgres d’écrire sur son volume. imagePullPolicy: IfNotPresent convient à un tag chargé localement ; :latest bascule toujours par défaut à Always et contacte le registre.

kubectl apply -f shop.yaml
kubectl rollout status deployment/api -n demo
kubectl port-forward -n demo svc/web 8080:80

port-forward remplace le 8080:80 de Compose sur votre ordinateur portable. Les autres machines devraient utiliser un Ingress.

Une PVC sans StorageClass reste Pending (Pod Pending / FailedScheduling). Une limite qui est trop petite est OOMKilled. Un mauvais DATABASE_URL est CrashLoopBackOff. Une image manquante est ImagePullBackOff.

Laissez Postgres à une seule réplique. ReadWriteOnce ne montera pas le même disque sur deux nœuds. Utilisez un StatefulSet et des volumeClaimTemplates plus tard si vous avez besoin d’un nom stable et d’un volume par réplique.

Quand choisir Compose

Choisissez Compose quand tous ces éléments sont vrais :

  • La pile tient sur une seule machine, y compris le disque pour Postgres.
  • Une politique de redémarrage suffit quand un processus s’arrête.
  • Les développeurs et la CI exécutent déjà docker compose up.
  • Un Pod n’a pas besoin de se déplacer vers un autre nœud.

Gardez Compose pour la boucle interne même après que la production soit un cluster. Un changement de fichier et docker compose up est plus court qu’un push et un déploiement.

Quand choisir Kubernetes

Choisissez Kubernetes quand l’un de ces éléments est vrai :

  • Vous avez besoin de plus d’un nœud ou un nœud peut être drained tandis que l’API continue de servir.
  • Vous voulez un historique de déploiement enregistré et kubectl rollout undo par Déploiement.
  • Plusieurs environnements doivent partager les mêmes manifests, avec de petites différences gérées par Helm ou Kustomize.
  • L’équipe plateforme exécute déjà le cluster et s’attend à des demandes, limites et probes.

Une deuxième réplique n’est pas une raison suffisante. --scale lance déjà des conteneurs API supplémentaires sur un seul moteur. Passez quand le placement, les mises à jour ou la politique importent.

Voie intermédiaire

Compose en CI. Démarrez la pile, échouez le job si les vérifications de santé ne passent pas, puis supprimez les volumes :

docker compose up -d --wait
docker compose ps
docker compose down -v

kind pour un plan de contrôle local. Chargez les images web et API, puis appliquez shop.yaml. Le nœud peut toujours tirer postgres:16. Quel cluster local utiliser est couvert dans kind vs minikube vs k3d.

kind create cluster --name demo
kind load docker-image example/web:1.4.0 --name demo
kind load docker-image example/api:1.4.0 --name demo
kubectl apply -f shop.yaml

Kompose comme brouillon, pas comme pipeline. Depuis le répertoire qui contient compose.yaml :

kompose convert -f compose.yaml

Kompose écrit un fichier YAML par objet dans le répertoire courant. Examinez les probes, le mot de passe, imagePullPolicy et le type de Service avant que quelqu’un ne les applique. Kubernetes n’exécute pas compose.yaml lui-même.

Chemin de migration

  1. Épinglez les tags d’image en CI. Le cluster devrait référencer example/api:1.4.0, pas un flottant latest et pas un bloc build:.
  2. Séparez la configuration. Les non-secrets vont à la ConfigMap. Le mot de passe va à un Secret créé en dehors de git sur tout cluster partagé.
  3. Tournez chaque vérification de santé Compose en readiness probe. Gardez une liveness probe seulement pour une vérification qui signifie « redémarrer ce conteneur ».
  4. Créez la PVC et copiez les données avec votre propre sauvegarde. docker compose down -v plus une nouvelle réclamation abandonne la base de données.
  5. Appliquez shop.yaml sur kind. Corrigez les pods Pending et les image pulls là.
  6. Ajoutez les demandes et les limites, et regardez l’API sous charge avant de copier les chiffres.
  7. Mettez un Ingress devant web. Laissez kubectl port-forward pour le débogage.
  8. Décidez où vivent les Postgres avant que les utilisateurs se déplacent : une base de données gérée, ou un StatefulSet avec sauvegardes.
  9. Appliquez sur le cluster partagé, exécutez kubectl rollout status, puis changez DNS.

FAQ

Docker Compose et Kubernetes sont-ils la même chose ?

Non. Compose lance les conteneurs sur un seul moteur Docker et les connecte avec un réseau projet. Kubernetes stocke les Déploiements, Services et volumes dans une API et peut placer les Pods sur d’autres nœuds. L’image peut être identique. Le modèle d’objet ne l’est pas.

Quand devrais-je passer de Docker Compose à Kubernetes ?

Passez quand le processus doit survivre à une défaillance de nœud, déployer sans temps d’arrêt, ou partager un cluster que d’autres équipes exécutent déjà. Restez sur Compose quand une seule machine, une politique de redémarrage et un job CI suffisent. Une diapositive qui dit « production » ne signifie pas obligatoirement Kubernetes.

Kompose produit-il des manifests prêts pour la production ?

Il produit un premier brouillon. kompose convert -f compose.yaml écrit un Déploiement et un Service par service Compose, et une PersistentVolumeClaim pour les volumes nommés. Vous devez toujours modifier les probes, les Secrets, les limites de ressources et le type de Service avant que quelqu’un d’autre applique les fichiers.

Qu’est-ce qui remplace depends_on dans Kubernetes ?

Rien n’attend une fois au démarrage et puis se retire. Une readinessProbe retire un Pod des endpoints du Service jusqu’à ce qu’il puisse servir, et elle le fait continuellement pendant la vie du Pod. Les clients doivent réessayer. Un initContainer peut bloquer l’app container, mais ne remplace pas la probe.

Comment ce guide a été vérifié

Rédigé à la main à partir de la documentation officielle, chaque commande revérifiée, puis relu une seconde fois avant publication. Dernière relecture le .

Sources officielles consultées pour cet article

Share.

Les commentaires sont fermés.