Jour 1 : Docker et les Conteneurs pour la Certification CKA
Docker expliqué pour débuter votre préparation à la certification CKA.

Docker est la base sur laquelle Kubernetes s’appuie pour orchestrer des applications conteneurisées à grande échelle. Maîtriser Docker constitue donc une première étape indispensable.


Jour 1

Bienvenue dans cette série dédiée à la préparation à la certification Certified Kubernetes Administrator (CKA). Cette formation pas à pas est conçue pour vous guider dans l’apprentissage et la maîtrise des concepts fondamentaux et des compétences pratiques essentiels pour obtenir cette certification reconnue mondialement. Tout au long de cette série, vous explorerez des sujets essentiels tels que :

  • La conteneurisation avec Docker et son rôle dans l’écosystème Kubernetes.
  • Les bases de Kubernetes, incluant la gestion des pods, services et déploiements.
  • L’administration des clusters Kubernetes, avec un accent sur la haute disponibilité et la sécurité.
  • La résolution des défis courants rencontrés par les administrateurs Kubernetes en environnements de production.

Chaque tutoriel est structuré pour offrir une progression claire, mêlant théorie et pratique, afin de renforcer vos compétences techniques. Que vous soyez débutant ou déjà expérimenté, cette série vous fournira les outils nécessaires pour exceller à votre examen CKA et dans vos projets professionnels.

Prenez le temps d’expérimenter chaque étape, et n’hésitez pas à revenir sur les concepts clés si nécessaire. Bonne préparation et bonne chance dans votre parcours vers la certification CKA !



1. Introduction à Docker

1.1. Qu’est-ce que Docker ?

Docker est une plateforme open-source puissante conçue pour simplifier le développement, le déploiement et l’exécution d’applications via la conteneurisation. Les conteneurs offrent des environnements légers, portables et isolés qui encapsulent une application ainsi que ses dépendances. Cela garantit un comportement cohérent à travers divers environnements informatiques. Docker est devenu indispensable pour les systèmes logiciels modernes, offrant une rapidité, une efficacité et une évolutivité incomparables.

La simplicité et la puissance de Docker en ont fait un outil révolutionnaire pour les équipes de développement. Il permet des workflows cohérents, que vous programmiez sur votre ordinateur portable, testiez dans un pipeline CI/CD, ou déployiez sur des clusters de production.

1.2. Comprendre les Conteneurs vs les Machines Virtuelles

Architecture d'une VM vs Conteneurs (source : <a href='https://www.open-telekom-cloud.com/en/blog/cloud-computing/container-vs-vm'>Open Telekom</a>)
Architecture d'une VM vs Conteneurs (source : Open Telekom)

Les conteneurs et les machines virtuelles (VMs) facilitent tous deux des environnements d’application isolés, mais leurs mécanismes sous-jacents diffèrent fondamentalement :

  • Machines Virtuelles (VMs) :
    • Fonctionnent via une couche d’hyperviseur, comme VMware ou VirtualBox, exécutant un système d’exploitation (OS) invité complet.
    • Consomment beaucoup de ressources, car chaque VM inclut son propre noyau OS.
    • Temps de démarrage relativement longs en raison de l’initialisation complète du système d’exploitation.
  • Conteneurs :
    • Partagent le noyau OS de l’hôte, éliminant la surcharge d’un système d’exploitation invité supplémentaire.
    • Légers, contenant uniquement l’application et ses dépendances.
    • Temps de démarrage rapides et consommation minimale de ressources, les rendant plus efficaces.

1.3. Conteneurs vs Machines Virtuelles : une analogie

Pour clarifier la différence, considérons l’analogie des bâtiments résidentiels :

  • Machines Virtuelles : Elles sont analogues à des maisons individuelles. Chaque maison possède ses propres fondations, murs, plomberie et électricité, à l’image d’un système d’exploitation complet. Bien qu’isolées, elles nécessitent des ressources significatives pour être construites et maintenues.

  • Conteneurs : Ceux-ci ressemblent à des appartements dans un immeuble partagé. Les appartements partagent une infrastructure commune (fondations, murs, utilités), comme le noyau OS de l’hôte. Chaque appartement est autonome mais plus léger et plus rapide à construire qu’une maison individuelle.

1.4. Défis des Applications Non-Conteneurisées

