Blog DevOps

Kompose : migration facile de Docker Compose à Kubernetes

Rédigé par Tristan Dietz | 7 juil. 2022 12:45:00

Docker-compose ?

"Compose is a tool for defining and running multi-container Docker applications. With Compose, you use a YAML file to configure your application’s services. Then, with a single command, you create and start all the services from your configuration."

Source : documentation docker compose

Compose est la solution proposée par Docker pour monter facilement et rapidement une stack applicative complète.

Elle est très intéressante dans un environnement local : une fois le code écrit par un développeur, ce dernier peut recompiler l’image Docker, et faire tourner l’ensemble de son application (avec bases de données, backend, frontend, workers, etc.) sur son poste. Cela peut même être fait automatiquement en mettant en place un dispositif de live-reload.

Kubernetes

L’orchestrateur de conteneurs n’est plus à présenter.

Le moteur Kubernetes fonctionne avec un système de déclarations de ressources décrites au travers de fichiers de configuration. Il permet ainsi de créer, configurer, et lier des ressources les unes aux autres.

apiVersion: apps/v1
kind: Deployment
metadata:
  name: nginx-deployment
  labels:
    app: nginx
spec:
  replicas: 3
  selector:
    matchLabels:
      app: nginx
  template:
    metadata:
      labels:
        app: nginx
    spec:
      containers:
      - name: nginx
        image: nginx:1.7.9
        ports:
        - containerPort: 80

La question qui se pose est donc : comment générer des fichiers de configuration pour Kubernetes avec notre stack Docker Compose actuelle ?

Kompose

"Kompose is a tool to help users who are familiar with docker-compose move to Kubernetes."

Source : kompose help

On comprend qu’il s’agit d’un outil permettant la migration d’une configuration Docker Compose vers des manifests Kubernetes.

L’outil Kompose est un projet open-source, qui reçoit le soutien de la communauté de manière constante depuis 5 ans. Quelques pull-requests sont également validées au fil des mois. Ce sont de bons indicateurs de la stabilité du projet, et d’un certain niveau de maturité, même si ces valeurs restent globalement assez faibles.

Ainsi, le déploiement d’une stack Odoo, dont voici le fichier docker-compose.yaml ...

version: "3.3"
services:
  # Traefik
  traefik:
    image: "traefik:v2.4"
    container_name: "traefik"
    command:
      - "--providers.docker=true"
      - "--providers.docker.exposedbydefault=false"
      - "--entrypoints.websecure.address=:443"
      - "--certificatesresolvers.myresolver.acme.tlschallenge=true"
      - "--certificatesresolvers.myresolver.acme.email=$ACME_EMAIL"
      - "--certificatesresolvers.myresolver.acme.storage=/letsencrypt/acme.json"
    ports:
      - "443:443"
    volumes:
      - "./traefik:/letsencrypt"
      - "/var/run/docker.sock:/var/run/docker.sock:ro"

  # Database
  db:
    image: postgres:9.4
    container_name: "odoo-database"
    environment:
      - POSTGRES_PASSWORD=$POSTGRES_PASSWORD
      - POSTGRES_USER=$POSTGRES_USER
    volumes:
      - ./postgresql:/var/lib/postgresql/data
    restart: always

  # Odoo
  odoo10:
    image: odoo:10.0
    container_name: "odoo-10"
    depends_on:
      - db
    volumes:
      - ./odoo-10/addons:/mnt/extra-addons
      - ./odoo-10/etc:/etc/odoo
    restart: always
    labels:
      - "traefik.enable=true"
      - "traefik.http.routers.odoo.rule=Host(`$ODOO_URL`)"
      - "traefik.http.routers.odoo.entrypoints=websecure"
      - "traefik.http.routers.odoo.tls.certresolver=myresolver"

  # PGAdmin
  pgadmin:
    image: dpage/pgadmin4:4.25
    container_name: pgadmin4
    environment:
      - PGADMIN_DEFAULT_EMAIL=me@example.org
      - PGADMIN_DEFAULT_PASSWORD=odoo
      - PGADMIN_LISTEN_PORT=80
    volumes:
      - ./pgadmin:/var/lib/pgadmin
    restart: always
    labels:
      - "traefik.enable=true"
      - "traefik.http.routers.pgadmin.rule=Host(`$PGA_URL`)"
      - "traefik.http.routers.pgadmin.entrypoints=websecure"
      - "traefik.http.routers.pgadmin.tls.certresolver=myresolver"

... après une migration via kompose

$ kompose convert

…devient une liste de manifests Kubernetes, qui sont facilement déployables.

Toutefois, déployer le projet en l’état ne fonctionnera pas. Il faut remplir des champs générés qui n’auront pas été remplis. Dans notre exemple, le CRM Odoo a par exemple besoin de son URL afin que Traefik redirige dessus.

template:
    metadata:
      annotations:
        kompose.cmd: kompose convert
        kompose.version: 1.26.1 (HEAD)
        traefik.enable: "true"
        traefik.http.routers.odoo.entrypoints: websecure
        traefik.http.routers.odoo.rule: Host(``) # <-- ici
        traefik.http.routers.odoo.tls.certresolver: myresolver

Observations et limitations

L’utilisation d’un outil tel que Kompose peut faciliter la vie d’un administrateur Kubernetes. Mais s’en remettre uniquement à cet outil pour déployer des applications sur un cluster est une grossière erreur.

