6 juin 2014

Docker - Premiers pas


Sommaire

  • Cycle de vie des images et des conteneurs
  • Dockerfile
  • Créer un fichier Dockerfile
  • Créer une image
  • Démarrer un conteneur
  • Accéder au Shell du conteneur démarré
  • Surcharger la commande lancée au démarrage du conteneur
  • Supprimer un conteneur et son image
  • Commandes groupées
  • Modifier et recréer une image et démarrer son conteneur
  • Démarrer le conteneur en tâche de fond

Pré-requis

Cet article nécessite de disposer d'une machine Linux.

Sous Windows, vous pouvez suivre l'installation rapide suivante : Installation rapide de Docker avec Vagrant et VirtualBox sur Windows

Il faut également avoir lu l'article sur la présentation de Docker : Présentation de Docker

Cycle de vie des images et des conteneurs

Avec Docker, on part d'un fichier Dockerfile pour construire une image de conteneur. Cette image servira à démarrer des conteneurs qui utiliseront le contenu de cette image.
Voici un schéma illustrant le cycle de vie d'un conteneur avec les commandes du client Docker :

Dockerfile

Le fichier Dockerfile décrit comment une image est construite. La commande docker build construit une image à partir de ce fichier Dockerfile.

Syntaxe

Voici la syntaxe du Dockerfile pour décrire l'image et la manière dont elle est créée :
  • MAINTAINER <name>
    • Auteur de l'image
  • FROM <image>
    • nom de l'image de base existante qui sert de base à l'image à créer afin de ne pas partir de rient de tout mais du contenu d'une image existante
    • pour une image fonctionnant sur Ubuntu, l'image de base peut être Ubuntu
  • ENV <key> <value>
    • Définir une variable d'environnement
  • ADD <src> <dest>
    • Ajoute un fichier local dans l'image
  • VOLUME ["/<dir>"]
    • Répertoire monté au démarrage du conteneur qui est externe au conteneur. 
    • Ceci permet de conserver les fichiers après arrêt du conteneur
    • Voir documentation : Sharing Directories using volumes
  • USER <user>
    • Indique l'utilisateur Linux utilisé lors du démarrage du conteneur
  • EXPOSE <port>
    • Indique le port réseau exposé par le conteneur
    • Ceci est utilisé pour définir sur quels ports réseau communiquent les conteneurs entre eux ou vers l'extérieur de la machine hôte de Docker
    • Voir documentation : 
  • RUN <command>
  • RUN ["executable", "param1", "param2"]
    • Exécute la commande sur l'image
  • CMD <command>
  • CMD ["executable", "param1", "param2"]
    • Il ne doit y avoir qu'un seul CMD dans le fichier Dockerfile
    • Il s'agit de la commande qui est lancée à chaque démarrage d'un conteneur basé sur cette image : par exemple, si l'image contient le serveur Tomcat, il peut s'agir de lancer l'exécution du serveur Tomcat au démarrage du conteneur
    • La commande n'est lancée qu'à l'exécution du conteneur et non durant la construction de l'image
    • Cette commande peut être redéfinie dans la commande de lancement du conteneur

Exemple

