cloud:2019:tp_swarm

Sujet de TP : Mise en œuvre de Docker Swarm

Ce TP sera évalué de façon automatique (script) le Vendredi au matin. Notez vos réponses aux questions sur une feuille que vous apporterez avec vous lors de l'examen finale.

Pour ce TP vous devez créer 3 machines virtuelles par groupe, or le projet ne permet la création que de 120 machines environ. Il est donc très important de travailler au moins en binôme et de supprimer celles qui ne vous servent plus.

Enfin vous devrez tous saisir dans Tomuss l'adresse IP du nœud Manager dans le champ IP_Manager_TP_Swarm.

L'objectif de ce TP est de se familiariser avec la solution de clusterisation proposée par Docker et nommée Swarm. Comme vu en cours, il existe des solutions alternatives que nous n'explorerons pas dans le cadre de ce TP (ex: Kubernetes).

Nous nous appuierons sur l'infrastructure OpenStack de Lyon 1 https://cloud-info.univ-lyon1.fr/horizon. Pour obtenir des informations sur comment se connecter : https://documentation.univ-lyon1.fr/ . Dans le projet CloudComputing ou SRIV-SCV nous déploierons au préalable 3 petites machines virtuelles (m1.xsmall est suffisant). L'une sera utilisée comme Manager, et les deux autres comme Worker. <hi #ffc90e>Attention :</hi> Pour ce TP nous avons préparé une image avec un docker pré-installé et pré-configuré qui fonctionne à l'université. Vous devez créer vos instances à partir de l'image nommée Ubuntu 20.04.3 LTS - Docker Ready.

  • Saisir l'adresse IP du nœud Manager sur Tomuss (étape à faire par tous).
  • Ajouter la clé publique de l'enseignant avec la commande suivante sur le nœud Manager.
echo "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQC4fp5+2v2t5LjEckY6V3lNPPf8qIXEsZIVZvL0bnqY+f+zyPHhdyMS/r5qH8lpeB8CHjKwQRp3ttF1zZ5v7A4cel/2uXccun/gwf96s5Kpm5j8KzKwQbkigb/5Q69GC4pNA0b6u7QQ3kXTghaSFkuVlmqueoaw9sO9udYRXSVk9FX48F8OxkkwYMlUIacAU69udhvrpcUGBDn+t2Q696OUGl/4CZtHP4me3otOCFp6v3dGbY0ccu5at1aRaOmLH0XTtxRNSny3d1vpsgguAIT/PJJAjeuGCAWApNvJIYiQi4z0f6MSsjaIPwWtBakkNWPMkxP5Gph3x6qkkzyHMsoD jp@h2g2" >> /home/ubuntu/.ssh/authorized_keys

L'installation de base se fait à présent de manière très simple. Tout d'abord sur la VM qui hébergera le manager :

docker swarm init --advertise-addr 192.168.XXX.YYY
  • Que fait cette commande ? Expliquez/Décrivez succinctement.. Vous pouvez utiliser la commande docker swarm init --help pour avoir une information sur les paramètres.

Puis sur chacun des workers

# docker swarm join --token letokenrenvoyeparleswarminit 192.168.XXX.YYY:2377
  • Que fait cette commande ? Expliquez/Décrivez succinctement.

Remarque : Pour chacune des questions vous trouverez des éléments de réponse dans la documentation officielle de Docker Swarm.

NB: Une fois le cluster installé, la commande ci-dessous vous permettra d'avoir un visualiseur de votre infrastructure déployée (http://IPDEVOTREVM:8080):

docker service create   --name=viz   --publish=8080:8080/tcp   --constraint=node.role==manager \
                        --mount=type=bind,src=/var/run/docker.sock,dst=/var/run/docker.sock    \
                        dockersamples/visualizer
  • Quelle commande peut-on utiliser pour lister et vérifier la liste des workers à disposition ?
  • Imaginons que vous souhaitiez ajouter un nouveau nœud worker et que vous avez égaré le token de votre cluster swarm. Quelle commande vous permet de le récupérer ?
  • Est il possible d'avoir plusieurs machines qui tiennent le rôle de manager (au même titre que plusieurs machines sont des workers). Comment fonctionnent t-ils ?
  • Sortez un worker du cluster puis remettez-le. Quelles commandes saisissez-vous ?

