XinCTO/premier lab docker 2/2

Ecrit le Sun, 17 December 2017 11:33:14 +0000
1326 mots 7 minutes

premier lab docker 2/2

Pendant la première partie de ce labo, nous avons exploré le fonctionnement standard des containers, la notion d’héritage, la construction d’un container applicatif et son exécution. Je vous propose dans cette seconde partie de plonger un peu plus dans l’intégration continue et le devops avec un usage de l’héritage pour gérer la séparation des pouvoirs et la mise en qualité de notre application.

A chacun son job

En tant que développeur, il est possible que l’on vous impose des images de référence pour les containers que vous devrez utiliser dans les clauses FROM de votre définition. C’est normal, voir même conseillé, ceci évite généralement les soucis d’exploitation, les problèmes de sécurité inhérents à des images plus ou moins fiables et surtout votre équipe de production ou votre info-géreur peut avoir besoin d’instrumenter les containers de base à des fins de supervision, d’administration ou de facturation.

Mettons en place ce principe de séparation en créant l’image de référence de la production : un centos avec de quoi faire du python. Cette image mettra en place un démarrage spécifique afin d’insérer ce dont à besoin la production pour superviser et administrer ce container applicatif. Dans les pistes on pourrait avoir :

  • l’enregistrement du container dans un référentiel (ie CMDB)
  • l’information régulière du fonctionnement du container (ie redis avec expiration de clé)
  • la remontée d’information vers un outil de monitoring Certaines informations pourraient être surchargées d’information que l’équipe de développement ou d’intégration devra renseigner dans un fichier de paramétrage.

Image ops-python

L’image proposée par les ops se nomme ops-python, sa version latest doit être privilégiée, mais ils sont sympa ils nous versionnent l’ensemble. Cette image contient quelques outils python, un watchdog qui informe leur redis de la présence du container sur la base de son identifiant et fourni quelques informations comme le nombre de processus en exécution, le cpu utilisé. Ce n’est qu’un exemple rapidement construit pour les besoins de ce labo, à vous de jouer pour en faire quelque chose de plus conséquent. Si le fichier /application.json est renseigné par les développeurs, alors l’information est remontée sur le redis pour plus de clarté.

Vous trouverez le Dockerfile sur le répertoire lab-02 du git, pour le construire :

docker build -t ops-python:1 ./lab-02/
docker tag ops-python:1 ops-python:latest

Si vous avez un redis disponible, vous pouvez l’utiliser pour l’exemple, mais ce n’est pas indispensable. Il faut positionner les variables d’environnement suivantes pour le serveur et le port : OPS_REDIS_HOST, OPS_REDIS_PORT et OPS_REDIS_DB. Si vous n’avez pas de redis, il est temps de mettre à profit ce que vous avez appris et de mettre en place le container fourni sur le hub, n’oubliez pas d’exposer le port 6379.

Pour executer le container vide (paramètres à adapter) :

docker run --rm -d -m 64m -e OPS_REDIS_PORT=6380 -e OPS_REDIS_HOST=192.168.56.103 ops-python

Prime v1

Afin de proposer aux ops la première version de notre application prime, il nous faut donc la faire se baser sur l’image python proposée : ops-python. Dans le répertoire lab-03, nous reprenons notre premier exemple vu dans la première partie du labo et adaptons le nom de l’image d’origine pour utiliser ops-python:latest. Autre modification importante, nous n’utilisons plus de définition CMD et remplaçons le démarrage de notre application par un script positionné à la racine et qui sera lancé conformément à ce qui a été défini par les ops.

docker build -t prime:1 ./lab-03/
docker tag prime:1 prime:latest

Vous devriez avoir le même comportement du container que dans la première partie de notre labo, n’hésitez pas à tester en vous y référant. En complément, le redis est mis à jour sur la base du nom du container en exécution, disponible avec les commandes docker ps et docker stats. Vous pourrez utiliser la commande suivante par exemple pour consulter rapidement le contenu du redis :

redis-cli -n 1 get 12a6c1359d12

Un peu de tests