Voici un exemple simple de fichier Dockerfile :

  • La ligne FROM indique que cette image se base sur l'image officielle ubuntu en version 14.04 disponible sur le registry public de Docker.
  • La commande suivante RUN apt-get update lance la mise à jour des packages Linux.
  • Enfin la dernière ligne CMD définit la commande qui sera lancée au démarrage du conteneur (et non lors de la création de l'image) qui est ici de lancer un shell Bash.

Créer le fichier Dockerfile

Nous pouvons tester la construction de cette image en créant le fichier Dockerfile et en lançant la construction de l'image :
  • Ouvrir un terminal de commandes
  • Se connecter à la VM de Vagrant via la commande :
    vagrant ssh
    • Ne pas définir de passphrase
    • Le mot de passe est : vagrant
  • Passer en utilisateur root :
    sudo su
  • Créer le répertoire image1 :
    mkdir image1
  • Créer le fichier Dockerfile :
    curl -o image1/Dockerfile https://gist.githubusercontent.com/ludo1026/9356580e889c777ec771/raw/df4f61a01f541fcf81f53b91e11516704a212dc8/Dockerfile
  • Vérifier le contenu du fichier Dockerfile via la commande suivante :
  • cat image1/Dockerfile
  • Le fichier Dockerfile doit avoir le contenu suivant :

Créer l'image à partir du fichier Dockerfile

  • Lancer la commande de création de l'image en indiquant le nom du répertoire contenant le fichier Dockerfile, ici image1:
    docker build -t image1 image1
    • L'option -t image1 n'est pas obligatoire : elle permet de nommer et de tagguer l'image dans le registry afin de la retrouver facilement
    • Docker télécharge l'image de base Ubuntu 14.04
    • Il lance ensuite la commande RUN pour mettre à jour les packages Linux dans l'image
    • Docker stocke l'image en local
  • Dans la sortie console de la commande docker build, la ligne suivante indique l'identifiant de l'image qui vient d'être créée :
    Successfully built <identifiant>
  • L'identifiant de l'image, exemple: 231fc2fcbf5a, est à utiliser dans les commandes docker pour référencer l'image
  • Lancer la commande pour visualiser les images disponibles en local :
    docker images
    • Le nom du tag image1 permet de repérer l'image qui vient d'être créée
    • L'image de base ubuntu déclarée dans le FROM du fichier Dockerfile a aussi été téléchargée et visible dans la liste des images du registry local de Docker
    • La colonne IMAGE ID contient l'identifiant de l'image

Démarrer un conteneur à partir de l'image

Maintenant que nous venons de créer l'image, nous pouvons lancer l'exécution d'un nouveau conteneur à partir de cette image :
  • Lancer la commande suivante pour démarrer un conteneur à partir de l'image que nous venons de créer en indiquant l'identifiant de l'image qui a été récupéré via la commande docker images (colonne IMAGE ID) :
    docker run <identifiant>
    • La sortie console de cette commande affiche "Hello !" : 
      • il s'agit de la commande echo "Hello !" définie par la ligne CMD à la fin du fichier Dockerfile et qui a été exécuté par Docker au démarrage du conteneur
    • Cette commande ne lance pas de processus qui tourne en permanence ou en tâche de fond, c'est pourquoi Docker arrête l'exécution du conteneur.
  • Lancer la commande suivante pour voir la liste des processus en cours d'exécution :
    docker ps
    • Le conteneur qui vient d'être exécuté n'est pas visible
  • Lancer la commande suivante pour voir la liste de tous les processus, même ceux arrêtés :
    docker ps -a
    • Le conteneur qui vient d'être exécuté est indiqué comme arrêté :
      STATUS : Exit 0
    • La colonne CONTAINER ID contient l'identifiant du conteneur
    • La colonne IMAGE contient l'identifiant de l'image
Ainsi pour qu'un conteneur continue de s'exécuter, il faut qu'il y ait au moins un processus qui tourne en tâche de fond dans celui-ci. Si aucun processus ne tourne dans le conteneur, ce conteneur est alors arrêté automatiquement par Docker.

Accéder au shell du conteneur démarré

Au démarrage du conteneur, il est possible d'ouvrir une session Shell sur ce conteneur.

Il faut ajouter les options -i et -t à la commande docker run.
docker run -i -t <identifiant>

  • Lancer la commande suivante :
    docker run -i -t image1
    • Nous accédons au shell du conteneur
    • Le conteneur reste en cours d'exécution même s'il n'y a pas de processus qui tourne dans ce conteneur
    • Nous pouvons lancer des processus ou apporter des modifications au conteneur via ce shell

Surcharger la commande lancée au démarrage du conteneur

La commande docker run permet de surcharger la commande lancée au démarrage du conteneur en indiquant la commande shell à lancer à la fin de la commande docker run.
docker run <identifiant> <commande shell>
  • Démarrer un conteneur en utilisant la commande echo Bonjour qui sera utilisée au démarrage du conteneur à la place de la commande echo "Hello !" contenue dans l'image :
  • docker run <identifiant> echo Bonjour
    • La console affiche "Bonjour" au lieu d'afficher "Hello !"
On pourrait imaginer que cela permette de lancer un script personnalisé au démarrage d'un nouveau conteneur pour que son comportement soit différent d'un autre conteneur lancé à partir de la même image.

Supprimer l'image et le conteneur

Nous devons supprimer le conteneur avant l'image, sinon docker affiche un message d'erreur. Ce message contient les identifiants des conteneurs qui utilisent cette image.

Supprimer le conteneur

Note : Un conteneur ne peut être supprimé s'il est en cours d'exécution.
  • Pour supprimer le conteneur, il faut lancer la commande suivante pour récupérer l'identifiant du conteneur :
    docker ps -a
    • La colonne CONTAINER ID contient l'identifiant du conteneur
    • La colonne IMAGE contient le nom de l'image
  • Lancer la commande de suppression du conteneur :
    docker rm <identifiant>
  • Vérifier que le conteneur n'existe plus via la commande :
    docker ps -a

Supprimer l'image

  • Pour supprimer l'image, il faut lancer la commande suivante pour récupérer l'identifiant de l'image :
    docker images
    • La colonne IMAGE ID contient l'identifiant de l'image
  • Lancer la commande de suppression de l'image :
    docker rmi <identifiant>
  • Vérifier que l'image n'existe plus via la commande :
    docker images

Commandes groupées

Il est également possible d'arrêter et de supprimer plusieurs conteneurs et plusieurs images en une seule commande :

Arrêter

  • Arrêter tous les conteneurs :
    docker ps|sed "1 d"|awk '{print $1}'|xargs docker stop
  • Arrêter les conteneurs d'une même image (ici "image1"):
    docker ps|sed "1 d"|grep "image1"|awk '{print $1}'|xargs docker stop

Supprimer

  • Supprimer tous les conteneurs :
    docker ps -a|sed "1 d"|awk '{print $1}'|xargs docker rm
  • Supprimer les conteneurs d'une même image (ici "image1"):
    docker ps -a|sed "1 d"|grep "image1"|awk '{print $1}'|xargs docker rm
  • Supprimer les conteneurs arrêtés :
    docker ps -a|sed "1 d"|grep "Exit"|awk '{print $1}'|xargs docker rm
  • Supprimer toutes les images :
    docker images|sed "1 d"|awk '{print $3}'|sort|uniq|xargs docker rmi
  • Supprimer les images selon leur nom (ici "image1") :
    docker images|sed "1 d"|grep "image1"|awk '{print $3}'|sort|uniq|xargs docker rmi
  • Supprimer les images qui n'ont pas été tagguées (valeur "<none>") :
    docker images|sed "1 d"|grep \<none\>|awk '{print $3}'|sort|uniq|xargs docker rmi

Recréer l'image et démarrer un conteneur

Maintenant que les conteneurs et l'image image1 n'existent plus, nous pouvons créer une image qui lancera un processus qui ne s'arrête pas.

Pour cela, nous modifions le fichier Dockerfile afin d'y définir le processus qui ne s'arrête pas :
  • Ouvrir le fichier Dockerfile :
    nano image1/Dockerfile
  • Remplacer la dernière ligne par celle-ci :
    CMD while true; do ps -aux; sleep 2; done
  • Supprimer la ligne qui effectue la mise à jour des packages Linux :
    RUN apt-get update
  • Lancer la création de l'image :
    docker build -t image1 image1
  • Récupérer l'identifiant de cette image via la commande :
    docker images
  • Lancer l'exécution du conteneur à partir de cette image :
    docker run <identifiant de l'image>
La commande ps -aux est lancée toutes les 2 secondes sur le conteneur.

La liste des processus du conteneur est affichée sur la console de la machine hôte.

Cependant, la commande docker run ne lance pas l'exécution du conteneur en tâche de fond :
  • La liste des processus du conteneur s'affiche toutes les 2 secondes sur la sortie console de la machine hôte
  • Faire CTRL + C pour arrêter l'exécution du conteneur
  • La commande docker ps -a indique que le conteneur est arrêté

Exécuter le conteneur en tâche de fond (mode détaché)


Pour lancer le conteneur en tâche de fond, lancer la commande docker run avec l'option -d :
  • Lancer l'exécution de notre conteneur en mode détaché :
    docker run -d <identifiant>
    • le conteneur tourne en tâche de fond via le démon de Docker
    • aucune sortie console n'est affichée
  • Afficher les conteneurs en cours d'exécution :
    docker ps
    • Notre conteneur est dans la liste des conteneurs en cours d'exécution
    • La colonne STATUS indique que le conteneur est en cours d'exécution depuis 20 secondes : Up 20 seconds
  • Le processus lancé dans le conteneur est visible via la commande ps -aux depuis la machine hôte :
  • Nous pouvons afficher la sortie console du conteneur sur la sortie console de la machine hôte :
    docker logs <identifiant>

Arrêter le conteneur

  • Lister les conteneurs en cours d'exécution :
    docker ps
  • Arrêter le conteneur :
    docker stop <identifiant>
  • Il est possible de relancer l'exécution du conteneur qui vient d'être arrêté via la commande :
    docker start <identifiant>
Il est nécessaire d'arrêter le conteneur avant de le supprimer.

Conclusion

L'objectif de cet article était de s'entraîner avec la création d'images et l'exécution des conteneurs.

Nous verrons dans un prochain article comment utiliser des images fournies par la communauté pour monter rapidement des serveurs Tomcat et des base de données.

Référence