Docker est une technologie qui s’est faite connaître il y a un peu plus de deux ans. Si elle est aujourd’hui aussi renommée, c’est parce qu’elle a totalement révolutionné le domaine de l’émulation de systèmes d’exploitation en rendant cela presque anodin et indolore.

Pour résumer grossièrement, Docker vous permet de créer des « containers », sortes de systèmes d’exploitation légers et jetables, contenant tout ce dont vous aurez besoin, et que vous pourrez manipuler à votre guise, multiplier, supprimer, et relancer, grâce à de simples commandes. Bien que ces containers aient les mêmes possibilités que l’environnement qu’ils émulent, ils ressemblent plus à des bacs-à-sable sur lesquels vous pouvez faire ce que vous souhaitez sans endommager votre machine hôte.

Parmi les nombreux avantages de Docker que nous pouvons citer, il y a notamment le fait que cela fonctionne complètement indépendamment de l’hôte et de son système d’exploitation.

 

L’architecture Docker

Pour bien comprendre l’architecture d’une technologie, rien ne vaut un bon vieux schéma :

Architecture Docker
Le bon vieux schéma

Vous voyez ici mentionné le mot « container » dont j’ai déjà parlé tout à l’heure, et non loin de celui-ci, le mot « image ».
Ces deux concepts sont les principaux à intégrer pour prendre en main Docker. C’est donc dans le prochain paragraphe que tout va se jouer, pour bien vous faire comprendre ces deux notions.

Je me lance :

  • Pour ceux qui connaissent la programmation orientée objet : une image Docker est une classe, et un container Docker est son instanciation.
  • Pour ceux qui savent cuisiner : une image Docker est une recette, et un container Docker est le gâteau que vous allez mettre au four en ayant suivi la recette.
  • Pour les autres : il existe de nombreux sites pour apprendre les rudiments de la cuisine, n’hésitez pas à y faire un petit tour et revenir ici ensuite.

Lorsque l’on souhaite « dockeriser » quelque chose, on commence donc tout d’abord par créer une image (docker build), puis on la lance (docker run).

Un des avantages de « docker run » est que vous n’avez pas besoin d’avoir l’image sur votre poste pour lancer la commande.
Explications : vous avez vu à droite sur le schéma le concept de « registry ». Il s’agit en fait d’un dépot d’images en ligne, que vous allez pouvoir récupérer ; le plus important des registries étant celui de Docker lui-même, le Docker Hub. Lorsque vous lancez une commande « run » sur une image, Docker va d’abord chercher l’image sur votre poste ; s’il ne la trouve pas, il ira alors la chercher sur le registry.

Pour ce qui est de créer une image, deux choix s’offrent à vous :

  • La méthode « pas terrible » : récupérer une image, en instancier un container avec « docker run », faire des modifications dessus, et faire un « docker commit » pour appliquer les modifications du container à l’image. Ceci ne sera pas traité dans cet article.
  • La méthode « bien mieux » : utiliser un Dockerfile.

Lors de ma précédente analogie avec le domaine culinaire, j’ai parlé de recette. En fait, le terme recette s’applique littéralement au Dockerfile, plutôt qu’à l’image elle-même.
Le Dockerfile est effectivement une recette d’image : vous allez y décrire tout ce que vous souhaitez avoir dans votre image.

Prenons un exemple tout simple : je souhaite avoir un environnement de développement en Python, installable rapidement n’importe où, pour faire de petites expérimentations rapides.

Voici un Dockerfile qui me permettra de l’avoir très vite :

FROM ubuntu:14.04
MAINTAINER RayBor "ray@frugalprototype.com"
RUN apt-get update -y && apt-get install -y python-pip python-dev build-essential

Explications : ce Dockerfile décrit une image très simpliste mais contenant le strict nécessaire pour développer en Python. Sur la première ligne, « FROM » désigne l’image sur laquelle on se base pour créer la nôtre ; il s’agit ici d’un Linux Ubuntu basique. À la compilation, Docker ira donc chercher cette image en ligne (si elle n’est pas déjà disponible sur votre machine) et y rajoutera ce qui est décrit les lignes suivantes : un « apt-get update » pour pouvoir récupérer des paquets, puis l’installation de python, de pip, etc.

ray@frugalprototype ~/docker $ mkdir python_env && cd python_env    #Je crée mon dossier de projet

ray@frugalprototype ~/docker/python_env $ vi Dockerfile    #Je crée le fichier Dockerfile et j'écris dedans les lignes décrites plus haut

