Docker 101 : principes fondamentaux et pratique
par Guilherme Péjon
Si vous en avez assez d'entendre vos collègues vanter Docker et ses avantages à chaque occasion, ou si vous en avez assez de hocher la tête et de vous éloigner à chaque fois que vous vous retrouvez dans l'une de ces conversations, vous êtes au bon endroit. lieu.
Aussi, si vous cherchez une nouvelle excuse pour vous éloigner sans vous faire virer, continuez à lire et vous me remercierez plus tard.
Docker
Voici la définition de Docker, selon Wikipédia :
Docker est un programme informatique qui effectue une virtualisation au niveau du système d'exploitation.
Assez simple, non ? Eh bien, pas exactement. Très bien, voici ma définition de ce qu'est Docker :
Docker est une plate-forme permettant de créer et d'exécuter des conteneurs à partir d'images.
Toujours perdu? Ne vous inquiétez pas, c'est parce que vous ne savez probablement pas ce que sont les conteneurs ou les images.
Les images sont des fichiers uniques contenant toutes les dépendances et configurations requises pour exécuter un programme, tandis que les conteneurs sont les instances de ces images. Allons-y et voyons un exemple pratique pour rendre les choses plus claires.
Remarque importante : Avant de continuer, assurez-vous d'installer Docker en suivant les étapes recommandées pour votre système d'exploitation.
Partie 1. « Bonjour le monde ! » à partir d'une image Python
Disons que Python n'est pas installé sur votre ordinateur - ou du moins pas la dernière version - et que vous avez besoin de Python pour imprimer "Hello, World!" dans votre terminal. Que fais-tu? Vous utilisez Docker !
Allez-y et exécutez la commande suivante :
docker run --rm -it python:3 python
Ne vous inquiétez pas, je vais vous expliquer cette commande dans une seconde, mais en ce moment, vous voyez probablement quelque chose comme ceci :
Cela signifie que nous sommes actuellement à l'intérieur d'un conteneur Docker créé à partir d'une image Docker Python 3, exécutant la commande python
. Pour terminer l'exemple, tapez print("Hello, World!")
et regardez la magie opérer.
Très bien, vous l'avez fait, mais avant de commencer à vous féliciter, prenons du recul et comprenons comment cela a fonctionné.
Décomposer
Commençons depuis le début. La commande docker run
est l'outil standard de docker pour vous aider à démarrer et exécuter vos conteneurs.
L'indicateur --rm
est là pour indiquer au démon Docker de nettoyer le conteneur et de supprimer le système de fichiers après la sortie du conteneur. Cela vous aide à économiser de l'espace disque après avoir exécuté des conteneurs de courte durée comme celui-ci, que nous avons seulement commencé à imprimer "Hello, World!".
L'indicateur -t (ou --tty)
indique à Docker d'allouer une session de terminal virtuel dans le conteneur. Ceci est couramment utilisé avec l'option -i (ou --interactive)
, qui maintient STDIN ouvert même s'il est exécuté en mode détaché (nous en parlerons plus tard).
Remarque : Ne vous inquiétez pas trop de ces définitions pour le moment. Sachez simplement que vous utiliserez l'indicateur
-it
à chaque fois que vous souhaiterez taper des commandes sur votre conteneur.
Enfin, python:3
est l'image de base que nous avons utilisée pour ce conteneur. À l'heure actuelle, cette image est fournie avec la version 3.7.3 de Python installée, entre autres. Maintenant, vous vous demandez peut-être d’où vient cette image et ce qu’elle contient. Vous pouvez trouver les réponses à ces deux questions ici, ainsi que toutes les autres images Python que nous aurions pu utiliser pour cet exemple.
Enfin et surtout, python
était la commande que nous avons demandé à Docker d'exécuter dans notre image python:3
, qui démarrait un shell python et permettait à notre print("Bonjour , Monde!")
appel au travail.
Encore une chose
Pour quitter Python et terminer notre conteneur, vous pouvez utiliser CTRL/CMD + D ou exit()
. Allez-y et faites-le maintenant. Après cela, essayez d'exécuter à nouveau notre commande docker run
et vous verrez quelque chose d'un peu différent et beaucoup plus rapide.
C'est parce que nous avons déjà téléchargé l'image python:3
, donc notre conteneur démarre beaucoup plus rapidement maintenant.
Partie 2. « Hello World ! » automatisé à partir d'une image Python
Quoi de mieux que d'écrire « Hello, World ! » dans votre terminal une fois ? Vous l’avez compris, en l’écrivant deux fois !
Puisque nous avons hâte de voir "Hello, World!" imprimé à nouveau dans notre terminal, et nous ne voulons pas avoir à ouvrir Python et à taper à nouveau print
, allons de l'avant et automatisons un peu ce processus. Commencez par créer un fichier hello.py
où vous le souhaitez.
# hello.py
print("Hello, World!")
Ensuite, continuez et exécutez la commande suivante à partir de ce même dossier.
docker run --rm -it -v $(pwd):/src python:3 python /src/hello.py
Voici le résultat que nous recherchons :
Remarque : J'ai utilisé
ls
avant la commande pour vous montrer que j'étais dans le même dossier dans lequel j'ai créé le fichierhello.py
.
Comme nous l’avons fait plus tôt, prenons du recul et comprenons comment cela a fonctionné.
Le décomposer
Nous exécutons à peu près la même commande que celle que nous avons exécutée dans la section précédente, à deux choses près.
L'option -v $ (pwd):/src
indique au démon Docker de démarrer un volume dans notre conteneur. Les volumes sont la meilleure façon de conserver les données dans Docker. Dans cet exemple, nous disons à Docker que nous voulons que le répertoire actuel - récupéré de $ (pwd)
- soit ajouté à notre conteneur dans le dossier /src
.
Remarque : Vous pouvez utiliser n'importe quel autre nom ou dossier de votre choix, pas seulement
/src
Si vous souhaitez vérifier que /src/hello.py
existe réellement dans notre conteneur, vous pouvez changer la fin de notre commande de python hello.py
à bash
. Cela ouvrira un shell interactif à l’intérieur de notre conteneur et vous pourrez l’utiliser comme vous l’attendriez.
Remarque : Nous ne pouvons utiliser que
bash
ici car il est préinstallé dans l'imagepython:3
. Certaines images sont si simples qu'elles n'ont même pas debash
. Cela ne veut pas dire que vous ne pouvez pas l'utiliser, mais vous devrez l'installer vous-même si vous le souhaitez.
Le dernier bit de notre commande est l'instruction python /src/hello.py
. En l'exécutant, nous disons à notre conteneur de regarder dans son dossier /src
et d'exécuter le fichier hello.py
en utilisant python
.
Peut-être que vous voyez déjà les merveilles que vous pouvez faire avec ce pouvoir, mais je vais quand même le souligner pour vous. Grâce à ce que nous venons d'apprendre, nous pouvons pratiquement exécuter n'importe quel code à partir de n'importe quel langage sur n'importe quel ordinateur sans avoir à installer de dépendances n'importe quel code.sur la machine hôte - à l'exception de Docker, bien sûr. Cela fait beaucoup de texte en gras pour une phrase, alors assurez-vous de la lire deux fois !
Partie 3. Le "Bonjour tout le monde !" le plus simple possible à partir d'une image Python en utilisant Dockerfile
Vous en avez assez de dire bonjour à notre belle planète ? C'est dommage, car nous allons recommencer !
La dernière commande que nous avons apprise était un peu verbeuse, et je me vois déjà fatigué de taper tout ce code à chaque fois que je veux dire "Bonjour, tout le monde !" Automatisons un peu plus les choses maintenant. Créez un fichier nommé Dockerfile
et ajoutez-y le contenu suivant :
# Dockerfile
FROM python:3
WORKDIR /src/app
COPY . .
CMD [ "python", "./hello.py" ]
Exécutez maintenant cette commande dans le même dossier dans lequel vous avez créé le Dockerfile
:
docker build -t hello .
Il ne reste plus qu'à devenir fou en utilisant ce code :
docker run hello
Vous savez déjà comment c'est. Prenons un moment pour comprendre comment fonctionne désormais un Dockerfile.
Le décomposer
En commençant par notre Dockerfile, la première ligne FROM python:3
indique à Docker de tout démarrer avec l'image de base que nous connaissons déjà, python:3
.
La deuxième ligne, WORKDIR /src/app
, définit le répertoire de travail à l'intérieur de notre conteneur. Il s'agit de certaines instructions que nous exécuterons plus tard, comme CMD
ou COPY
. Vous pouvez voir le reste des instructions prises en charge pour WORKDIR
ici.
La troisième ligne, COPY . .
indique essentiellement à Docker de tout copier de notre dossier actuel (premier .
) et de le coller sur /src/app
(deuxième .
). L'emplacement de collage a été défini avec la commande WORKDIR
juste au-dessus.
Remarque : Nous pourrions obtenir les mêmes résultats en supprimant l'instruction
WORKDIR
et en remplaçantCOPY . .
avecCOPY . /src/app
. Dans ce cas, nous devrons également remplacer la dernière instruction,CMD ["python", "./hello.py"]
enCMD ["python", "/src/app /bonjour.py"]
.
Enfin, la dernière ligne CMD ["python", "./hello.py"]
fournit la commande par défaut pour notre conteneur. Cela signifie essentiellement que chaque fois que nous exécutons
un conteneur à partir de cette configuration, il doit exécuter python ./hello.py
. Gardez à l'esprit que nous exécutons implicitement /src/app/hello.py
au lieu de seulement hello.py
, puisque c'est là que nous avons pointé notre WORKDIR
à.
Remarque : La commande
CMD
peut être écrasée au moment de l'exécution. Par exemple, si vous souhaitez exécuterbash
à la place, vous feriezdocker run hello bash
après avoir construit le conteneur.
Une fois notre Dockerfile terminé, nous allons de l'avant et commençons notre processus de build
. La commande docker build -t hello .
lit toute la configuration que nous avons ajoutée à notre Dockerfile et crée une image docker à partir de celle-ci. C'est vrai, tout comme l'image python:3
que nous avons utilisée pour tout cet article. Le .
à la fin indique à Docker que nous voulons exécuter un Dockerfile à notre emplacement actuel, et l'option -t hello
donne à cette image le nom hello
, afin que nous puissions facilement le référencer au moment de l'exécution.
Après tout cela, tout ce que nous avons à faire est d'exécuter l'instruction docker run
habituelle, mais cette fois avec le nom de l'image hello
en fin de ligne. Cela démarrera un conteneur à partir de l'image que nous avons récemment construite et enfin imprimera le bon vieux "Hello, World!" dans notre terminale.
Étendre notre image de base
Que faisons-nous si nous avons besoin d'une dépendance pour exécuter notre code qui n'est pas préinstallé avec notre image de base ? Pour résoudre ce problème, docker dispose de l'instruction RUN
.
En suivant notre exemple Python, si nous avions besoin de la bibliothèque numpy
pour exécuter notre code, nous pourrions ajouter l'instruction RUN
juste après notre commande FROM
.
# Dockerfile
FROM python:3
# NEW LINERUN pip3 install numpy
WORKDIR /src/app
COPY . .
CMD [ "python", "./hello.py" ]
L'instruction RUN
donne essentiellement une commande à exécuter par le terminal du conteneur. De cette façon, puisque notre image de base est déjà livrée avec pip3
installé, nous pouvons utiliser pip3 install numpy
.
Remarque : Pour une véritable application Python, vous ajouteriez probablement toutes les dépendances dont vous avez besoin à un fichier
requirements.txt
, le copieriez dans le conteneur, puis mettriez à jour leRUN
pourRUN pip3 install -r Requirements.txt
.
Partie 4. « Bonjour le monde ! » à partir d'une image Nginx à l'aide d'un conteneur détaché de longue durée
Je sais que vous en avez probablement marre de m'entendre le dire, mais j'ai encore un "Bonjour" à dire avant de partir. Allons de l'avant et utilisons notre puissance Docker nouvellement acquise pour créer un simple conteneur de longue durée, au lieu de ceux de courte durée que nous avons utilisés jusqu'à présent.
Créez un fichier index.html
dans un nouveau dossier avec le contenu suivant.
# index.html
<h1>Hello, World!</h1>
Maintenant, créons un nouveau Dockerfile dans le même dossier.
# Dockerfile
FROM nginx:alpine
WORKDIR /usr/share/nginx/html
COPY . .
Construisez l'image et donnez-lui le nom simple_nginx
, comme nous l'avons fait précédemment.
docker build -t simple_nginx .
Enfin, exécutons notre image nouvellement créée avec la commande suivante :
docker run --rm -d -p 8080:80 simple_nginx
Vous pensez peut-être que rien ne s'est passé parce que vous êtes de retour sur votre terminal, mais regardons de plus près avec la commande docker ps
.
La commande docker ps
affiche tous les conteneurs en cours d'exécution sur votre machine. Comme vous pouvez le voir dans l'image ci-dessus, j'ai actuellement un conteneur nommé simple_nginx
en cours d'exécution sur ma machine. Ouvrons un navigateur Web et voyons si nginx
fait son travail en accédant à localhost:8080
.
Tout semble fonctionner comme prévu et nous servons une page statique via le nginx
exécuté dans notre conteneur. Prenons un moment pour comprendre comment nous y sommes parvenus.
Le décomposer
Je vais ignorer l'explication de Dockerfile car nous avons déjà appris ces commandes dans la dernière section. La seule "nouvelle" chose dans cette configuration est l'image nginx:alpine
, dont vous pouvez en savoir plus ici.
Hormis ce qui est nouveau, cette configuration fonctionne car nginx
utilise le dossier usr/share/nginx/html
pour rechercher un fichier index.html
et commençons à le servir, donc puisque nous avons nommé notre fichier index.html
et configuré le WORKDIR
pour qu'il soit usr/share/nginx/html
, ceci la configuration fonctionnera dès la sortie de la boîte.
La commande build
est exactement comme celle que nous avons utilisée dans la section précédente, nous utilisons uniquement la configuration Dockerfile pour créer une image avec un certain nom.
Passons maintenant à la partie amusante, l'instruction docker run --rm -d -p 8080:80 simple_nginx
. Nous avons ici deux nouveaux drapeaux. Le premier est le drapeau détaché (-d
), ce qui signifie que nous voulons exécuter ce conteneur en arrière-plan, et c'est pourquoi nous sommes de retour à notre terminal juste après avoir utilisé le docker run
, même si notre conteneur est toujours en cours d'exécution.
Le deuxième nouvel indicateur est l'option -p 8080:80
. Comme vous l'avez peut-être deviné, il s'agit de l'indicateur port
, et il mappe essentiellement le port 8080
de notre machine locale au port 80
à l'intérieur de notre récipient. Vous auriez pu utiliser n'importe quel autre port au lieu de 8080
, mais vous ne pouvez pas modifier le port 80
sans ajouter un paramètre supplémentaire à l'image nginx
, car 80
est le port standard exposé par l'image nginx
.
Remarque : Si vous souhaitez arrêter un conteneur détaché comme celui-ci, vous pouvez utiliser la commande
docker ps
pour obtenir le nom du conteneur (pas l'image ), puis utilisez l'instructiondocker stop
avec le nom du conteneur souhaité en fin de ligne.
Partie 5. La fin
C'est ça! Si vous lisez encore ceci, vous disposez de toutes les bases pour commencer à utiliser Docker dès aujourd'hui sur vos projets personnels ou votre travail quotidien.
Faites-moi savoir ce que vous avez pensé de cet article dans les commentaires, et je m'assurerai d'écrire un article de suivi couvrant des sujets plus avancés comme docker-compose
quelque part dans un avenir proche.
Si vous avez des questions, n'hésitez pas à me le faire savoir.
Acclamations!