Exécutons trois instances de nginx dans notre cluster swarm.

(manager)# docker service create --name web --publish 80:80 --replicas=3 nginx:latest
  • Quelle commande utilise-t-on pour lister tous les services créés ?
  • Quelle commande utilise-t-on pour lister toutes les instances du service ?
  • Quelle commande utilise-t-on pour obtenir des informations détaillées sur un service ? (inspect)

Comment étendre un service

Afin d'avoir à présent cinq instances de notre service

(manager)# docker service scale web=5

On peut également réduire le nombre de services (scale down)

(manager)# docker service scale web=1

Notez que pour supprimer un service (ne le faite pas ici)

(manager)# docker service rm web

Maintient automatique du service

Si nous avons défini un service avec un nombre donné de replicas, le Swarm manager est en charge de s'assurer de maintenir cet état. Pour vous en assurer :

  • Instanciez trois répliquas d'un service (voir commande précédente);
  • Connectez-vous sur l'une des machines de travail hébergeant l'un de ces répliquas et détruisez brutalement le docker (docker kill …).
  • Que se passe t-il au bout de quelques secondes ?
  • À présent stoppez la machine de travail (via l'interface *horizon*). Que se passe-t-il ?
  • On souhaite éteindre un nœud physique (de type worker) pour effectuer une opération de maintenance sur ce dernier. Quelle procédure aller vous suivre (ou Quelles commandes allez vous saisir) pour faire cela proprement ?
  • Expliquez brièvement la notion de promotion d'un nœud worker (promote/demote).

Supprimez tous les services utilisés précédemment (sauf le service de vizualisation), vous allez en relancer un seul avec un unique réplicas :

docker service create --name nginx -p 80:80 --replicas 1 nginx

Ensuite, retrouvez sur quel nœud de calcul se trouve le docker. Le nom du docker est un peu différent du nom du service.

docker logs -f nginx.x.yyyyyyyyyyy

Grâce à la commande précédente afficher les logs du docker nginx. Puis avec votre navigateur tentez de contacter le serveur web sur vos 3 VM (Manager et Workers).

  • Que constatez-vous ?
  • Quel réseau utilise le docker ? (utilisez la commande docker inspect …)
  • Afficher la liste des réseaux, quel est le driver de ce réseau ?

Créez votre réseau overlay

  • Créer un réseau 172.21.21.0/24 avec le driver overlay ayant pour nom reseau (sans accent). Ajoutez lors de sa création l'option attachable. Cette option vous permettra de créer des dockers utilisant le réseau sans être géré par swarm.
  • Déployer sur le réseau nouvellement créer 3 réplicas du service nginx sur le port 80 et dont le nom est 'web'. Observez l'adressage IP.

Vous avez vu lors des exercices précédant que le cluster swarm pouvait facilement lancer des dockers basés sur des images publiques. Mais pour le moment vous ne pouvez pas le faire avec des images construites via un Dockerfile. Pour éviter cette limite, il faut installer un dépôt docker privé dans lequel vous placerez et téléchargerez les images que vous avez générées. Vous trouverez la documentation ici

Attention, les registry que vous pouvez facilement utiliser sont des registry locaux c'est à dire que leur adresse est localhost. En effet, pour autoriser un registry distant, il faut gérer des certificats d'authentification SSL ce qui est hors du cadre de ce TP. Vous allez donc créer un unique docker registry qui répondra à l'adresse localhost:5000 sur plusieurs machines virtuelles. Cela n'est possible que grâce au cluster swarm qui va s'occuper pour vous de la redirection des paquets.

  • Dans le cluster swarm, créer un service registry dont le nom sera registry, issue de l'image registry:2 et qui partage le port 5000 avec toutes les machines du cluster.
  • Sur quel nœud ce dernier est-il instancié ?
  • Le registry est un nœud de stockage, si l'ordonnanceur décide de le déplacer, toutes les images qu'il contient seront effacées. Pour éviter cela, supprimez le service registry et recréez le en imposant (constraint) qu'il soit instancié sur le nœud Manager.
  • Testez sa réponse à l'adresse localhost en vous connectant sur une machine virtuelle qui ne fait pas tourner le service *registry*.
    • Pour ce faire vous pouvez pull une image officielle (prendre l'image hello-world) puis
    • la tagger en localhost:5000/my-hello-world.
    • Ensuite vous la push sur localhost et supprimez les deux images (celle récupérée via le pull et celle taggée).
    • Enfin, faite un pull sur localhost de l'image taggée.

Exploration du registry

Le registry semble un peu abstrait car il ne permet pas facilement d'explorer son contenu. Mais il y a des services spécialisés dans cette exploration que vous pouvez bien sur installer sous forme de conteneurs.

docker service create  -d --network reseau  -e ENV_DOCKER_REGISTRY_HOST=registry   \
                       -e ENV_DOCKER_REGISTRY_PORT=5000 -p 8443:80  --name regbrowser \
                       konradkleine/docker-registry-frontend:v2

Consultez la page http://IP_DE_VOTRE_VM:8443 et explorez le contenu du registry.

La gestion des données persistante est un problème dans ce type de cluster, car l'ordonnanceur est susceptible de déplacer le conteneur à tout moment. Si ce conteneur détient des données, rien n'est a priori prévu pour déplacer les données sur nouveau nœud. Une première méthode est d'interdire ce déplacement. C'est ce qui est utilisé pour le registry de la question précédente. Mais cela n'est pas idéal.

Prenons le cas d'un docker implémentant un notebook jupyter : notebooktoto.zip. Dans l'archive, il y a un fichier Dockerfile et un docker-compose qui permettent de lancer le conteneur en tant que service dans le cluster.

  • Construisez l'image localhost:5000/notebookplop_notebook à partir du Dockerfile
  • Supprimer le service de visualisation qui utilise le même port (8080) que celui qu'on veut utiliser maintenant.
  • Placez la dans le registre.
  • Lancez le service via la commande docker stack deploy
docker stack deploy --compose-file docker-compose.yml notebook
  • Vérifiez son lancement, le conteneur doit se lancer sur l'un des nœuds workers la suite du TP. Si ce n'est pas le cas et qu'il est lancé sur le maitre, il faut recommencer le lancement du service en supprimant les services qui encombrent les nœuds workers que l'ordonnanceur les choisisse et en en utilisant une contrainte pour interdire l'utilisation du maitre.
  • Sur le notebook, le mot de passe est toto. Créez un fichier dans le répertoire work avec un code python simple par exemple celui-ci qui affiche l'adresse IP et le hostname du docker :
import socket
def get_Host_name_IP(): 
    try: 
        host_name = socket.gethostname() 
        host_ip = socket.gethostbyname(host_name) 
        print("Hostname :  ",host_name) 
        print("IP : ",host_ip) 
    except: 
        print("Unable to get Hostname and IP") 
  
# Driver code 
get_Host_name_IP() #Function call 
  • Sauvez bien le fichier.
  • Forcez le déplacement du docker en plaçant le nœud qui le porte en mode drain. Une fois que vous avez constaté le déplacement du conteneur, replacer le nœud en mode active.
  • Reloguez-vous sur le service, vous avez perdu votre travail.

En effet, le volume contenant le répertoire work est uniquement présent sur la machine qui exécutait le docker au départ, il a donc été perdu.

Pour résoudre le problème, il faut utiliser un volume présent sur tous les nœuds du cluster. Pour cela, il faut utiliser un partage de fichier et un driver spécial pour les volumes qui permet de s'y connecter. Le driver le plus simple à utiliser sous docker swarm est NFS, il n'est pas idéal, car il n'est pas adapté à la plupart des besoins des services (ne l'utilisez pas pour une base de donnée par exemple). Le serveur nfs préparé pour le TP est 192.168.76.5 (dans le projet TIW7) et 192.168.246.27 (dans le projet IGA) et 192.168.239.162 (dans le projet M2SRIV)

  • Installez un client NFS sur chacun des noeuds (c'est à dire le paquet nfs-common)
  • Vérifiez le fonctionnement en montant le répertoire partagé 192.168.76.5:/exports/p1VOTRENUMEROETUDIANT dans le répertoire local /tmp/toto. N'oubliez pas de le démonter ensuite, mais conservez le montage NFS sur le noeud maitre.
$ sudo mkdir -p /mnt/partage
$ sudo mount -t nfs ADRESSEIPSERVEUR:REPERTOIRESURLESERVEUR /mnt/partage
# On peut vérifier le montage par la commande df et démonter par umount
  • Dans le docker-compose modifiez la définition du volume works pour qu'il utilise votre partage NFS. Pour cela vous pouvez consulter la documentation.
  • Supprimez puis relancez le service, ajoutez un fichier au répertoire work.
  • Vous pouvez constater que le fichier est sauvegardé dans le répertoire monté par NFS.
  • Refaites la manipulation pour déplacer le service, vous devez retrouver votre travail dans le nouveau docker.

Est-ce suffisant pour créer un cluster de notebook ?

  • Augmentez le service en demandant 2 réplicas. Essayez de l'utiliser. Cela ne fonctionne pas. Pourquoi ?

Pour la suite du TP nous allons recréer les 2 worker, il va donc falloir les supprimer proprement.

  • supprimez tous les services en ne conservant que les service regbrowser, viz et registry (a priori il faut supprimer les services nginx et le stack notebook).
  • Modifiez les 2 workers pour les vider de leur dockers.

Vous constatez que les services sont déplacés sur le manager (qui est le seul nœud encore utilisable du cluster).

  • Si le registry a été déplacé, regardez une nouvelle fois son contenu. Il est vide ! Expliquez pourquoi.
  • Il est important d'avoir un registry disponible et qui ne perde pas son contenu. En cas e besoin refaites le en imposant comme contrainte qu'il soit sur le noeud manager.
  • Retirez les workers du cluster

Docker machine est un script qui permet de générer des machines virtuelles et d'installer docker automatiquement. Il est capable d'utiliser plusieurs hyperviseurs comme virtutalbox ou openstack mais aussi les fournisseurs de cloud publiques : AWS, Azure … Vous avez une documentation plus spécifique sur openstack ici.

Normalement, toutes les machines utilisées sont générées par docker-machine. Mais certaines requêtes de docker machine utilisent un port inhabituel qui est filtré sur le réseau wifi ce qui perturberait le TP. Il faut donc l'utiliser depuis une machine qui se trouve dans le même réseau. Pour ce TP, il est donc plus simple de l'utiliser depuis le manager pour créer les 2 workers.

  • Supprimez les workers du début du TP mais conservez le manager.
  • Sur le manager, vérifiez que docker-machine est installé avec l'utilitaire de complétion. Si ce n'est pas le cas, suivez les instructions d'instalation qui sont ici et .

La commande docker-machine est assez simple car elle signale la plupart des options qui lui manque. La commande de création est documentée ici, les options relatives à openstack là. CE qu'il faut comprendre est que la commande va contacter les API openstack et s'occuper de l'installation des machines à votre place. Il faut donc lui fournir toutes les informations qui vous serait nécessaire pour faire la même chose. Vous pouvez aussi bien sur suivre la progression de son travail en consultant le site https://cloud-info.univ-lyon1.fr. Vous devez savoir que :

  1. le driver est openstack
  2. l'url d'authentification est https://cloud-info.univ-lyon1.fr:5000/v3/
  3. le nom d'utilisateur et le mot de passe sont ceux qui vous permettent de vous connecter (voir la remarque plus loin).
  4. le tenant est le projet openstack où créer les machines Il faut donner son identifiant avec l'option openstack-tenant-id et lui donner la valeur pour un TIW ou un DS 9b0d64f0d1d241d8b8afbd60caac615a ou pour un SRIV ff3a9802a71b4509ab90215974b51236 1).
  5. le nom de l'image à utiliser est celui du snapshot habituel snap-docker-machine2).

Quelques conseils :

il faut bien spécifier les options nécessaires au fonctionnement à l'université. Le proxy via l'option engine-env qui permet de spécifier une variable d'environnement3). Cela permettra de configurer le docker installé pour l'utilisation du proxy de l'université et s'utilise de cette manière :

docker-machine ... 
  --engine-env HTTP_PROXY=http://proxy.univ-lyon1.fr:3128 \
  --engine-env HTTPS_PROXY=http://proxy.univ-lyon1.fr:3128 \
  --engine-env NO_PROXY=.univ-lyon1.fr \
  --engine-opt "dns 10.10.10.10" \
  --engine-opt "log-level debug" \
  ... NOMDELAMACHINE

Lorsque docker-machine a commencé à créer une machine, il en garde une trace et interdit de réutiliser le même nom. Vous pouvez effacer ces traces en utilisant la commande docker-machine rm -f NOMMACHINE.

<hi #ff7f27>Protection de votre mot de passe</hi>: Attention, vous vous connectez au serveur openstack avec votre login et mot de passe de l'université. Pour éviter de laisser votre mot de passe en clair dans la ligne de commande, n'utilisez pas l'option openstack-password. Vous devez stocker votre mot de passe dans la variable d'environnement OS_PASSWORD. Pour cela copier/coller la commande :

  export OS_PASSWORD=$(python3 -c "import getpass; pa=getpass.getpass('Mot de passe : '); print (pa)")

Ensuite, vous pourrez utiliser la variables d'environement $OS_PASSWORD

<hi #ff7f27>Erreur</hi>: Si vous obtenez l'erreur suivante faite un export NO_PROXY=.univ-lyon1.fr

Error creating machine: Error in driver during machine creation: Post https://cloud-info.univ-lyon1.fr:5000/v3/auth/tokens: Forbidden

<hi #ff7f27>Erreur</hi>: Vous aurez sans doute l'erreur suivante :

Error creating machine: Error running provisioning: Unable to verify the Docker daemon is listening: Maximum number of retries (10) exceeded

Elle est du à l'utilisation du proxy, mais normalement, elle n'a pas empêcher le lancement des machines. Donc vous pouvez simplement l'ignorer.

  • En utilisant docker-machine, créez 2 machines qui serviront de nœuds de travail pour Swarm. Appelez-les VOTRELOGIN-work1, VOTRELOGIN-work2.

Vous pouvez ensuite voir les nœuds gérés par docker-machine via la commande docker-machine ls, en supprimer une par docker-machine rm

docker-machine stocke ses informations dans le répertoire .docker/machine/….

  • Où sont les clefs ssh des machines que vous avez créées ?

Pour exécuter des commandes docker sur ces machines il y a 3 méthodes :

  1. Vous pouvez utiliser les variables d'environnement docker fixées par la commande docker-machine env NOMMACHINE.
  2. Vous pouvez vous connecter sur les machines via la commande docker-machine ssh NOMMACHINE
  3. Vous pouvez exécuter les commande à distance via la commande docker-machine ssh NOMMACHINE COMMANDE. Par exemple
docker-machine ssh NOMMACHINE sudo docker ps
  • Ajouter à votre cluster swarm les 2 nœud de travail créés par docker machine.

Vous allez construire une image personnelle, mais cette dernière est basée sur une autre qui se trouve dans un registry privé et nécessite une authentification. L'image que vous allez construire devra être déposé dans le registry local localhost:5000 où elle sera disponible à tous les noeud du cluster ce qui permettra de l'utiliser dans un service.

Authentification auprès d'un registry

  • via la commande docker pull forge.univ-lyon1.fr:4567/romain.chanu/cloudcomputing/udl-php:v1 vérifiez que cette image n'est pas publique.
  • En utilisant la commande docker login (infos ici), authentifiez-vous auprès de la forge de l'université sur le port 4567. Ensuite, re-testez le téléchargement de l'image qui devrait fonctionner.
  • Dans le répertoire .docker, retrouvez le jeton d'authentification.

Construction d'une image

Vous pouvez construire une image personnelle, à partir de cette image. Pour cela créez un dockerfile avec les caractéristiques suivantes:

  1. hérite de forge.univ-lyon1.fr:4567/romain.chanu/cloudcomputing/udl-php:v1
  2. définit une variable d'environnement du nom de CC_LOGIN avec le nom d'utilisateur de votre choix
  • Il faut maintenant construire son image normalement
  • La “tagguer” correctement afin de la déposer dans le registry local localhost:5000. Rappel, la documentation est ici ici (le nom du tag doit commencer par celui du registry c'est à dire localhost:5000/)
  • Une fois les étapes précédentes réalisées il faut l'envoyer dans le registry

Vous pouvez alors voir l'image dans l'explorateur du registry.

Maintenant que tous les noeud du cluster peuvent avoir accès à cette image, elle peut être utilisée dans un service.

  • lancer un service swarm
    1. basé sur cette image;
    2. avec 3 réplicas;
    3. redirigeant le port 80 des VM sur le port 80 des dockers;
    4. utilisant le réseau reseau;
    5. dont le nom est myphp.
  • vérifiez qu'il est bien instancié sur plusieurs machines différentes;
  • vérifiez qu'il répond avec une répartition de charge.

Pour automatiser vos déploiements, il est plus pratique d'utiliser un fichier yaml pour décrire votre application comme pour le premier TP avec docker-compose. Vous allez effctivement créer ce fichier, mais la commande de lancement utilisée sera maintenant docker stack. La version du fichier est maintenant une version supérieur à 3, par exemple 3.1.

  • Vous devez supprimer votre précédent service avec docker service rm myphp.
  • Maintenant que tout est prêt, vous pouvez écrire votre fichier yaml : stack/stack.yml pour :
    1. lancer un service de nom php
    2. basé sur votre image localhost:5000/TAG;
    3. avec une réplication à 2;
    4. qui expose le port 80 sur les nœuds;
  • Une fois qu'il est écrit, vous pouvez lancer votre stack avec l'aide de la commande docker stack deploy en lui donnant le nom stackphp.

Gestion des secrets

Le service mis en place est un petit site proposant un login. Le login est celui que vous avez mis dans le Dockerfile, le mot de passe et un token de sécurité sont aussi nécessaires et peuvent être fournis par le fichier de description stack.yaml. Mais il est préférable que ces dernier n'apparaissent pas directement dans le fichier. C'est le rôle des secrets.

En effet, lors de la mise en place d'applications, il y a de nombreuses données sensibles comme les certificats ou les mots de passe. Pour ne pas les mettre en clair dans vos fichiers, docker propose la possibilité de créer des secrets. Cela tombe bien… Car l'application que vous avez construit est spécialement prévue pour utiliser les secrets pour le mot de passe et le token de soumission.

La première chose à faire est de retrouver le token dans les logs de la stack déployée. En effet, lors du lancement de l'application, la première ligne affichée dans les journaux est la valeur de votre token de soumission.

  • utiliser docker service logs … pour afficher les logs des dockers lancé et obtenir le token.

Avec la commande docker secret vous allez créer 2 secrets:

  • Le premier contenant le mot de passe de votre choix (Cela ne doit pas être le mot de Lyon1). Ce secret doit s’appeler password_login où login est la valeur de la variable d'environnement que vous avez mis dans le Dockerfile de la partie précédente.
  • Le second doit se nommer token et vous pouvez trouver la valeur à la première ligne du journal de l'application (que vous avez du lancer au moins une fois)

Une fois que les secrets sont créés, vous devez modifier votre fichier yaml pour ajouter la prise en charge de ces 2 secrets et préciser que votre service puisse y accéder. Si vous avez utilisé une fonction de type echo pour la création votre secret, il sera de type external.

Si vous avez tout configuré correctement, vous pouvez normalement vous connecter sur le petit formulaire. Si vous y arrivez, un blond (pas celui à qui vous pensez) vous félicitera.


1)
Cette information se trouve dans le fichier généré par le menu fichier openstack rcv3 en haut à droite de l'interface horizon
2)
Il est possible de lui faire utiliser une image standard à condition de lui donner toutes les options qui permettent d'adapter docker à notre environnement, mais cela est fastidieux et il serait impossible à vos enseignants de faire les corrections
3)
donnez une valeur pour HTTP_PROXY et HTTPS_PROXY
  • cloud/2019/tp_swarm.txt
  • Dernière modification : 2021/12/05 01:59
  • de jean-patrick.gelas