Introduction à Docker
Wow, on attaque du lourd là. Docker. Docker, qu’est ce que c’est ? Cet outil va vous permettre de déployer des applications sans vous préoccuper de sur quelle machine, OS où vous voulez installer votre application, elle marchera quoi qu’il en soit. Pour être bref, Docker va vous aider à encapsuler vos logiciels au sein d’ultra légères VMs (qui ne n’en sont pas, mais on verra un peu plus tard dans l’article) qu’on appelle “containers”.
Installation
C’est assez simple d’installer docker sur archlinux :
sudo pacman -S docker
Sur Ubuntu, c’est un poil plus compliqué :
|
|
Maintenant vous pouvez utiliser des commandes comme root. Pour pouvoir
utiliser les commandes docker avec votre utilisateur courant, vous devez
l’ajouter dans le groupe docker .
usermod -a -G docker VotreUtilisateur
Maintenant vous devriez être capable d’utiliser la cli de docker. Je vous recommande vivement de faire cette petite manip’, vous pourrez bénéficier de l’auto-complétion sur zsh ou bash pour les noms de containers.
Qu’est ce qu’un container?
Un container, comme je vous l’ai dit dans l’introduction est une ultra légère VM. Mais vous allez demander pourquoi je vous ai dit qu’en fait non… On peut prendre l’exemple d’une maison et d’un appartement (j’invente rien, je cite juste la doc officielle de Docker). La maison est construite de rien et va contenir beaucoup de choses, voir même plus que ce que vous aurez besoin pour vivre, de plus vous allez construire une maison non pas pour vous seulement mais sûrement pour la partager avec votre famille, vos amis, vos animaux, etc… L’appartement quant à lui ne peut exister sans immeuble et ce que vous déciderez de placer dans l’appartement sera souvent le strict minimum à défaut de place (à moins que vous roulez sur l’or, chose que je vous souhaite). Dans cette petite métaphore la maison est la VM et l’appartement le container.
Maintenant on peut se demander, ok, c’est bien gentil d’installer des apparts partout mais il nous faut un immeuble, non ? Et bien, cet immeuble, c’est le fameux docker host ou engine (comme vous voulez). Le docker host va remplacer l’hypervisor et devenir le constructeur et l’hébergeur de vos containers. Je vous mets une image ci-dessous vous montrant la différence entre une architecture avec VMs et un archi avec containers.
Comme on peut le voir plus haut, la VM contient bien des éléments qui ne sont pas forcément utile au fonctionnement de notre application, le Guest OS.
Je ne sais pas si vous avez essayé de lancer une commande docker mais
normalement vous devriez avoir une erreur du genre :
Is the docker host running?
, devinez quoi, bah non, il ne tourne pas.
Pour faire tourner le docker engine ou host sur votre machine, executez
cette commande :
sudo systemctl start docker
ou sudo service docker start
tout dépend
de quelle version d’Ubuntu vous utilisez. Et maintenant, si vous
essayez docker ps
, vous devriez avoir :
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
qui ne sert pas à grand chose mais on s’est débarrassé du message d’erreur ce qui prouve que tout marche comme prévu !
Lancer un container {#“lancer-un-container”}
Maintenant que nous avons notre docker engine / host qui tourne, construisons donc quelques appartements. Pour cet exemple nous allons utiliser une image super cool: jess/hollywood, si vous avez toujours voulu être un hacker comme dans les films hollywoodien, vous allez être comblé ! Je vous conseille de mettre votre terminal en plein écran et lancer la commande suivante :
docker run -it jess/hollywood
Stylé hein? Bon c'était juste pour le fun et merci à @jessfraz pour l’image.
Regardons ce que la commande précédente a fait. Si vous n’aviez pas
localement une copie de l’image sur votre docker host, docker a
effectué docker pull jess/hollywood
pour la télécharger. Une fois le
téléchargement effectué, docker va exécuter lancer le container et
retourner la sortie du service directement dans notre terminal. On
aurait pu runner ce container en mode détaché mais il n’y aurait plus
d’intérêt d’utiliser ce container.
Exécuter une commande {#“executer-une-commande” name="“executer-une-commande”"}
Ok, commençons les choses sérieuses. On va installer une base de données sur notre docker host. Par contre ce coup-ci, on va y aller tranquillement.
|
|
Si vous exécutez docker ps
, vous devriez avoir un résultat comme
celui-là:
|
|
Comme vous pouvez le voir, on vient de créer un container nommé
mongodb
avec la docker image de mongo !
Maintenant, nous allons essayer de créer un admin dans la base données.
Pour cela :
docker exec -it mongodb mongo admin
Vous devriez avoir une sortie comme celle-ci :
|
|
Maintenant exécutons la commande :
db.createUser({ user: 'etienne', pwd:'ALaTienne', roles: [{role: "userAdminAnyDatabase", db: "admin"}]})
et normalement, vous devriez avoir ça :
|
|
Pour résumer ce que nous venons de faire, On peut exécuter des commandes au sein d’un container, si le binaire est au sein du container, il nous suffit de lancer
docker exec -it my_container my_command --m my_parameter
Conseil: Si vous voulez vous rendre dans un container pour inspecter quoi que ce soit, vous pouvez utiliser cette commande
docker exec -it my_container /bin/bash
.
Lier un dossier ou un fichier depuis le docker host dans un container
Bon ok, on a notre base mongo et quelques données dedans. Cool, cool, cool… On est d’accord que des containers ça peut être facilement effacés ? Du coup que deviennent les données lorsqu’on détruit un container ?
Essayons, on verra après :
docker stop mongodb
docker rm mongodb
Lol, pas de panique, tout va bien, docker a juste réagit comme ça:
Bon… On a tout perdu. On vient de faire une “Gitlab”. Mais bon c’est pas grave, “ça arrive”.
Gardez en tête que vos containers sont temporaires, un peu comme des
vms, vous devriez être capables de les détruire et être capable de
toujours avoir vos données et de récupérer l'état de votre vm comme si
rien ne s'était passé. Docker nous permet de stocker des fichiers ou
dossiers sur notre docker host grâce à ce que l’on appelle des
docker volumes
.
Créons donc un volume docker lié à notre container afin de garder les données de notre mongodb.
Construisons de nouveau notre docker container mais cette fois-ci avec le volume :
docker run --name mongodb -d -v /somehwere/youwant/to/store/the/data/on/the/host:/data/db mongo
Comme vous pouvez le voir, nous avons ajouter l’option -v (pour volume,
CAPTAIN OBVIOUS IN DA PLACE) avec ce pattern
chemin_du_dossier_sur_lhost:_chemin_dans_le_container
Et si vous vous
rendez dans le dossier que vous avez spécifié, vous pouvez voir que des
données sont apparues dans ce dossier. Pas mal, hein ?
De cette façon, nous pouvons supprimer le container sans problème et lié de nouveau notre dossier pour récupérer un container dans le même état que celui que nous avons détruit.
Si nous ne voulions pas accéder aux données et simplement avoir un
“espace de stockage” au sein de docker, on aurait pu créer un
volume docker de cette façon :
docker volume create --name mongodb_data
et le lié de cette manière à notre container :
docker run --name mongodb -d -v mongodb_data:/data/db mongo
Vous pouvez aussi lister les volumes que vous avez sur votre docker host. Pour cela, utilisez :
docker volume list
Et bizarrement; vous devriez avoir plusieurs volumes… J’ai menti, je m’excuse. Quand vous utilisez une image docker contenant des instructions “VOLUME” (voir la section Créer notre propre docker image), un volume non nommé va être créé par docker et contiendra les données du container. Dans l’image de mongodb ( que vous pouvez trouver ici, on peut voir qu’il y a une instruction VOLUME pour les dossiers /data/db, /data/configdb, ce qui veut dire que les volumes trouvés précédemment sont ceux de notre premier mongodb… Oups.
Si vous vouliez vraiment détruire les volumes d’un container, utilisez
l’option -v : docker rm -v my_container
.
Lier deux containers ensembles
OOOOOOh, un peu de réseau, the best part EVER. Il serait un peu étrange de laisser une base données sans avoir d’applications connectées à celle-ci. Malheureusement, nous n’allons pas créer d’application spécifique pour cet exemple. Désolé.
MAIS! Nous allons utiliser le client mongodb (dans un container) pour se connecter à notre base de données.
Ajoutons notre utilisateur admin comme dans la deuxième section.
|
|
Maintenant créons notre client.
docker run -it --rm --link mongodb:mongo mongo mongo -u etienne -p ALaTienne --authenticationDatabase admin mongo/apero
Ok, le mongodb:mongo mongo mongo, est un peu chelou. Vous connaissez
docker run -it --rm
, on l’a déjà utilisé. -i -> interactive mode
(garde STDIN ouvert), -t -> alloue un pseudo tty, et --rm supprime le
container quand il est arrêté.
On a ajouté le --link pour créer (suspense…) un lien entre le
container que nous sommes en train de créer et celui nommé mongodb
.
--link mongodb:mongo
veut dire
Créé un lien depuis le container mongodb avec l'alias mongo
. L’alias
peut être considéré comme une entrée dans le fichier /etc/hosts liant
l’IP du container au label.
Pour être sûr d'être compris, dans le container que nous créons, nous
pouvons accéder à la base de donnée en utilisant le nom de domaine
mongo
.
J’ai expliqué docker run -it --rm --link mongodb:mongo
, le prochain
mongo
est le nom de l’image docker. Et le reste est la commande que
nous allons exécuter dans le container.
Retournons taffer un peu. Vous devriez avoir un curseur vous attendant, du genre :
>
Écrivons :
> db.getName()
et vous devriez avoir :
apero
Bref, on vient juste de lier deux container. MAIS Si vous avez regardé la documentation, vous avez sûrement vu que cette manière de faire est dépassée… Vu qu’on est des mecs au top de la technologie digitale, révolutionnant l’industrie et le monde dans une ambiance bien plus que familiale, faisons ça de manière classe!
Avec docker vous pouvez créer des réseaux pour vos applications et ils vous facilitent la liaison de vos containers ! Par exemple, si vous voulez créer encore une fois une base de données, tous les containers qui seront liés à cette base de données auront besoin d'être relancés pour pouvoir recréer le lien entre les containers. Avec les réseaux docker, vous pouvez en créer un, placer vos container dedans et comme de par magie, ils vont être capables d’interagir entre eux. Vous devez toujours configurer votre application afin qu’elle utilise les noms de domaines correspondant aux noms de vos docker containers mais ça marche plutôt bien.
Créons en donc un ! Pour créer un réseau, utilisez cette commande :
docker network create mongo_network
Nous devons ajouter notre mongodb dans ce réseau. Détruisons notre container mongo et reconstruisons le.
docker stop mongodb
docker rm mongodb
docker run --network=mongo_network --name mongodb -d -v mongodb_data:/data/db mongo
Et pour le client…
docker run -it --rm --network=mongo_network mongo mongo --host=mongodb -u etienne -p ALaTienne --authenticationDatabase admin mongo/apero
Et tout devrait refonctionner comme précédemment ! Importante chose à savoir, vous ne pouvez plus utiliser les labels comme nous avions fait avec les liens. Donc faites attention aux noms des containers que vous attribuez.
On vient juste de terminer tous les points importants de la ligne de commande de docker. Félicitations (ça fait jamais de mal de se féliciter de temps en temps :D). Maintenant nous allons passer au niveau supérieur et créer notre propre image docker.
Créer sa propre image
Commençons et terminons sur ce point.Quand vous voulez créer une nouvelle application avec votre framework préféré et que vous voulez utiliser docker, vous allez devoir créer un Dockerfile contenant les informations nécessaires pour installer votre application.
Construisons une simple application qui va retourner une string en sortie. On va construire étape par étape cette application.
Premièrement, nous allons coder le cli.py. Le code :
|
|
Maintenant nous pouvons ajouter le Dockerfile pour construire notre application.
La première étape qui est sûrement la plus importante est de choisir sur quelle image nous allons baser notre dockerfile. On peut commencer avec une image d’ubuntu ou directement utiliser une image optimisée pour python. La dernière image sera la plus facile à utiliser car nous n’aurons pas besoin d’installer de dépendance vu que tout sera contenu dans l’image python de base.
Créons donc notre Dockerfile
là où vous avez créé le fichier cli.py
:
|
|
Si vous avez besoin de plus d’informations à propos de la syntaxe du dockerfile, la documentation officielle est votre amie.
Maintenant que nous avons notre Dockerfile, nous pouvons créer notre image. Pour cela :
docker build -t hello_cli .
Qui créera donc l’image docker basée sur le dockerfile trouvé dans le
répertoire courant (d’où le .
) avec le nom hello_cli
. Si tout
s’est bien passé, vous devriez avoir une sortie du genre:
|
|
Comme vous pouvez le voir, chaque commande dans le dockerfile est équivalent à une étape lorsque docker construit notre image. Il est important de savoir que lorsque votre build échoue, docker va “cacher” toutes les étapes qui ont réussies pour que la prochaine construction de votre image soit plus rapide. De plus, docker va détecter si vous avez changé une étape et la ré-exécutera ainsi que toutes les suivantes.
Pour vérifier que tout s’est bien passé, nous pouvons inspecter quelles sont les images que nous avons sur notre docker host. Lancez:
docker images
Vous devriez avoir une image hello_cli
mais aussi l’image mongo que
nous avons téléchargée dans le chapitre précédent. Maintenant nous
pouvons créer notre container et voir si tout fonctionne :
docker run -it --rm hello_cli
Et vous devriez voir : Hello world!
AWESOME!
Publier une image
Si vous êtes très fier du travail que nous venons d’accomplir, nous
pouvons pousser cette image au sein d’un registre docker (qui est une
sorte de “base de données” d’image docker). Gitlab intègre un
registry par défaut mais vous avez aussi le docker hub. C’est comme
vous voulez. Pour pousser une image, vous aurez besoin de vous loguer
sur le registre depuis votre docker host avec cette commande
docker login myregistry.com
et pousser votre docker image avec
docker push hello_cli
et vous serez maintenant capable de la
télécharger sur tous vos docker hosts !
Le point important à se rappeler et que si vous utilisez un registre perso, vous devez vous authentifier avant de télécharger votre image.
Conclusion
Et TADAM. On vient de voir à quel point Docker est génial dans cette petite introduction. Bien sûr, ceci n'était qu’une introduction pour vous montrer comment Docker fonctionne mais je vous invite à jouer avec dès que vous créez une nouvelle application. Vous pouvez l’utiliser pour créer des environnements de développement ou même l’utiliser en production et faire de l’intégration continue sans avoir peur d’une dépendance manquante ou quoi que ce soit. Pour ce dernier, je vous invite fortement à utiliser un “outil d’orchestration” pour gérer tous vos containers comme Nomad ou Ansible ou Kubernete, mais tu devras attendre un prochain article pour ça jeune padawan !
Sur ce, codez bien! Ciao!