L’équipe de dev est fier d’elle et a livré une première version de son webservice prime en utilisant l’image des ops, ils seront satisfaits également. Les responsables du bon fonctionnement de la plate-forme peuvent faire confiance et utiliser le container fourni, on vérifie rapidement qu’il est bien dépendant de l’image fournie par les ops. Mais comme ils sont un peu paranoïaques, ils préfèrent lancer les tests d’intégration au cas où. Fort heureusement, ils disposent d’une couche de tests leur permettant de valider le bon fonctionnement (dans la vraie vie, la plate-forme d’intégration continue ferait cela automatiquement à chaque livraison…).

Cet exemple se veut volontairement simpliste, car les développeurs lancent en toute rigueur leurs tests régulièrement et surtout avant de livrer. Mais on pourrait imaginer des cas plus complexes de container portant un contenu de base de données, ou des fichiers bien spécifiques. Aussi, on pourrait disposer de plusieurs jeux de données différents qui seraient utilisés à chaque fois dans un container de test surchargeant celui poussé par les devs. Regardez le temps que vous passez à faire des tests et comment les containers pourraient vous simplifier la vie, c’est assez impressionnant généralement.

Pour simplifier le fonctionnement pour notre équipe d’homologation (ou test, ou intégration, ou production), l’image fournie par les dev doit être taggée avec homolo. Il est donc nécessaire d’ajouter ce tag à notre image :

docker tag prime:1 prime:homolo

Il faut ensuite construire un container partant de cette version (donc prime:homolo) afin de pouvoir faire des tests. Ici, on fait des tests sur le code, à base de flask et de nosetest, le webservice n’est donc pas lancé, aussi, on surcharge le démarrage du container avec une commande spécifique (voir le Dockerfile).

docker build -t prime-tests ./lab-04/

La hiérarchie de ce container utilise l’héritage :

  • centos:7
  • ops-python:latest
  • prime:1, prime:homolo
  • prime-tests:latest

Le container issu du développement n’a pas été touché, en revanche on y a ajouté les fichiers de test et les outils nécessaire à l’exécution des tests. Il ne nous reste plus qu’à lancer le test :

docker run --rm -m 64m prime-tests

Et là c’est le drame ! Les tests ne passent pas ; 25 est considéré comme premier par notre service, pas top :( Validation du dev sur le container d’homologation lancé pour la contre expertise, effectivement le retour n’est pas bon. Il faut retourner sur la planche à dessin…

Prime v2

Nos développeurs sont performants, ils proposent une version corrigée de leur web service, elle est disponible dans le répertoire lab-05. Pour la fournir de nouveau, on recrée un container avec une version supérieure (2 ici) et on ajoute le tag homolo, la phase de test sera la même que précédemment.

docker build -t prime:2 ./lab-05/
docker tag prime:2 prime:homolo

docker build -t prime-tests ../lab-04
docker run --rm -m 64m prime-tests

Et la, c’est correct. Ne reste plus qu’à appliquer le tag latest sur la version 2 de notre service pour le livrer définitivement à la production : on en profite également pour supprimer la version de test qui n’est plus utile :

docker tag prime:2 prime:latest
docker rmi prime:homolo
docker rmi prime-tests

Et la suite ?

Dans la même veine, vous pourriez travailler sur des phases d’homologation plus riches, des tests de performance, des tests fonctionnels, un testeur de web service, etc… L’utilisation des héritages de container permet de faire tout cela avec un niveau d’abstraction assez grand et toujours une indépendance forte à l’environnement technique d’exécution.

Une fois cette mécanique installée dans votre pipeline préféré, vous allez gagner en temps bien sûr puisque tout ceci sera automatisé, idéalement à partir d’un hook lié à la publication d’une branche de votre gestionnaire de source. Autre avantage, l’utilisation de container n’impute pas votre capital d’infrastructure, fini les serveurs de recette qui restent allumés en permanence au cas où, fini les multiples environnements de test, puisque l’on reconstruit tout à la demande, il est possible de planifier les phases de test de l’ensemble de son patrimoine logiciel afin d’utiliser un minimum de puissance de son infrastructure.

Cela ouvre des perspectives intéressantes, non ? N’hésitez pas à réagir dans les commentaires.


Photo from Ankush Minda