En effet, Kompose prend certains partis pris qui sont en dehors des standards de l’utilisation de Kubernetes.

Les volumes

Une première chose qui a toute son importance : Kompose ne génère pas de fichiers pour déclarer les Persistant Volumes (PV). Ceux-ci doivent donc être déclarés à part, car dissociés de la partie applicative.

L’application de la configuration donnée en exemple ci-dessus ne permet pas de lancer directement l’application. Les volumes ont été migrés en Persistant Volume Claims (PVC), cependant sans aucune configuration de PV associée, ils ne pourront pas se déployer correctement :

pod has unbound immediate PersistentVolumeClaims

Cela entraîne une impossibilité pour les pods de se déployer, et donc un problème sur les déploiements qui tomberont en erreur.

La raison est assez simple à deviner : le stockage de données étant spécifique à chaque infrastructure, et chaque entreprise ayant des besoins spécifiques en termes de disponibilité et d’archivage, on peut aisément comprendre que Kompose ne souhaite pas donner de recommandation particulière à ce sujet.

L’ingress controller

Le standard de Kubernetes est l’utilisation d’un Ingress Controller. Pour rappel, celui-ci fait office de proxy entre le monde extérieur et les applications au sein du cluster. Sur un seul Ingress Controller, on peut ainsi rerouter des routes. Celles-ci, déclarées au moyen d’Ingress, vont rediriger vers les services associés aux déploiements des applications, lesquels permettront d’accéder enfin aux pods.

Toutefois, Kompose ne permet pas de reconnaître une ressource de ce type. Aussi, la configuration du conteneur traefik va être émulée afin de correspondre fonctionnellement à ce qui est demandé : un service ouvert sur un port particulier, et qui permet de recevoir des requêtes sur un port donné dans le conteneur.

Traduit dans le monde Kubernetes, cela revient à créer un service en mode NodePort, qui va récupérer tout le trafic entrant. C’est finalement un comportement assez similaire à celui dont fonctionne un réel Ingress Controller, mais cela entraîne une particularité dans votre cluster. Utiliser son propre système de redirection de flux requiert d’être sûr de ce que vous faites, car vous vous retrouverez à devoir gérer des problèmes qui seront spécifiques à l’implémentation de cette couche réseau. Les ressources aidant au debug seront plus difficiles à trouver.

Un autre effet secondaire : cette émulation peut fonctionner lors du déploiement d’une application. Cependant, déployer une seconde stack du même type sur le cluster Kubernetes, avec une autre instance de Traefik donnera sur une erreur : le port 443 ici utilisé sera déjà pris par la première stack déployée.

Choix par défaut

L’outil Kompose ne peut pas répondre à toutes les questions. C’est pourquoi il est préférable de revoir les fichiers générés afin de pouvoir les étoffer. Dans notre exemple, aucune mention de limites de CPU ou de RAM n’a été précisée.

Dans les manifests générés, on retrouve dans les templates des déploiements les propriétés suivantes :

[...]
    spec:
      containers:
        - [...]
          resources: {}

Le champ resources, permettant de spécifier des limites et des requests pour les pods, est créé vide, comme un placeholder. Ainsi, les pods générés tournent en QoS class Best-Effort, ce qui signifie que ce seront les premiers à se faire evicted si le cluster manque de ressources.

Note

Il est possible de spécifier des limites CPU/RAM avec Docker Compose sur une version supérieure à 3 de la manière suivante, en dessous d’un service :

deploy:
  resources:
    limits:
      cpus: '0.5'
      memory: 500M

Cependant, cette portion ne sera pas interprétée par Docker Compose par défaut. En effet, la clé deploy n’est pas prise en compte par Docker Compose, car elle est dédiée à Docker Swarm. Les limitations CPU/RAM sur les containers ont été retirées de la spec entre la version 2 et la version 3. Il est toutefois possible de lancer sa configuration avec la commande suivante :

$ docker-compose --compatibility up

Cependant, la pratique n’est pas recommandée :

$ docker-compose --help
[...]
--compatibility             If set, Compose will attempt to convert keys
                            in v3 files to their non-Swarm equivalent (DEPRECATED)

Toutefois, cette option est interprétée par Kompose, même si les valeurs générées ne sont pas des plus lisibles par un humain.

[...]
		spec:
      containers:
        - image: odoo:10.0
          name: odoo-10
          resources:
            limits:
              cpu: 500m
              memory: "524288e3"

 

Conclusion

Kompose répond à la problématique de générer facilement des manifests Kubernetes prêts à l’emploi à partir d’un fichier Docker Compose. Toutefois, comme tout outil générateur de code, tout n’est pas bon à prendre. D’aucuns devront alors garder un œil critique et averti sur les configurations avant de les déployer jusqu’en production.

L’utilisation de Kompose dans un process de PoC est intéressante, car elle permet de gagner énormément de temps. Mais l’intégrer directement dans un processus de CI/CD automatisé dans le but de déployer automatiquement une configuration n’en est pas un bon usage.

L’administration de cluster Kubernetes est un travail à plein temps, qui nécessite une bonne connaissance de l’outil afin de prévenir un maximum de problèmes. Padok se tient à votre disposition pour vous accompagner dans vos migrations sur l’outil Kubernetes si besoin 🙂