TP1 : Découverte et prise en main de Docker

Table des matières

Introduction

L’objectif de ce premier TP / labo est de vous permettre de découvrir la notion de “container” et son utilité au travers d’exercices pratiques avec l’outil de conteneurisation Docker.

A la fin de ce TP, vous devez être capable de :

  • comprendre les termes “image”, “container”, “publication de ports”, “registre d’images”, “dockerfile”
  • démarrer un container avec les paramètres appropriés,
  • interagir avec le container via un shell,
  • récupérer des informations sur un container en cours d’exécution (adresse IP, ports à l’écoute, …),
  • envoyer des requêtes au container en exploitant à bon escient la publication de ports,
  • pouvoir utiliser une image soit depuis le système de fichier local, soit depuis un registe d’images,
  • créer une image sur mesure depuis un Dockerfile

Lectures préalables

Avant de réaliser ce TP, il est important de vous informer sur la notion de “container”. Pour cela, vous êtes invités à prendre connaissance des chapitres théoriques suivants :

  • Chapitre 1 : Virtualisation et conteneurisation. L’objectif est de comprendre les concepts et techniques de virtualisation, la différence entre la virtualisation et la conteneurisation, et les enjeux spécifiques à chacun d’eux. Les sections accompagnées du tag [facultatif] sont des sections qu’il est conseillé de lire mais sur lesquelles vous ne serez pas interrogés.
  • Chapitre 2 : Les containers et Docker. Ce chapitre détaille les aspects techniques des containers et de leur fonctionnement. Ici aussi, certaines sections sont marquées du tag [facultatif], cfr ci-dessus.
  • Chapitre 3 : Docker en pratique. Ce chapitre présente de manière plus pratique et concrète l’utilisation de Docker. Il vous sera utile pour comprendre les manipulations que vous effectuerez dans ce TP, n’hésitez pas à y revenir régulièrement. Les sections 3.1. à 3.3 vous serons utiles cette semaine, les suivantes serviront pour le second TP.

Vous pouvez également, si vous le souhaitez ou si vous avez besoin de plus d’explications, consulter la documentation officielle Docker.

Installation

Pour utiliser Docker, vous avez deux possibilités :

  • Utiliser une machine virtuelle sur laquelle Docker est installé. C’est la configuration recommandée si vous travaillez sur les PCs des labos. Vous pouvez par exemple utiliser la VM GNS3-Admin-I-2022, qui est une distribution Ubuntu sur laquelle Docker est installé. Si vous n’arrivez pas à installer Docker sur votre machine, cette option peut également vous convenir.
  • Installer Docker sur votre ordinateur personnel. Pour cela, référez-vous aux instructions d’installation sur la documentation officielle Docker

1. Premier container

1.1. Hello World

Pour vous assurer que Docker est fonctionnel sur votre poste de travail, exécutez la commande suivante : docker run hello-world

Pouvez-vous expliquer avec vos mots ce qui s’est passé suite à l’exécution de cette commande?

1.2. Observer un container

Exécutez à présent la commande : docker container ls -a

Retrouvez les informations suivantes sur le container lancé précédemment :

  1. Quel est son identifiant ?
  2. Quel est son nom ?
  3. Quel est son état ?
  4. Quel est le nom de son image? Avez-vous vu au point 1.1. d’‘où cette image provenait?
  5. Quelle commande le container a-t-il exécuté?
  6. Si vous avez installer Docker Desktop, pouvez-vous retrouver ces mêmes informations dans l’interface graphique?

1.3. Les images

Tapez à présent la commande suivante : docker image ls

  1. Quelles informations voyez-vous? Quel est le lien avec ce que vous avez observé auparavant?
  2. Comparez l’output de cette commande avec la vue correspondante de l’interface graphique.
  3. Essayez de trouver la commande qui vous permettra de supprimer cette image. C’est une bonne idée de ne pas conserver les images non utilisées sur votre système de fichiers : même avec la mutualisation de couches, elles prennent de l’espace sur le disque!

2. Utiliser un container

Nous allons à présent voir comment il est possible d’interagir avec un container. Pour cela, nous allons prendre un simple container basé sur une distribution Linux Ubuntu, et interagir avec lui comme nous le ferions dans le cas d’une machine “normale” ou d’une machine virtuelle, via un shell interactif.

