Docker et Kubernetes : qui fait quoi, concrètement

Docker fabrique l’image, Kubernetes la fait tourner. Suivre la chaîne du code jusqu’au navigateur — image, Pod, Deployment, Service, ConfigMap — et voir la place de chacun.

6 min de lecturekubernetesdockerconteneurs

« Docker ou Kubernetes ? » est une question mal posée, et c’est la source de la plupart des confusions de débutant. Les deux ne sont pas des concurrents qui feraient la même chose : ils sont consécutifs. Docker fabrique le colis, Kubernetes le distribue et veille sur lui.

Le meilleur remède est de suivre la chaîne complète, du fichier source jusqu’au navigateur, et de nommer ce que chaque étape apporte.

La chaîne, en une image

La frontière est nette : tout ce qui est au-dessus de l’image est Docker, tout ce qui est en dessous est Kubernetes. Le ConfigMap, sur le côté, ne fait pas partie de la chaîne : il l’alimente.

Ce que fait vraiment docker build

bash
docker build -t demo-k8s:1.0 ./app

Trois morceaux, trois décisions :

MorceauCe qu’il dit
docker buildfabrique une image
-t demo-k8s:1.0nomme-la demo-k8s, version 1.0
./applis le Dockerfile et les fichiers de ce dossier

En français : prends l’application dans ./app, suis les instructions de son Dockerfile, et produis une image que j’appellerai demo-k8s:1.0.

Vérification :

bash
docker images
REPOSITORY   TAG   SIZE
demo-k8s     1.0   142MB

À ce stade, rien ne tourne. Vous avez fabriqué un colis, pas démarré un service. C’est le point que beaucoup de tutoriels sautent, et celui qui rend la suite incompréhensible.

La taille de ce colis se travaille, et elle compte : c’est le sujet du build multi-étapes, qui ramène une image d’un gigaoctet à sa taille utile.

Ce que Kubernetes fait de cette image

Il la référence dans un Deployment :

yaml
containers:
  - name: demo-web
    image: demo-k8s:1.0

Ce qui se lit : pour fabriquer mes Pods, utilise cette image.

demo-k8s:1.0

  Deployment  (replicas: 3)

 ┌────┴────┬────────┐
 ↓         ↓        ↓
Pod 1    Pod 2    Pod 3

Les trois Pods exécutent la même image. Ce ne sont pas trois applications différentes, ce sont trois exemplaires de la même.

Le Pod, en une phrase

Le Pod est l’endroit où Kubernetes fait tourner votre conteneur.

Docker seul          Avec Kubernetes
-----------          ---------------
Image                Image
  ↓                    ↓
Container            Pod

                     Container

Le Pod ajoute une couche autour du conteneur : une adresse IP, un cycle de vie, des réglages, une place sur un nœud. C’est cette couche qui permet à Kubernetes de le déplacer, de le recréer, de le compter.

Le Service, et le problème qu’il résout

Les Pods sont jetables, et c’est voulu. Leurs adresses changent :

Pod 1 → 10.1.0.5
Pod 2 → 10.1.0.6      Kubernetes recrée Pod 2
Pod 3 → 10.1.0.7             ↓
                      Pod 2 → 10.1.0.18

Vous ne pouvez donc pas donner l’adresse d’un Pod à un utilisateur : elle sera fausse demain. Le Service fournit une adresse stable devant le groupe, et répartit les requêtes :

        Utilisateur

          SERVICE          ← nom et IP stables

 ┌───────────┼───────────┐
 ↓           ↓           ↓
Pod 1      Pod 2      Pod 3

C’est développé dans Service Kubernetes : pourquoi l’IP d’un Pod ne suffit pas, avec le diagnostic à faire quand un Service ne répond rien.

Le ConfigMap, et pourquoi il existe

Imaginez trois réglages : un titre, une version, une couleur de fond. Vous pourriez les écrire dans le code. Mais alors, changer le bleu en rouge impose de refaire une image et de la redéployer — pour une valeur hexadécimale.

Le ConfigMap sort ces valeurs de l’image :

yaml
apiVersion: v1
kind: ConfigMap
metadata:
  name: demo-config
data:
  APP_TITLE: 'Bonjour depuis Kubernetes'
  APP_VERSION: '1.0.0'
  BG_COLOR: '#0f172a'