ray@frugalprototype ~/docker/python_env $ docker build -t raybor/python-env .   #Je lance la compilation de l'image
Sending build context to Docker daemon 2.048 kB
Sending build context to Docker daemon 
Step 0 : FROM ubuntu:14.04
 ---> 87d8afb341b5
Step 1 : MAINTAINER RayBor "ray@frugalprototype.com"
 ---> Running in ba06366bb4f8
 ---> 957a9de54c8f
Removing intermediate container ba06366bb4f8
Step 2 : RUN apt-get update -y && apt-get install -y python-pip python-dev build-essential
 ---> Running in 5d0e055baba2
[...]
 ---> 8767cc312694
Removing intermediate container 5d0e055baba2
Successfully built 8767cc312694

Je vous ai épargné ici, grâce aux « […] », les nombreuses lignes décrivant la mise à jour et l’installation des paquets. Sachez simplement que la compilation a pris en tout une petite minute.
Mon image est maintenant prête, je peux la lancer grâce à la commande suivante :

ray@frugalprototype ~/docker/python_env $ docker run -it raybor/python-env bash

root@03ef4fdd59f2:/#

Dans ce cas particulier, l’ajout de « bash » à la fin permet de lancer le container et d’entrer dedans, afin de pouvoir utiliser l’environnement de développement.
Cet ajout n’est pas systématique, bon nombre d’images Docker étant faites pour fonctionner toutes seules, on les lance simplement sans rentrer dedans.

On a donc fait un petit tour pratique de Docker, ce qui fera office d’introduction. Pour installer Docker et apprendre à l’utiliser pleinement, je vous recommande le site officiel de Docker.

Et le prototypage, dans tout ça ?

Maintenant que vous avez compris le fonctionnement de Docker, on va pouvoir passer au véritable sujet de l’article : comment utiliser Docker pour développer ses prototypes frugaux ?

Si je reprends l’exemple précédent, nous avons créé une image, basée sur un environnement Ubuntu, prête pour développer et lancer des scripts Python.

Ce que je n’ai pas encore dit, mais que peut-être vous savez déjà, c’est qu’un container Docker n’est pas fait pour subsister. C’est à dire que ce qui est fait dedans (dans notre cas l’écriture de script) n’est pas gardé lorsque l’on met fin à ce container. On peut alors comprendre qu’il ne s’agit donc pas d’une bonne solution pour développer et conserver ses scripts. En revanche, cela peut être très pratique si l’on souhaite tester un script dans un environnement réaliste sans pour autant risquer d’infliger des dégâts à son ordinateur.

L’apprentissage par l’exemple

Recentrons-nous donc sur le principal : la réalisation de prototypes rapides et peu coûteux. Imaginons que suite à la lecture des articles d’Ali Benfattoum sur le choix de protocoles pour l’IoT et de Romain Tribout sur l’installation de votre propre broker MQTT, vous ayez décidé de vous lancer et de réaliser votre propre prototype, incluant un broker MQTT.

Et bien plutôt que de devoir relire l’article de M. Tribout pour bien suivre les étapes, je vous propose de faire la même chose en une ligne dans votre terminal :

docker run -ti -p 1883:1883 -p 9001:9001 toke/mosquitto

Si vous avez suivi, cette commande permet donc de lancer l’image nommée « mosquitto », créée par un certain «toke». Une fois cette commande exécutée, vous aurez alors un broker MQTT fonctionnel sur votre machine, et disponible sur votre réseau sur les ports 1883 (applications) et 9001 (websockets) de votre machine.
Le paramètre « -p » de la commande « docker run » permet en effet de mapper le port du container sur le port de la machine hôte, le rendant ainsi accessible à l’extérieur.

Maintenant, chaque fois que vous aurez besoin d’installer un broker MQTT, vous pourrez lancer cette commande sur la machine concernée et obtenir le service en quelques dizaines de secondes.
Avouez que c’est bien pratique, non ?

docker
Image provenant de https://bloglaurel.com/

Mais allons plus loin. Utiliser les images pré-compilées disponibles sur Internet est très pratique. Cependant, il viendra un moment où vous aurez un petit besoin spécifique, qui vous demandera de modifier l’image d’un internaute. Autant donc prendre tout de suite le taureau par les cornes, et modifier notre image.

Modifiez votre première image Docker

Supposons que vous souhaitiez changer le port sur lequel les websockets MQTT seront disponibles (ils permettent de communiquer en MQTT via une application web), pour le passer du 9001 actuel au 61614. Celui-ci étant paramétré à l’intérieur d’un fichier de configuration, nous allons donc récupérer l’image avant sa compilation par Monsieur Toke.