2.1. Interagir avec un container

Lancez un container Ubuntu avec la commande suivante : docker run -it ubuntu bash

Normalement, après téléchargement de l’image ubuntu, le container se lance et vous obtenez un “prompt” dans votre console indiquant que vous pouvez interagir avec ce container.

  1. A quoi servent les options i et tdans la commande ci-dessus?
  2. Chaque container Docker est destiné à exécuter une commande unique. Quelle est-elle dans ce cas-ci?
  3. Dans le container, quels sont les processus présents? Et leurs PIDs?
  4. Avec quel utilisateur êtes-vous loggé?
  5. Votre container a-t’il accès à Internet? Qui est son résolveur?

2.2. Inspecter un container

Dans un autre terminal et la commande docker inspect ou via l’interface graphique, essayez d’obtenir des informations sur ce container. Conseil : la commande greppeut vous être d’une grande aide!

  1. Chaque container dispose d’une interface réseau. Quelle est l’adresse IP de l’interface de votre container?
  2. Votre container a-t’il des ports ouverts?

Une fois que vous avez pu retrouver l’information demandée, vous pouvez à présent éteindre votre container ubuntu. Deux options sont possibles :

  • Utiliser la commande docker stopdepuis la GUI ou un autre terminal.
  • Taper exitdans le shell du container. Cela aura pour effet de quitter le processus bash, qui est le processus principal du container. Une fois ce processus quitté, le container n’a plus de raison d’être et s’arrête automatiquement (il est néanmoins possible de le redémarrer).

2.3. Faire tourner un service dans un container

Les containers sont utiles pour faire tourner des services de manière isolée, tout en permettant beaucoup de souplesse pour la migration ou la mise à jour : les containers peuvent être détruits et redémarrés très facilement, simplement sur base d’une image. Nous allons ici lancer un petit serveur web et voir comment le rendre accessible.

Nous allons commencer par exécuter un container sur base de l’image officielle “nginx” (serveur web concurrent d’Apache). Cette image est disponible sur le Docker Hub, et sera automatiquement téléchargée lorsque vous tenterez de l’utiliser. Il s’agit d’une image sur laquelle nginx et ses dépendances ont été installée, et qui peut donc être utilisée telle quelle.

  • Exécutez la commande docker run --name=mon-serveur-web nginx dans un terminal.
  • Examinez à présent le container ainsi lancé, soit depuis l’interface graphique, soit depuis la console. Observez ce que vous voyez au niveau des “ports”.
  • Le serveur nginx tourne donc sur le port 80, sur l’interface du container. Cependant, ce serveur n’est pas accessible en dehors de l’environnement Docker. Pour solutionner ce problème, nous allons effectuer une redirection de port (aussi appelée publication de port en terminologie Docker). Eteignez le container, puis relancez-en un nouveau, cette fois avec la commande docker run -p80:80 --name mon-serveur-web nginx.
  • Analysez le container et comparez les informations relatives aux ports ouverts avec celles obtenues deux étapes plus tôt. Quelle est la différence ? Si votre hôte est un système Linux, n’hésitez pas à confronter ces informations à l’output de netstat.
  • Essayez à présent d’accéder au serveur web via votre navigateur.

Qu’avez-vous observé au niveau des “ports” ? Expliquez et illustrez votre réponse avec des screenshots.

3. Construire des images

Pour la suite de ce TP, référez-vous à cette section du support théorique.

3.1. Figer un container

Imaginons que nous souhaitions configurer un service conformément à nos besoins. Par exemple, dans le cas de notre petit serveur web, nous souhaitons modifier la page d’accueil pour la personnaliser.
Nous pouvons lancer un container sur base d’une image de base, ouvrir un shell interactif et aller éditer nos configurations.

  • Vérifiez que votre serveur web tourne toujours. Son processus principal (“commande”) est normalement le processus nginx.
  • Démarrez terminal bash. Pour cela, nous allons lancer un second processus sur le container, en y attachant notre terminal afin de pouvoir interagir : docker exec -it \<nom-du-container> /bin/bash
  • Nous allons à présent commencer à personnaliser notre container. Nous pouvons installer des outils supplémentaires, via un apt updatepuis apt install. Installez ce que vous voulez, par exemple nano, ou encore votre outil favori netstat.
  • Pour personnaliser la page d’accueil, vous pouvez ensuite aller éditer le fichier /usr/share/nginx/html et le transformer à votre goût. Tester ensuite avec votre navigateur que les modifications ont bien eu lieu.