Les Pods les lisent au démarrage. Changer une couleur redevient ce que ça devrait être : modifier une ligne de YAML. Le détail — et il compte — est que les Pods ne voient pas le changement tout seuls ; c’est le sujet de ConfigMap Kubernetes : sortir la configuration de l’image.

Un mot de passe, en revanche, ne va pas là. Il existe un objet distinct pour les valeurs sensibles, le Secret — dont le nom promet un peu plus qu’il ne tient.

Le tableau des responsabilités

C’est la carte à garder en tête.

ObjetSon rôleQui le fournit
Image Dockercontient l’applicationDocker
Podexécute l’applicationKubernetes
Deploymentmaintient le nombre de Pods vouluKubernetes
ConfigMapfournit les réglagesKubernetes
Secretfournit les réglages sensiblesKubernetes
Servicedonne un accès stable et répartit le traficKubernetes

L’analogie du restaurant

Elle fonctionne parce que chaque rôle a son équivalent exact.

L’image Docker est la recette. Écrite une fois, identique partout, versionnée.

Le Pod est un cuisinier qui exécute cette recette. Trois Pods, trois cuisiniers qui font le même plat.

Le Deployment est le gérant qui dit : je veux toujours trois cuisiniers en poste. Si l’un s’absente, il en fait venir un autre — sans qu’on le lui demande.

Le ConfigMap est la feuille de consignes du jour : sauce barbecue, menu d’été, couleur de l’écran d’affichage. On la change sans réécrire la recette.

Le Service est le comptoir. Le client commande au comptoir ; il ne va pas choisir son cuisinier en cuisine, et il ne sait même pas combien ils sont.

              Client

             COMPTOIR
            (le Service)

      ┌──────────┼──────────┐
      ↓          ↓          ↓
   Cuisinier  Cuisinier  Cuisinier

Là où l’analogie s’arrête, et il faut le savoir : un cuisinier apprend et s’améliore, un Pod non. Un Pod est strictement interchangeable, sans mémoire de ce qu’il a fait. C’est précisément cette absence d’état qui permet à Kubernetes de le détruire sans prévenir.

Le projet complet

bash
docker build -t demo-k8s:1.0 ./app
kubectl apply -f k8s/

Le second ordre lit un dossier :

k8s/
├── configmap.yaml
├── deployment.yaml
└── service.yaml

et construit ceci :

                 CONFIGMAP
               les réglages


                DEPLOYMENT
            image demo-k8s:1.0
                replicas: 3

        ┌────────────┼────────────┐
        ↓            ↓            ↓
      POD 1        POD 2        POD 3
        └────────────┼────────────┘

                  SERVICE
                port 30080


                Navigateur

Ce projet existe pour de vrai, avec ses sept fichiers, ses commandes et les trois vérifications qui prouvent que chaque objet fait son travail : déployer une application sur Kubernetes, le projet complet.

Pourquoi mon image n’est pas trouvée par le cluster ?

Parce que docker build a mis l’image dans le dépôt local de votre machine, et que le cluster cherche dans le sien. Sur un cluster local, il faut la lui donner explicitement — minikube image load demo-k8s:1.0 ou kind load docker-image demo-k8s:1.0 — ou construire directement dans son démon Docker. Sur un vrai cluster, l’image doit être poussée dans un registre accessible depuis les nœuds.

C’est l’erreur numéro un des premiers déploiements, et elle se manifeste par un Pod en ErrImagePull ou ImagePullBackOff.

Ce qu’il faut retenir

Une phrase, si vous n’en gardez qu’une :

Docker fabrique l’application sous forme d’image. Kubernetes prend cette image, en crée des Pods, le Deployment maintient ces Pods en vie, le ConfigMap leur donne leurs réglages, et le Service permet aux utilisateurs de les joindre.

La suite naturelle est le Deployment lui-même, qui est l’objet à maîtriser avant tous les autres : votre premier Deployment vraiment fiable. Puis la question de la deuxième version, qui n’est plus du tout la même : rolling update et migration de base sans coupure.

Ce sujet fait partie d’un cours complet

Développement et déploiement de solutions de données — les premiers modules sont en accès libre.

Voir le plan du cours

Continuer sur le même sujet