Avant l’avènement de la conteneurisation, les applications faisaient face à plusieurs défis inhérents :

  1. Conflits de Dépendances : Les différentes applications nécessitaient souvent des versions incompatibles des mêmes bibliothèques ou environnements d’exécution, provoquant des conflits.

  2. Incohérences Environnementales : Les applications fonctionnaient fréquemment sur la machine locale d’un développeur mais échouaient en production en raison de différences environnementales.

  3. Inefficacité des Ressources : Exécuter plusieurs applications sur la même machine nécessitait des machines virtuelles gourmandes en ressources ou des configurations complexes.

  4. Limitations de Scalabilité : Faire évoluer ou mettre à jour des applications était laborieux, sujet aux erreurs et chronophage.

1.5. Comment Docker Résout Ces Défis

Docker répond efficacement à ces défis systémiques :

  1. Isolation : Les conteneurs encapsulent toutes les dépendances nécessaires, éliminant ainsi les conflits entre les applications.

  2. Portabilité : Les conteneurs garantissent un comportement cohérent des applications à travers différents environnements—du développement aux tests en passant par la production.

  3. Efficacité : Le partage du noyau OS de l’hôte réduit la consommation de ressources et accélère les temps de démarrage.

  4. Scalabilité : Docker simplifie le scaling horizontal en permettant le déploiement rapide de plusieurs conteneurs issus de la même image.

Passons maintenant de la théorie à la pratique pour découvrir comment utiliser Docker efficacement.

1.6. Un Workflow Docker Simple

Pour comprendre l’utilité de Docker, explorons son workflow de base :

  1. Écrire un Dockerfile : Créez un Dockerfile pour définir le modèle de votre conteneur, spécifiant l’image de base, le code de l’application et les dépendances.

  2. Construire une Image : Exécutez docker build pour compiler une image à partir du Dockerfile.

  3. Exécuter un Conteneur : Utilisez docker run pour instancier et exécuter un conteneur à partir de l’image, créant un environnement d’exécution isolé.

  4. Pousser vers un Registre : Enregistrez et partagez l’image en la poussant vers un registre de conteneurs (par ex., Docker Hub) avec docker push.

  5. Tirer et Déployer : Téléchargez et déployez l’image sur un autre système avec docker pull, garantissant un comportement d’application cohérent.

1.7. L’Architecture Docker

L'Architecture Docker (source : <a href='https://docs.docker.com/get-started/docker-overview/#docker-architecture'>Documentation officielle Docker</a>)
L'Architecture Docker (source : Documentation officielle Docker)

L’architecture Docker comprend plusieurs composants essentiels :

  1. Client Docker : L’interface utilisateur pour interagir avec Docker. Les commandes comme docker build et docker run sont transmises au Daemon Docker.

  2. Daemon Docker (dockerd) : Le service central qui gère les images, les conteneurs et leur cycle de vie. Il traite les requêtes du client et coordonne les opérations.

  3. Images : Modèles immuables pour créer des conteneurs. Elles encapsulent l’environnement et les dépendances de l’application.

  4. Conteneurs : Instances légères des images qui fournissent un environnement d’exécution pour l’exécution des applications.

  5. Registre Docker : Un dépôt centralisé pour stocker et distribuer les images Docker (par ex., Docker Hub ou des registres privés).

  6. Runtime de Conteneurs : Le moteur sous-jacent responsable de l’exécution des conteneurs. Le runtime de Docker, tel que containerd, garantit une exécution efficace des conteneurs.

Ce guide offre une compréhension fondamentale des capacités de Docker et de son rôle transformateur dans le développement logiciel moderne. À mesure que vous approfondissez vos connaissances, expérimentez les fonctionnalités de Docker pour maîtriser la conteneurisation et simplifier le déploiement des applications.


2. Installation de Docker

Avant de pouvoir conteneuriser un projet, assurez-vous que Docker est installé sur votre système. Voici les deux options principales :

2.1. Utilisation de Docker Desktop (solution complète mais lourde)

  • Avantages : Simple à installer, avec une interface graphique et des outils intégrés.
  • Inconvénients : Alourdit votre système en installant des composants souvent inutiles pour la préparation au CKA. La licence peut également poser problème en entreprise.
  • Étapes :
    1. Téléchargez Docker Desktop depuis docker.com.
    2. Installez et lancez Docker Desktop.

2.2. Installation des composants Docker individuellement (recommandée)

  • Avantages : Installation légère, adaptée à une préparation au CKA.
  • Inconvénients : Nécessite quelques étapes supplémentaires sur macOS et Windows.

2.2.1. Pour Windows avec WSL2

wsl --install
Powershell
  • Étape 2 - Ouvrez la distribution Ubuntu Linux qui s’est installée et suivez les étapes d’installation pour Linux ci-dessous.

2.2.2. Pour Linux

  • Étape 1 - Mettez à jour vos dépôts :
