Recherche de site Web

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 fichier hello.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'image python:3. Certaines images sont si simples qu'elles n'ont même pas de bash. 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çant COPY . . avec COPY . /src/app. Dans ce cas, nous devrons également remplacer la dernière instruction, CMD ["python", "./hello.py"] en CMD ["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écuter bash à la place, vous feriez docker 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 le RUN pour RUN 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'instruction docker 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!

Articles connexes