Votre container est à présent personnalisé : vous avez effectué des modifications sur la couche supérieure du système de fichiers en éditant le fichier html et en ajoutant des packages à l’OS. Néanmoins, il s’agit d’un container spécifique : l’image de départ n’a pas été modifiée, et si vous perdiez votre container, vous perdriez également toutes vos modifications!

Une première façon de rendre ces modifications persistantes consiste à créer un snapshot de ce container, et d’en faire une image.

  • Utilisez la commande docker container commit \<nom-container> \<nom-image>pour créer une image sur base de votre serveur web.
  • Vérifiez dans la liste de vos images que vous retrouvez bien celle de votre serveur web.
  • Arrêtez votre précédent container si ce n’est pas déjà fait, afin de libérer le port 80.
  • Démarrez un nouveau container sur base de votre image personnelle, et vérifiez qu’elle fonctionne comme prévu.

3.2. Créer une image sur base d’un Dockerfile

La technique du snapshot est pratique, mais l’image ainsi générée doit alors être soigneusement documentée pour comprendre sa configuration. De plus, toute modification ultérieure (par exemple pour des mises à jour, ou simplement pour modifier la page d’accueil) nécessite également des interventions manuelles sur l’image, complexifiant ainsi son historique.

Une seconde technique, plus propre, consiste à écrire dans un fichier les instructions qui permettront de construire le container souhaité. Docker peut alors créer lui-même l’image sur base de ces instructions. Ce fichier s’appelle un “Dockerfile”.

  • Créez un Dockerfile réalisant les mêmes opérations que celles que vous avez effectuées plus tôt, sur base de cet exemple. Enregistrez-le dans un répertoire de travail, en l’appelant Dockerfile.
      FROM nginx:latest
      RUN apt update
      RUN apt install -y nano net-tools
      COPY index.html /usr/share/nginx/html
    
  • Mettez également dans ce répertoire un fichier index.htmlqui sera votre page d’accueil personnalisée.
  • Exécutez ensuite la commande docker build -t <nom de l'image> . dans ce même répertoire. Cela aura pour effet de déclencher la construction d’une nouvelle image, sur base de l’image nginx.
  • Vérifiez ensuite que cette image fonctionne comme prévu.

Exercices récapitulatifs

4.1. Démarrer un serveur Web Apache

Effectuez les mêmes opérations que plus haut, mais cette fois en utilisant Apache au lieu de nginx.

  • Trouvez une image Apache sur le Docker Hub
  • Démarrez un container sur base de cette image et vérifiez qu’il fonctionne sur localhost via votre navigateur.
  • Créez un second virtualhost sur le port 8080.
  • Personnalisez la page d’accueil des deux virtual hosts.
  • Créez un Dockerfile permettant d’obtenir une image avec votre configuration Apache personnalisée.

4.2. Lancer un résolveur Bind dans un container Docker

  • Allez sur le Docker Hub, et voyez s’il existe une image Docker avec bind pré-installé. Téléchargez-la.
  • Démarrez un container sur base de cette image, et faites en sorte de pouvoir l’interroger via le port 53 de l’interface localhost de votre hôte. Attention : rappelez-vous le protocole transport utilisé par le DNS!
  • Testez votre container avec l’outil ad-hoc.
  • Modifiez la configuration de Bind pour transformer votre résolveur en forwarder DNS vers 8.8.8.8. Validez son comportement en utilisant Wireshark.
  • Construisez le Dockerfile permettant d’obtenir une image intégrant cette configuration de Bind.
  1. Quelle configuration avez-vous effectuée au niveau des ports ?
  2. Qu’avez-vous observé dans la trace Wireshark qui prouve que la configuration est correcte? Illustrez avec un screenshot de la capture.

4.3. Container avec script Python

Créez un Dockerfile définissant une image permettant d’exécuter un container dont le rôle est d’afficher l’heure sur stdout au démarrage. C’est la seule opération que ce container effectuera avant de s’arrêter. Voici un exemple de code Python permettant d’obtenir ce résultat.

from datetime import datetime
now = datetime.now()
print(f"Nous sommes le {now.date()} à {now.time()}")