sudo apt update
Bash
  • Étape 2 - Installez Docker :
sudo apt install docker.io
Bash
  • Étape 3 - Activez et démarrez Docker :
sudo systemctl enable docker
sudo systemctl start docker
Bash
  • Étape 4 - Ajoutez votre utilisateur au groupe Docker (pour éviter d’utiliser sudo) :
sudo usermod -aG docker $USER
Bash

Redémarrez votre terminal pour appliquer les modifications.

2.2.3. Pour macOS avec Docker CLI et Colima

  • Étape 1 - Installez le client Docker avec Homebrew :
brew install docker
Bash
  • Étape 2 - Installez Colima avec Homebrew :
brew install colima
Bash

Pourquoi Colima ?

  • macOS nécessite Colima pour exécuter Docker, car son noyau Darwin (BSD-based) ne prend pas en charge nativement les fonctionnalités de conteneurisation Linux comme les namespaces et les cgroups.
  • Colima est un outil léger basé sur Lima qui crée une machine virtuelle Linux optimisée pour exécuter le daemon Docker, offrant ainsi un environnement compatible.
  • Étape 3 - Démarrez Colima pour initier le daemon Docker :
colima start -f
Bash

2.3. Test de l’installation

  • Lancer la commande docker suivante pour testez votre installation :
docker run hello-world
Bash

Vous devriez voir un message indiquant que tout fonctionne comme ci-dessous.

Hello from Docker!
This message shows that your installation appears to be working correctly.

To generate this message, Docker took the following steps:
 1. The Docker client contacted the Docker daemon.
 2. The Docker daemon pulled the "hello-world" image from the Docker Hub.
    (arm64v8)
 3. The Docker daemon created a new container from that image which runs the
    executable that produces the output you are currently reading.
 4. The Docker daemon streamed that output to the Docker client, which sent it
    to your terminal.

To try something more ambitious, you can run an Ubuntu container with:
 $ docker run -it ubuntu bash

Share images, automate workflows, and more with a free Docker ID:
 https://hub.docker.com/

For more examples and ideas, visit:
 https://docs.docker.com/get-started/
Output

En suivant ces étapes, vous aurez un environnement Docker configuré et fonctionnel, que ce soit pour des tests locaux, le développement ou la préparation à l’orchestration avec Kubernetes.

Alternatives à Docker

Bien que Docker reste l’outil de référence pour la conteneurisation, de nombreuses alternatives sont désormais disponibles. Parmi les principales solutions pouvant remplacer le daemon Docker ou le client Docker, on peut citer :

  • Podman : Une alternative sans daemon avec une interface similaire à Docker.
  • Containerd : Un runtime conteneur léger utilisé par Docker lui-même, pouvant être utilisé directement.
  • CRI-O : Un runtime conteneur optimisé pour Kubernetes, respectant l’interface CRI.

Cependant, dans le cadre de la préparation à la certification CKA, il est recommandé d’utiliser Docker pour garantir une familiarité avec les bases et les outils souvent mentionnés dans la documentation.


3. Utilisation de Dockker : Tutoriel Étape par Étape

Ce tutoriel vous guidera dans le processus de conteneurisation d’un projet avec Docker. À la fin de ce guide, vous saurez comment créer un Dockerfile, construire et pousser une image Docker, et travailler avec des conteneurs.

3.1. Le Dockerfile (Approche de Base)

Commençons par une approche simple. Cela nous permettra d’en comprendre les limitations et de les comparer avec une approche plus optimisée.

  • Étape 1 - Créez un répertoire pour votre projet :
mkdir docker-c-app
cd docker-c-app
Bash
  • Étape 2 - Créez un fichier main.c contenant :
#include <stdio.h>

int main() {
    printf("Bienvenue dans votre application Docker !\n");
    return 0;
}
C
  • Étape 3 - Créez un premier fichier Dockerfile :
# Utiliser une image contenant GCC pour compiler et exécuter l'application
FROM gcc:latest

# Définir le répertoire de travail
WORKDIR /app

# Copier le fichier source
COPY main.c .

# Compiler l'application
RUN gcc -o app main.c

# Définir la commande par défaut
CMD ["./app"]
Dockerfile

Syntaxe des Dockerfiles

Pour une compréhension approfondie de la syntaxe et des concepts des Dockerfiles, vous pouvez consulter la documentation officielle : Dockerfile Reference.

  • Étape 4 - Construisez l’image Docker :
docker build -t c-app-basic:1.0.0 .
Bash

