Aller au contenu

Migrer depuis un docker-compose

Vous avez une application qui tourne avec docker compose up et vous voulez la faire tourner sur Kontainers ? Bonne nouvelle : les concepts se correspondent presque un pour un. Cette page donne la table de conversion, puis migre une stack réaliste de bout en bout.

Table de correspondance

docker-composeKubernetesNotes
services.<nom>Deployment + ServiceLe Service reprend le nom : les autres conteneurs le joignent par ce nom, comme dans compose
image:spec.containers[].imageRegistre privé ? Il faudra un pull secret
ports: (exposition web)IngressHTTPS automatique — jamais de ports: publiés directement
ports: (TCP brut)Service NodePort ou LoadBalancerAvec le label exposition: public sur les pods
environment:env: ou ConfigMapGuide
env_file:ConfigMap (+ Secret pour le sensible)envFrom: reproduit le comportement
volumes: (named volume)PersistentVolumeClaimGuide
volumes: (bind mount ./conf:...)ConfigMap montée en volumePas de bind mount sur un cluster
depends_on:readinessProbe (+ initContainers au besoin)Kubernetes redémarre jusqu’à ce que ça marche — les probes rendent ça propre
restart: always(comportement par défaut d’un Deployment)Rien à faire
deploy.resourcesresources.requestsÀ poser d’emblée : sinon 0,1 vCPU / 200 Mi sont injectés et facturés
networks:(rien)Tous les pods d’un namespace se joignent par nom de Service

Ce qui ne se transpose pas

  • network_mode: host, privileged: true, montages de /var/run/docker.sock : rejetés par les politiques de sécurité — repensez le besoin ou parlez-en au support ;
  • build: : Kubernetes ne construit pas d’images. Construisez et poussez vers un registre en amont (CI ou poste local), puis référencez l’image (guide registre privé à venir).

Migration guidée : web + base de données + volume

Le docker-compose.yml de départ, très classique :

services:
web:
image: ghcr.io/acme/mon-app:1.4
ports:
- "8080:8080"
environment:
DATABASE_URL: postgres://app:secret@db:5432/app
db:
image: postgres:17-alpine
environment:
POSTGRES_USER: app
POSTGRES_PASSWORD: secret
POSTGRES_DB: app
volumes:
- dbdata:/var/lib/postgresql/data
volumes:
dbdata:

Sa traduction complète pour Kontainers — un seul fichier, prêt à adapter :

# --- Le volume de la base (remplace "volumes: dbdata") ---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: dbdata
spec:
accessModes: [ReadWriteOnce]
storageClassName: ceph-block-rwo
resources:
requests:
storage: 10Gi
---
# --- Les secrets (remplacent les mots de passe en clair) ---
apiVersion: v1
kind: Secret
metadata:
name: db-credentials
stringData:
POSTGRES_USER: app
POSTGRES_PASSWORD: changez-moi
POSTGRES_DB: app
DATABASE_URL: postgres://app:changez-moi@db:5432/app
---
# --- Le service "db" ---
apiVersion: apps/v1
kind: Deployment
metadata:
name: db
spec:
replicas: 1
strategy:
type: Recreate # volume RWO : jamais 2 pods db en même temps
selector:
matchLabels: {app: db}
template:
metadata:
labels: {app: db}
spec:
containers:
- name: postgres
image: postgres:17-alpine
envFrom:
- secretRef: {name: db-credentials}
resources:
requests: {cpu: 250m, memory: 512Mi}
volumeMounts:
- name: data
mountPath: /var/lib/postgresql/data
volumes:
- name: data
persistentVolumeClaim: {claimName: dbdata}
---
apiVersion: v1
kind: Service
metadata:
name: db # ← le nom que "web" utilise, comme dans compose
spec:
selector: {app: db}
ports: [{port: 5432}]
---
# --- Le service "web" ---
apiVersion: apps/v1
kind: Deployment
metadata:
name: web
spec:
replicas: 2
selector:
matchLabels: {app: web}
template:
metadata:
labels: {app: web}
spec:
containers:
- name: web
image: ghcr.io/acme/mon-app:1.4
env:
- name: DATABASE_URL
valueFrom:
secretKeyRef: {name: db-credentials, key: DATABASE_URL}
resources:
requests: {cpu: 100m, memory: 256Mi}
readinessProbe: # remplace depends_on
httpGet: {path: /, port: 8080}
initialDelaySeconds: 5
---
apiVersion: v1
kind: Service
metadata:
name: web
spec:
selector: {app: web}
ports: [{port: 8080}]
---
# --- L'exposition publique (remplace ports: "8080:8080") ---
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: web
annotations:
cert-manager.io/issuer: letsencrypt
spec:
rules:
- host: app.example.com
http:
paths:
- path: /
pathType: Prefix
backend:
service: {name: web, port: {number: 8080}}
tls:
- hosts: [app.example.com]
secretName: web-tls

Déployez, puis vérifiez :

Fenêtre de terminal
kubectl apply -f stack.yaml -n <votre-namespace>
kubectl get pods -n <votre-namespace>

Pointez ensuite votre DNS vers la plateforme — et votre application est en ligne, en HTTPS.

Les pièges de conversion à connaître

  1. Posez vos resources.requests dès la conversion — compose n’en exigeait pas, Kontainers en injecte sinon (et les facture) ;
  2. La limite mémoire = la request : si votre conteneur consommait 800 Mi sous compose, demandez 1 Gi de request, pas 200 Mi (pourquoi) ;
  3. depends_on n’existe pas : votre app doit tolérer une base pas encore prête au premier démarrage (elle redémarrera jusqu’à ce que ça passe — c’est normal et sain) ;
  4. Quotas : 20 pods, 10 vCPU, 20 Gi de requests par namespace (les chiffres) — largement suffisant pour une stack compose typique, mais à connaître avant de scaler.