Lors de mon précédent article sur Docker, j’ai essayé de vous montrer l’intérêt que peut avoir cette technologie dans le développement de vos prototypes et projets frugaux. Souhaitant continuer sur cette lancée, j’ai décidé de rentrer un peu plus dans le vif du sujet, et vous propose de réaliser cette fois-ci un « squelette » d’image Docker.
Un squelette ? Mais quelle idée morbide !
Rien de mortuaire ici, je parle simplement de squelette par opposition à une image finie. En effet, une image au sens Docker du terme consiste en un projet fini, prêt à être employé et décliné en container.
Notre squelette ne sera donc pas une image finie, mais un modèle d’image, que vous pourrez adapter à tous vos besoins. Ici, le but de ce squelette sera de lancer automatiquement un script au démarrage du container et, petit bonus, pouvoir monitorer son exécution.
Grâce à votre nouvel ami le squelette, chaque fois que vous aurez besoin de dockeriser un script, vous n’aurez qu’à faire un simple copier-coller du projet, y insérer votre script, et lancer votre container.
Cependant, je vous montrerai dans ce tutoriel uniquement comment dockeriser un script Python ; selon le langage utilisé, il faudra peut-être procéder à quelques adaptations, si vous ne voulez pas tomber sur un os. Mais n’anticipons pas trop vite, et commençons toute suite la réalisation.
Des membres aux membres
Tout projet Docker s’articulant autour d’un Dockerfile, nous commencerons bien sûr par là. Je vous propose donc de créer le dossier dans lequel nous allons travailler, que nous nommerons par exemple « python_script_docker ». Oui, je ne suis pas fracassé le crâne pour trouver un nom, mais au moins on sait tout de suite ce que cela contient. Créez maintenant votre fichier nommé Dockerfile à l’intérieur du dossier.
Nous allons maintenant, couche par couche décrire ce dont nous avons besoin. Commençons donc par l’image sur laquelle nous nous baserons :
FROM ubuntu:14.04
Bien, ce fut intensif. Je vous propose donc une petite pause pour vous détailler un peu la suite. Je vous ai parlé de lancer et monitorer un script. Toujours dans une démarche frugale, nous éviterons donc de nous fêler une côte en redéveloppant un outil de monitoring, quand nous pouvons simplement en trouver un tout fait.
Nous utiliserons donc un outil léger et facile à prendre en main nommé Monit. Doté d’une interface simpliste mais tout de même efficace, il vous permet de suivre, stopper, et relancer l’exécution de scripts depuis une page web.
Nous allons donc rajouter dans notre Dockerfile la ligne suivante, qui inclut les outils dont nous aurons besoin pour installer Monit :
RUN apt-get update && apt-get install -y wget tar
Pour l’instant, rien d’inconnu donc ; je vous propose cependant d’utiliser une nouvelle commande Docker :
ENV MONIT_VERSION 5.18
Comme vous l’aurez peut-être deviné, ENV vous permet de déclarer une constante dont nous nous servirons dans le Dockerfile pour représenter le numéro de version de Monit. Nous aurions pu faire sans, mais cela nous permettra de changer facilement de version lorsque nous le souhaiterons. On va donc rajouter deux commandes pour pouvoir télécharger Monit et l’installer :
RUN wget -O /tmp/monit-$MONIT_VERSION-linux-x64.tar.gz http://mmonit.com/monit/dist/binary/$MONIT_VERSION/monit-$MONIT_VERSION-linux-x64.tar.gz RUN cd /tmp && tar -xzf /tmp/monit-$MONIT_VERSION-linux-x64.tar.gz && cp /tmp/monit-$MONIT_VERSION/bin/monit /usr/bin/monit
Grâce à ces deux lignes, Monit sera installé dans le dossier « /usr/bin/monit » de notre future image. Intéressons-nous donc maintenant à la configuration de Monit.
Ossature d’une configuration Monit
Je vous ai brièvement expliqué le fonctionnement de Monit. On va donc rentrer un peu plus dans le détail, en analysant rapidement l’interface graphique du logiciel :
Sur cet exemple d’interface de Monit, vous pouvez voir la liste des processus qui sont monitorés par le programme, ainsi que leur état, leur durée d’exécution, etc. En cliquant sur un des processus, vous pourrez obtenir plus de détails dessus, et l’arrêter ou le relancer. Comme je vous l’avais promis, Monit est un outil très simple d’utilisation, et largement suffisant pour la plupart des besoins de monitoring.
Comme tout bon logiciel, Monit utilise un fichier de configuration, appelé « monitrc ». Créez donc ce fichier dans la racine du dossier du projet, et éditez-le avec les lignes ci-dessous.
set daemon 30 //Fréquence de vérification l'état des processus monitorés, en secondes set logfile /var/log/monit.log //Emplacement du fichier de logs set httpd port 2812 //Le port sur lequel on accèdera à l'interface de Monit allow admin:monit //Les identifiants de connexion à l'interface include /etc/monit/conf.d/* //Inclusion d'un dossier contenant les configurations de monitoring des différents scripts/services
Ceci est relativement simple, mais j’ai néanmoins détaillé ces lignes une par une. N’oubliez pas de retirer les commentaires lors de la copie, sinon cela risque de ne pas fonctionner !
On a maintenant une configuration basique au possible, mais qui ne se trouve pour le moment que dans le dossier projet : il ne reste donc plus qu’à la copier dans l’image.
Ajoutez donc les lignes suivantes à votre Dockerfile :
COPY ./monitrc /etc/monitrc RUN chmod 0700 /etc/monitrc
Le script, c’est chic !
Maintenant que Monit est configuré, nous allons pouvoir lui fournir un script à monitorer. Comme je l’écrivais en début d’article, je parlerai ici d’un script Python ; n’hésitez cependant pas à utiliser ce que vous souhaitez, en procédant aux changements qui s’imposent.
Nous prendrons ici un petit script permettant d’envoyer en boucle un message MQTT :
import paho.mqtt.client as mqtt import time username = "un lecteur" # Mettez ici votre nom mqttc = mqtt.Client(username) mqttc.connect("mqtt.frugalprototype.com", 1883, 60) while mqttc.loop() == 0: mqttc.publish("frugal/tutorial","Bonjour, je suis " + username + " et j'aime FrugalPrototype.com") time.sleep(600)
Si tout fonctionne correctement, au lancement de notre container, ce script sera lancé et monitoré par Monit, et enverra toutes les 10 minutes un message MQTT sur mqtt.frugalprototype.com. Ceci peut servir à vérifier qu’il a bien été lancé.
Créez donc un repertoire « scripts » dans votre dossier de projet, et à l’intérieur, le fichier « main.py », dans lequel vous insérez le script sus-décrit.
Retournez maintenant sur votre Dockerfile, et ajoutez-y les lignes suivantes :
RUN mkdir scripts COPY scripts /scripts
Ceci créera donc le dossier /scripts dans le container, et y copiera le contenu du dossier scripts de votre projet.
J’ai ici fait exprès de prendre un script utilisant un module python externe : paho-mqtt. Il va donc falloir que notre container installe ce module pour pouvoir l’utiliser ; c’est un très bon moyen pour découvrir Pip.
Pip est un utilitaire Python très pratique qui permet d’installer facilement de nouveau modules. Il s’utilise comme suit :
pip install nom-du-module
Une autre manière d’utiliser Pip, que nous allons utiliser dans notre cas, est de se servir de fichiers pour lister l’ensemble des modules à installer.
Dans votre dossiers scripts, créez donc le fichier « requirements.txt », et dedans, écrivez simplement « paho-mqtt », et sauvegardez.
Puis, dans votre Dockerfile :
RUN pip install -r /scripts/requirements.txt
Au lancement du container Docker, Pip s’occupera donc d’installer tous les modules Pythons que vous aurez listé dans ce fichier.
Wrapper sur la ville
Comme je vous sens déjà sur les rotules, je vais essayer de rester concis dans ce paragraphe, qui aborde un sujet un peu complexe.
Pour monitorer un processus, Monit se sert de fichiers nommés « PIDfiles ». Ce sont des fichiers dans lesquels sont inscrits les « numéros » de chaque processus en cours sur la machine.
Le problème avec les scripts est qu’il ne fonctionnent pas avec ces PIDfiles, et ne peuvent donc pas être monitorés par défaut.
On va donc ici se servir d’un « wrapper », ou « emballage », qui s’occupera pour nous de créer ce PIDfile. On transformera ainsi notre simple script en quelque chose qui s’apparente à un « daemon » ou service.
À la racine du projet, créez le fichier « mqtt_publisher », notre futur wrapper, et collez ces lignes à l’intérieur :
#!/bin/bash PIDFILE=/var/run/mqtt_publisher.pid case $1 in start) # Launch your program as a detached process python /scripts/main.py &>/dev/null & # Get its PID and store it echo $! > ${PIDFILE} ;; stop) kill `cat ${PIDFILE}` # Now that it's killed, don't forget to remove the PID file rm ${PIDFILE} ;; *) echo "Usage: mqtt_publisher {start|stop}" ;; esac exit 0
Dans le Dockerfile, entrez les deux lignes ci-dessous, pour placer le wrapper dans le dossier des services (init.d) et le rendre exécutable.
COPY mqtt_publisher /etc/init.d/ RUN chmod +x /etc/init.d/mqtt_publisher
Grâce à ce wrapper, vous pourrez maintenant lancer votre script grâce à la commande « mqtt_publisher start ». C’est d’ailleurs ce que Monit fera lui-même.
Si vous vous souvenez, lors de la création du fichier monitrc précédemment, nous avions ajouté la ligne suivante :
include /etc/monit/conf.d/*
Cette ligne indique à Monit le répertoire dans lequel il trouvera quels scripts il doit monitorer, et leurs configurations.
Nous allons donc créer ce dossier et son contenu dans notre dossier de projet, et l’envoyer dans l’image Docker grâce au Dockerfile.
Créez donc le dossier « conf.d » à la racine de votre projet, et à l’intérieur, un fichier nommé « mqtt_publisher », dans lequel vous entrez ces lignes :
check process mqtt_publisher with pidfile /var/run/mqtt_publisher.pid start program = "/etc/init.d/mqtt_publisher start" stop program = "/etc/init.d/mqtt_publisher stop"
Vous vous en doutez, c’est ici qu’est faite la liaison avec le wrapper que nous avons écrit plus tôt : Monit se servira de ce fichier pour trouver le PIDfile de notre script, ainsi que les commandes pour le lancer et le stopper.
Encore une fois la même manipulation, on ajoute donc une ligne au Dockerfile pour copier le contenu de notre dossier conf.d au dossier /etc/monit/conf.d :
COPY ./conf.d /etc/monit/conf.d
Et voilà ! Au lancement de Monit, notre script Python devrait être exécuté et monitoré.
Le Docker sur la main
Pour finaliser notre projet, il ne nous reste plus que quelques lignes à ajouter à notre Dockerfile, mais pas des moindres.
Tout d’abord, nous avons utilisé plusieurs outils dans les différentes commandes du fichier, et pour ne pas avoir à revenir là-dessus à chaque fois, j’ai préféré attendre la fin de ce tutoriel pour écrire, en une seule fois, la ligne permettant de les installer ; la voici donc :
RUN apt-get update && apt-get install -y wget \ tar \ build-essential \ ca-certificates \ gcc \ git \ libpq-dev \ make \ python-pip \ python2.7 \ python2.7-dev \ && apt-get autoremove \ && apt-get clean
Ces outils étant utilisés tout au long du Dockerfile, il faut bien penser à les installer au début du fichier, soit à la troisième ligne par exemple ; sinon, vous risquez d’obtenir quelques erreurs !
Enfin, il ne reste plus qu’à rendre disponible le port utilisé par Monit (2812, comme défini dans le monitrc), et bien entendu, lancer Monit au démarrage du container :
EXPOSE 2812 CMD monit -I
Ceci est naturellement à écrire à la toute fin du Dockerfile.
Il devrait donc ressembler maintenant à ceci :
FROM ubuntu:14.04 ENV MONIT_VERSION 5.11 RUN apt-get update && apt-get install -y wget \ tar \ build-essential \ ca-certificates \ gcc \ git \ libpq-dev \ make \ python-pip \ python2.7 \ python2.7-dev \ && apt-get autoremove \ && apt-get clean RUN wget -O /tmp/monit-$MONIT_VERSION-linux-x64.tar.gz http://mmonit.com/monit/dist/binary/$MONIT_VERSION/monit-$MONIT_VERSION-linux-x64.tar.gz RUN cd /tmp && tar -xzf /tmp/monit-$MONIT_VERSION-linux-x64.tar.gz && cp /tmp/monit-$MONIT_VERSION/bin/monit /usr/bin/monit COPY ./monitrc /etc/monitrc RUN chmod 0700 /etc/monitrc COPY ./conf.d /etc/monit/conf.d RUN mkdir scripts COPY scripts /scripts COPY mqtt_publisher /etc/init.d/ RUN chmod +x /etc/init.d/mqtt_publisher RUN pip install -r /scripts/requirements.txt EXPOSE 2812 CMD monit -I
Il ne nous reste plus qu’à compiler l’image !
À la racine du projet, lancez donc la commande build, puis la commande run si le build s’est correctement passé :
ray@frugalprototype ~/docker/python_script_docker $ docker build -t ray/python_script . ray@frugalprototype ~/docker/python_script_docker $ docker run -it -p 2812:2812 ray/python_script
Pour rappel, le paramètre « -p 2812:2812 » de la commande Docker run permet de mapper un port du container sur le port de la machine hôte.
Pour accéder à l’interface de Monit, il suffira donc, depuis votre navigateur, de vous rendre à l’adresse « localhost:2812 ».
Vous pourrez ainsi voir le statut de l’exécution de votre script Python.
Attention, il est possible que Monit vous affiche initialement une erreur sur l’exécution du script ; cela arrive généralement avec cette configuration. Mais normalement, au bout de 30 secondes, Monit réactualise l’interface et le script apparaît correctement en « running ».
Revenons à nos squelettes de moutons
Nous avons donc réalisé ici une image fonctionnelle, mais générique. Il vous suffit en effet de remplacer le script contenu dans le répertoire « scripts », ainsi que le fichier « requirements.txt » pour lancer le script de votre choix. Bien entendu, et selon les cas, il vous faudra en plus fournir une certaine quantité d’huile de coude pour adapter votre image selon le besoin ; mais néanmoins, vous avez ici quelque chose de relativement prêt à l’emploi, et surtout, vous avez étendu votre connaissance de ce formidable outil qu’est Docker !
Pour ceux qui souhaiteraient gagner un peu de temps, vous pouvez retrouver ce projet sur mon GitHub : https://github.com/RayBor/docker_image_python_monit
N’hésitez pas à nous faire savoir ce que vous avez pensé de l’article, si vous l’avez trouvé utile, et si vous avez réussi à utiliser dans vos projets ce que vous avez appris.
Si des erreurs se sont glissées dans l’article, n’hésitez pas non plus à 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.
Related Posts
31 janvier 2017
Un bot Facebook Messenger pour la mobilité urbaine
Depuis quelques années, les bots, ces…
Merci pour ton tuto.
Si j’ai bien compris monit ne te sert que pour démarrer le script qui est dans le container ?
La page web n’étant pas accessible puisse qu’uniquement visible dans le container.
Hello,
La page web est bien accessible depuis l’extérieur, puisque lors du lancement du container nous ajoutons l’option « -p 2812:2812 », qui mappe le port du container sur le port de la machine hôte.
Le serveur web est donc accessible depuis la machine hôte, mais également depuis le réseau sur lequel elle se trouve !
Si ce n’était pas le cas, Monit ne serait que d’une faible utilité, et il serait bien plus simple de lancer directement le script de manière classique.
Attention toutefois, ce tuto est orienté « preuve de concept et frugalité », et la pratique qui est décrite va plutôt à l’encontre des principes de Docker, puisque la bonne pratique est de ne faire tourner qu’un processus par container ; dans notre cas, nous en faisons tourner deux : le script lui-même et son monitoring par Monit.
Un réel plaisir de lire cet article. Tant bien même que Docker me semble pas encore familieux mais ce post m’a éclairer un peu plus mieux de plus.