Explication de la commande :

  • docker build : Cette commande crée une image Docker à partir des instructions définies dans le fichier Dockerfile présent dans le répertoire courant.
  • -t c-app-basic : L’option -t permet d’attribuer un nom (ici c-app-basic) et un tag (1.0.0) à l’image. Si aucun tag explicite n’est précisé, Docker utilise latest par défaut.
  • . : Ce point indique que le contexte de construction est le répertoire courant, où Docker recherche le Dockerfile et les fichiers nécessaires pour construire l’image.

Une fois cette commande exécutée, Docker lit les instructions du Dockerfile, crée l’image et l’enregistre localement dans votre système.

  • Étape 5 - Vérifiez que l’image a été créée :
docker images
Bash

Cette commande affiche la liste des images Docker présentes sur votre système local :

% docker images
REPOSITORY    TAG       IMAGE ID       CREATED         SIZE
c-app-basic   1.0.0     70543dee1b46   4 minutes ago   1.39GB
gcc           latest    d18c3b309576   5 months ago    1.39GB
hello-world   latest    ee301c921b8a   20 months ago   9.14kB
Output

Recherchez le nom c-app-basic dans la liste pour confirmer que l’image a bien été créée avec succès.

  • Étape 6 - Exécutez le conteneur :
docker run --rm c-app-basic:1.0.0
Bash

Explication de la commande :

  • docker run : Cette commande démarre un nouveau conteneur à partir de l’image spécifiée, ici c-app-basic:1.0.0.
  • --rm : Cette option indique que le conteneur sera automatiquement supprimé après son exécution. Cela permet de garder votre environnement propre et d’éviter d’accumuler des conteneurs inutilisés.
  • c-app-basic:1.0.0 : Nom de l’image et tag à partir desquels le conteneur a été créé.

Lorsque vous exécutez cette commande, Docker crée puis démarre un conteneur basé sur l’image c-app-basic et du tag 1.0.0. Une fois le programme dans le conteneur terminé, vous verrez le message suivant dans le terminal :

Bienvenue dans votre application Docker !
Output

Après l’exécution, le conteneur sera supprimé grâce à l’option --rm.

Référence des commandes Docker CLI

Pour découvrir et maîtriser les commandes essentielles de Docker CLI, consultez la fiche pratique officielle : Docker CLI Cheat Sheet.

3.2. Problème avec l’Approche de Base

L’approche de base, bien qu’elle soit fonctionnelle, présente plusieurs inconvénients majeurs :

  • Taille excessive de l’image : L’image finale inclut tous les outils nécessaires à la compilation, comme gcc, ainsi que d’autres bibliothèques et dépendances non nécessaires pour exécuter l’application. Cela augmente considérablement la taille de l’image, la rendant plus lourde à transférer et à déployer.

  • Surface d’attaque accrue : Les outils de compilation, bien qu’utiles pendant le processus de build, n’ont aucune utilité dans l’image finale. Leur présence élargit la surface d’attaque potentielle, exposant l’image à des vulnérabilités inutiles dans un environnement de production.

  • Non-optimisation pour la production : Une bonne pratique en production est de ne conserver que les fichiers nécessaires à l’exécution de l’application. Dans cette approche, les fichiers temporaires de build et les outils de compilation augmentent la complexité inutilement.

Pour vérifier la taille de l’image générée avec cette approche de base, utilisez la commande suivante :

docker images | grep c-app-basic
Bash

La grande taille de cette image (1.39Go) met en évidence l’importance d’optimiser les processus de construction avec une approche comme la construction multi-stage, qui sera explorée dans la section suivante.

3.3. Construction Multi-Stage avec Docker

La construction multi-stage est une technique qui permet de créer des images Docker optimisées en séparant le processus de build de l’application de celui de l’image. Chaque étape utilise une image spécifique et contribue à construire progressivement l’application, mais seules les parties nécessaires sont incluses dans l’image finale.

3.3.1. Avantages du multi-stage

  1. Réduction de la taille de l’image : Les outils de compilation et autres fichiers temporaires restent dans les étapes intermédiaires et ne sont pas inclus dans l’image finale.
  2. Sécurité améliorée : En excluant les outils inutiles comme gcc ou les bibliothèques de build, on réduit la surface d’attaque potentielle.
  3. Optimisation pour la production : L’image finale est minimale, ne contenant que ce qui est nécessaire à l’exécution de l’application.

3.3.2. Fonctionnement du multi-stage

Le fichier Dockerfile est structuré en plusieurs étapes. Une étape peut s’appuyer sur une autre en utilisant la directive COPY --from=<nom_étape>. Par exemple :

  1. Une première étape appelée étape de construction inclut tous les outils nécessaires pour compiler ou assembler le projet.
  2. Une étape suivante appelée étape finale récupère uniquement les fichiers nécessaires à l’exécution de l’application et utilise une image de base légère, comme alpine ou chainguard, pour réduire la taille de l’image.