Je vous propose pour cela d’utiliser git, puisque les fichiers sont disponibles sur GitHub. Si vous n’avez pas envie d’utiliser git, vous pouvez simplement cliquer sur « Clone or download » puis « Download ZIP », et récupérer ainsi puis extraire l’archive contenant le projet.

Si comme moi vous êtes amoureux de git et que vous ne ratez pas une occasion de l’utiliser, vous pouvez lancer la commande suivante (en supposant que vous ayez bien configuré votre git) :

ray@frugalprototype ~/git $ git clone git@github.com:toke/docker-mosquitto.git
Cloning into 'docker-mosquitto'...
remote: Counting objects: 156, done.
remote: Compressing objects: 100% (6/6), done.
remote: Total 156 (delta 1), reused 0 (delta 0), pack-reused 150
Receiving objects: 100% (156/156), 31.12 KiB | 0 bytes/s, done.
Resolving deltas: 100% (66/66), done.
Checking connectivity... done.

Que vous ayez cloné le repository ou téléchargé l’archive, vous voilà maintenant en possession des fichiers vous permettant de compiler vous-même votre image Docker de broker MQTT Mosquitto.

Regardons donc ce que nous avons récupéré :

ray@frugalprototype ~/git $ cd docker-mosquitto && ls 
config docker-entrypoint.sh Dockerfile LICENSE README.md

Dans un ordre totalement aléatoire, nous y trouvons donc :

  • les fichiers LICENSE et README.md qui vont généralement de paire avec un repository git
  • le fameux Dockerfile, on y reviendra
  • un fichier docker-entrypoint.sh, qui fait partie de bonnes pratiques Docker, mais dépassant le cadre de ce tutoriel
  • un dossier config, qui nous intéresse

Examinons donc le dossier config :

ray@frugalprototype ~/git/docker-mosquitto $ ls config
ca_certificates certs conf.d mosquitto.conf mosquitto.conf.example

Nous y trouvons ici le fichier « mosquitto.conf ». Celui-ci semble tout indiqué pour configurer les ports qui nous intéressent.

ray@frugalprototype ~/git/docker-mosquitto $ cat config/mosquitto.conf
# Place your local configuration in /mqtt/config/conf.d/

pid_file /var/run/mosquitto.pid

persistence true
persistence_location /mqtt/data/

user mosquitto

# Port to use for the default listener.
port 1883


log_dest file /mqtt/log/mosquitto.log
log_dest stdout

include_dir /mqtt/config/conf.d

On y voit donc mentionné le port 1883, mais pas le 9001. Celui-ci doit donc être inclus le dossier « /mqtt/config/conf.d », dont il est fait mention en bas du fichier.
Attention, il s’agit d’un chemin à l’intérieur du futur container, puisque ce fichier de configuration est fait pour fonctionner à l’intérieur dudit container.
Une des petites difficultés lorsque l’on commence à créer ses propres images Docker est de bien faire la différence entre ce qui concerne la création de l’image, et le fonctionnement une fois le container lancé.

Ici, nous devons donc trouver d’où provient le dossier « /mqtt/config/conf.d » une fois sur le container, et pour cela, nous allons avoir besoin du Dockerfile.

ray@frugalprototype ~/git/docker-mosquitto $ cat Dockerfile
FROM debian:jessie

MAINTAINER Thomas Kerpe 

RUN apt-get update && apt-get install -y wget && \
 wget -q -O - https://repo.mosquitto.org/debian/mosquitto-repo.gpg.key | gpg --import && \
 gpg -a --export 8277CCB49EC5B595F2D2C71361611AE430993623 | apt-key add - && \
 wget -q -O /etc/apt/sources.list.d/mosquitto-jessie.list https://repo.mosquitto.org/debian/mosquitto-jessie.list && \
 apt-get update && apt-get install -y mosquitto && \
 adduser --system --disabled-password --disabled-login mosquitto

RUN mkdir -p /mqtt/config /mqtt/data /mqtt/log
COPY config /mqtt/config
RUN chown -R mosquitto:mosquitto /mqtt
VOLUME ["/mqtt/config", "/mqtt/data", "/mqtt/log"]

EXPOSE 1883 9001

ADD docker-entrypoint.sh /usr/bin/

ENTRYPOINT ["docker-entrypoint.sh"]
CMD ["/usr/sbin/mosquitto", "-c", "/mqtt/config/mosquitto.conf"]

Vous voyez donc ici tout ce qu’il faut faire pour partir d’une image Debian de base pour obtenir une image proposant un broker MQTT.

On peut déjà voir le port 9001 mentionné :

