« Tu passeras jamais de Docker Compose à Kubernetes »

Temps de lecture estimé : 7 minutes

Il y a quelques années, alors que je présentais une pile conteneurisée sur laquelle je travaillais à un confrère, celui-ci m’a affirmé avec aplomb : « tu ne passeras jamais de Docker Compose à Kubernetes ». Bien sûr, ce qui est affirmé sans preuve peut être nié sans preuve1, mais cette phrase est révélatrice d’une réflexion insidieuse dans le monde informatique. Aussi, étudions-la de plus près pour révéler ce qui s’y cache.

C’est vrai au moins ?

La question la plus évidente pour commencer est aussi la plus simple : est-ce que cette phrase est vraie ? Pour répondre techniquement à cette question, il faut comprendre ce qu’est et ce que fait l’outil Docker Compose. La conteneurisation n’est pas une virtualisation, c’est du cloisonnement : bien utiliser un conteneur, c’est avoir une relation d’identité entre le programme et le conteneur qui l’encapsule : notre conteneur n’a qu’un seul processus à faire tourner.

Architecturalement, notre pile est donc représentée par un conteneur pour le message broker, un autre pour le serveur web, un suivant pour la base de données, etc. Et c’est très bien comme ça, ça assure l’horizontalité et le découplage ! Seulement, pour éviter de manipuler une foule de conteneurs individuellement, Docker Compose a été créé. Il sert à deux choses : 1. manipuler facilement une pile logicielle complète et 2. créer un réseau pour les composants de cette même pile. C’est tout. Docker Compose n’est pas un orchestrateur, c’est un gestionnaire inerte de pile conteneurisée. Il n’y a aucune syntaxe de Docker Compose qui ne peut être traduite en Docker seul2.

Donc Docker Compose, c’est Docker. Or, Docker s’appuie sur containerd, lui-même implémentant l’interface CRI… consommée par Kubernetes3. Autrement dit, puisque la première technologie est socle de la seconde, il n’existe aucune incompatibilité entre Docker et Kubernetes.

Mais s’arrêter à la technique seule ne suffit pas à représenter la possibilité de migration. Un choix technologique, c’est plus qu’un outil : c’est une syntaxe, des procédures et une organisation. Bref, un contexte d’exécution. Le passage d’une technologie à une autre s’évalue donc au regard de ce qu’on devra changer pour arriver à la situation finale, autrement dit le coût de la migration. Il y a pas de solution toute prête et universellement applicable, chaque situation est différente. Ceci étant, la description de la solution technique (syntaxe et sémantique) est la plus facile à migrer : l’outil officiel Kompose4 permet justement de convertir un descripteur docker-compose.yml en manifestes Kubernetes. Est-il probant ? Voyons cela.

Notre descripteur passe de ça :

services:
  proxy:
    image: nginx
    volumes:
      - type: bind
        source: ./proxy/nginx.conf
        target: /etc/nginx/conf.d/default.conf
        read_only: true
    ports:
      - 80:80
    depends_on:
      - backend

À ça :

apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  labels:
    io.kompose.service: proxy-claim0
  name: proxy-claim0
spec:
  accessModes:
    - ReadOnlyMany
  resources:
    requests:
      storage: 100Mi
---
apiVersion: apps/v1
kind: Deployment
metadata:
  annotations:
    kompose.cmd: kompose convert
    kompose.version: 1.32.0 (HEAD)
  labels:
    io.kompose.service: proxy
  name: proxy
spec:
  replicas: 1
  selector:
    matchLabels:
      io.kompose.service: proxy
  strategy:
    type: Recreate
  template:
    metadata:
      annotations:
        kompose.cmd: kompose convert
        kompose.version: 1.32.0 (HEAD)
      labels:
        io.kompose.network/src-default: "true"
        io.kompose.service: proxy
    spec:
      containers:
        - image: nginx
          name: proxy
          ports:
            - containerPort: 80
              hostPort: 80
              protocol: TCP
          volumeMounts:
            - mountPath: /etc/nginx/conf.d/default.conf
              name: proxy-claim0
              readOnly: true
      restartPolicy: Always
      volumes:
        - name: proxy-claim0
          persistentVolumeClaim:
            claimName: proxy-claim0
            readOnly: true
---
apiVersion: v1
kind: Service
metadata:
  annotations:
    kompose.cmd: kompose convert
    kompose.version: 1.32.0 (HEAD)
  labels:
    io.kompose.service: proxy
  name: proxy
spec:
  ports:
    - name: "80"
      port: 80
      targetPort: 80
  selector:
    io.kompose.service: proxy

Pas mal non ? Bien sûr, sa promesse d’absorber 99% du travail est un slogan commercial, mais l’outil est suffisamment abouti pour réduire grandement le coût de réécriture. Le reliquat, l’arborescence système et l’amélioration vous appartiennent.