3.3.3. Mise en Pratique

Pour résoudre les problèmes mentionnés dans l’approche de base, utilisons une construction multi-stage pour notre application :

  • Étape 1 - Modifiez le Dockerfile pour inclure plusieurs étapes :
# Étape de construction
FROM gcc:latest as builder

# Définir le répertoire de travail
WORKDIR /app

# Copier le fichier source
COPY main.c .

# Compiler l'application
RUN gcc -o app main.c

# Étape finale
FROM chainguard/glibc-dynamic

# Définir le répertoire de travail
WORKDIR /app

# Copier l'exécutable depuis l'étape de construction
COPY --from=builder /app/app .

# Définir la commande par défaut
CMD ["./app"]
Dockerfile
  • Étape 2 - Construisez l’image en multi-stage :
docker build -t c-app-optimized:1.0.0 .
Bash
  • Étape 3 - Exécutez le conteneur :
docker run --rm c-app-optimized:1.0.0
Bash

Vous verrez le même message dans le terminal : Bienvenue dans votre application Docker !

  • Étape 4 - Comparer la différence de taille :
docker images | grep c-app
Bash

Dans mon cas et à date, j’obtiens :

% docker images | grep c-app
c-app-optimized            1.0.0     cd510de25230   17 minutes ago      9.56MB
c-app-basic                1.0.0     70543dee1b46   About an hour ago   1.39GB
Output
  • Vous remarquerez que l’image optimisée est beaucoup plus petite (99% plus petite pour cette application certes très simple) grâce à l’utilisation d’une image légère (chainguard/glibc-dynamic) et à l’élimination des outils de compilation inutiles.

3.4. Résumé des Avantages de la Construction Multi-Stage

  • Réduction de la taille de l’image : L’image optimisée est beaucoup plus petite et rapide à déployer.
  • Sécurité améliorée : Les outils de compilation et les fichiers inutiles sont exclus de l’image finale.
  • Pratiques modernes : La construction multi-stage est une meilleure approche pour les environnements de production.

En comparant les deux approches, vous avez pu voir comment une construction multi-stage simplifie la gestion des conteneurs tout en réduisant les risques et les ressources.


4. Conclusion

Les conteneurs autonomes, bien qu’utiles pour des applications simples, posent des défis en termes de scalabilité, de gestion des ressources et d’orchestration lorsqu’ils sont déployés dans des systèmes plus complexes. Gérer le réseau, l’équilibrage de charge, et garantir une haute disponibilité manuellement devient de plus en plus difficile avec l’augmentation de la complexité de l’application.

Kubernetes résout ces défis en automatisant le déploiement, le scaling et la gestion des applications conteneurisées. Il offre des capacités d’auto-réparation, d’équilibrage de charge, d’optimisation des ressources et de configuration déclarative, le rendant idéal pour gérer des applications en environnement de production.

4.1. Quand Utiliser Kubernetes

  • Applications avec une architecture de microservices nécessitant une orchestration.
  • Environnements nécessitant une haute scalabilité et une tolérance aux pannes.
  • Pipelines CI/CD pour des tests et déploiements cohérents.
  • Déploiements multi-cloud ou hybrides pour répartir les charges de travail.
  • Applications avec des besoins dynamiques en trafic et en ressources.

4.2. Quand Ne Pas Utiliser Kubernetes

  • Applications petites et à conteneur unique.
  • Prototypes ou projets d’apprentissage où la simplicité est essentielle.
  • Systèmes avec des budgets ou des ressources limités.
  • Charges de travail statiques qui ne nécessitent pas de mises à jour ou de scalabilité fréquentes.
  • Systèmes en temps réel nécessitant une ultra-faible latence, où l’abstraction de Kubernetes pourrait ajouter un délai.

En intégrant des outils comme Docker avec Kubernetes, vous pouvez créer des environnements évolutifs et efficaces adaptés aux besoins de votre application. Cependant, évaluez toujours les compromis pour vous assurer que Kubernetes correspond à l’échelle et à la complexité de votre projet.

Jean-Jerome Levy

Ecrit par

Jean-Jérôme Lévy

Consultant DevOps

Professionnel chevronné dans le domaine de l’informatique, cumulant plus de 20 années d’expérience au sein de DSI de grandes entreprises, mon expertise diversifiée m’a permis de jouer un rôle clé dans de nombreux projets, caractérisés par la mise en place de pratiques DevOps innovantes.