EXPOSE 1883 9001

Il s’agit de la ligne qui permet d’ouvrir le port du container. Changeons le tout de suite par 61614 :

EXPOSE 1883 61614

Puis intéressons-nous à la ligne suivante :

COPY config /mqtt/config

Celle-ci signifie qu’à la compilation de notre image, le dossier « config » (on parle bien du dossier ~/git/docker-mosquitto/config sur ma machine) sera copié à l’intérieur de l’image, dans « /mqtt/config ».

Cela vous rappelle-t-il quelque chose ? Nous cherchions d’où pouvait provenir « /mqtt/config/conf.d » ; nous pouvons donc maintenant déduire que notre dossier « conf.d » se trouve dans le dossier « config » de la machine.

Cela paraîtra évident au plus perspicace, mais ne l’est pas forcément : rien n’oblige à conserver la même arborescence lorsque l’on copie des fichiers vers une image Docker.

Regardons donc dans notre dossier config/conf.d :

ray@frugalprototype ~/git/docker-mosquitto $ ls config/conf.d/
README websockets.conf

ray@frugalprototype ~/git/docker-mosquitto $ cat config/conf.d/websockets.conf 
listener 9001
protocol websockets

Nous l’avons trouvé ! Le fichier config/conf.d/websockets.conf contient le paramètre définissant le port sur lequel seront disponibles les websockets.
Éditons donc la ligne concernée dans le fichier, et remplaçons 9001 par 61614.

ray@frugalprototype ~/git/docker-mosquitto $ vi config/conf.d/websockets.conf

Nous pouvons maintenant tenter de compiler et lancer l’image pour voir si cela fonctionne :

ray@frugalprototype ~/git/docker-mosquitto $ docker build -t raybor/docker-mosquitto .
[...]
Successfully built b288ad6db7af

ray@frugalprototype ~/git/docker-mosquitto $ docker run -it -p 1883:1883 -p 61614:61614 raybor/docker-mosquitto
1470409429: mosquitto version 1.4.9 (build date Fri, 03 Jun 2016 09:02:12 +0100) starting
1470409429: Config loaded from /mqtt/config/mosquitto.conf.
1470409429: Opening websockets listen socket on port 61614.
1470409429: Opening ipv4 listen socket on port 1883.
1470409429: Opening ipv6 listen socket on port 1883.

Le container est correctement lancé et Mosquitto semble démarré avec les bons paramètres. Testons donc cela !

Sur votre navigateur web préféré, allez sur l’URL : http://mqtt.frugalprototype.com
Renseignez les champs « Host : localhost » et « Port : 61614 » et cliquez sur « Connect ». Vous devriez maintenant être connecté à votre broker MQTT.

Retournez sur le terminal sur lequel vous avez lancé votre container ; une nouvelle ligne est apparue :

1470409637: New client connected from 172.17.42.1 as clientId-TaTGd61vq4 (c1, k60).

Voilà ! Vous avez correctement modifié, compilé, et lancé votre première image Docker ! Félicitations.

Notez que l’adresse IP 172.17.42.1 correspond à votre machine, vue depuis le container Docker. Il s’agit typiquement du pont réseau créé entre le container et son hôte.

Vous avez maintenant une image Docker de broker MQTT personnalisée pour vos besoins, que vous pourrez utiliser à chaque fois que vous le souhaitez.
Pour pouvoir la mettre en ligne, vous pouvez suivre cet article officiel Docker.

 

Pour aller plus loin

Un des points importants lorsque l’on utilise Docker est de trouver le juste milieu entre « récupérer les images créées par d’autres », et « créer ses propres images ».
Nous sommes ici dans une démarche frugale, donc n’hésitez pas à bien regarder sur Internet si quelqu’un n’a pas déjà créé l’image que vous souhaitez avant de tenter de la construire vous-même : vous pourrez de cette façon gagner beaucoup de temps.

Si le sujet vous intéresse, je vous invite à vous pencher davantage sur les possibilités de Docker. Vous découvrirez ainsi tout le potentiel de cette technologie.
Entre grandes entreprises et simples prototypeurs, nombreux sont ceux à déjà utiliser Docker en environnement de production.

Par ailleurs, ne vous étonnez pas si vous en entendez à nouveau parler dans les prochains articles sur ce site !

Si des erreurs se sont glissées dans l’article, n’hésitez pas à nous en faire part.

À bientôt sur Frugal Prototype !


Raymond Borenstein

Spécialisé dans les applications et objets connectés, j'aime faire communiquer ensemble tout ce que je peux.

All author posts
Related Posts

Privacy Preference Center