NB: attention aux compose.yml templatés. Au pire, résolvez les variables avec docker compose config

Ce que signifie vraiment la phrase

Cette phrase laisse entendre que Kubernetes est la solution technique finale, une cible à atteindre. Disons-le sans aucun détour, ce type de pensée est dangereux. Effectivement, Kubernetes est populaire, c’est indéniable5, mais utiliser une solution parce que tout le monde l’utilise correspond à suivre le sophisme « argumentum ad populum », sans tenir compte de ses qualités intrinsèques. Et pour autant, ses qualités sont réellement présentes : Kubernetes est très efficace sur la gestion du cycle de vie, la réconciliation ou la scalabilité tridimensionnelle. Vraiment, j’aime Kubernetes.

Mais ces raisons font-elles de Kubernetes une solution universelle ? En aucune façon. Tout comme il n’existe pas d’algorithme de compression universel6, il n’existe aucune solution technique adaptée à tout besoin et toute équipe. Toute situation en somme. Il faut donc choisir avec soin par quels moyens et outils nous allons répondre aux besoins de l’entreprise. Puisque ces choix peuvent être structurants, sélectionner l’outil avant même de comprendre la nature du problème est l’assurance de devoir payer tôt ou tard un coût de correction très élevé, quand il sera évident pour tous que la solution ne rend pas le service escompté.

Cette analyse se fait à un instant T, mais elle est simpliste. Il existe un cran supérieur de complexité vu qu’une entreprise vit et se transforme au cours du temps ; ce qui était vrai hier ne le sera sans doute plus demain. Comment alors créer une solution pour une situation en perpétuel mouvement ? Pourquoi dire « il nous faudra Kubernetes » ? N’est-ce pas faire des suppositions sur le futur avec, là encore, le risque d’avoir un décalage entre le besoin et la solution ? C’est ce que nous enseigne l’agile, à mettre de côté la prétention selon laquelle nous pouvons penser en amont à tous les besoins présents et futurs, et concevoir la solution parfaite pour y répondre (ou Big Upfront Design7). C’est bien mal connaître la réalité d’une entreprise et c’est anti-agile.

évolution d’un transport sous le modèle agile

Mais il faut bien avancer et commencer quelque part, j’en ai bien conscience. Et c’est là la difficulté de notre métier, car la frontière est fine entre « anticiper » et « faire quelque chose qui ne servira à rien ». Nous devons penser à demain mais pas trop, nous concentrer sur l’actuel mais pas trop. Trop de l’un et on a du code dormant qui ne produit pas de valeur, trop de l’autre et on crée des ridigités. Dans notre cas précis, peut-être que nous n’aurons jamais besoin de l’orchestration fournie par Kubernetes ou peut-être que Docker Swarm suffira amplement.

C’est pour cette raison que je dégage de cette analyse quelques mantras :

  1. Faire preuve d’humilité en faisant le moins de suppositions possibles,
  2. Avancer par petits pas,
  3. Découpler la solution technique pour permettre de pivoter quand ce sera nécessaire (coucou DDD !).

Pour ce qui est de l’avenir, il ne s’agit pas de le prévoir, mais de le rendre possible.
~ Antoine de Saint-Exupéry

On passera de Docker Compose à Kubernetes ?

Nous avons parfois de mauvaises raisons de penser ce qu’on pense, c’est pourquoi nous devons nous effacer devant la réalité de l’entreprise ; pour éviter les biais, on devrait toujours pouvoir retracer le chemin logique de nos choix techniques. La priorité est de toujours partir du besoin métier et des capacités de l’entreprise, surtout quand l’enjeu est la pertinence de l’entreprise sur son marché. « Quel est mon problème et comment puis-je y répondre ? Quelle est la trajectoire à 2 ans ? » doivent être les questions qui nous pilotent. Ces questions sont un cadre rigoureux pour faire coller la solution aux besoins et strictement aux besoins. Tout le monde n’est pas Google et ne requiert pas une architecture scalable jusqu’à 5 000 serveurs. Tout est une question d’arbitrage.

La conséquence de ça, c’est que l’entreprise va devoir payer régulièrement pour les transformations de la solution au lieu de tout financer en une fois, mais c’est la seule solution pour accompagner les évolutions du marché et être performant. Il n’y a pas de raccourci et penser le contraire est une illusion.

Comme d’habitude, vous pouvez retrouver le code pour convertir par vous-même le fichier compose en manifestes Kubernetes sur le dépôt d’exemple.


Sources:


    Ce billet vous a plu ? Partagez-le sur les réseaux…


    … Ou inscrivez-vous à la newsletter pour ne manquer aucun article (Si vous ne voyez pas le formulaire, désactivez temporairement uBlock).

    Voir aussi