• 12 heures
  • Difficile

Ce cours est visible gratuitement en ligne.

course.header.alt.is_certifying

J'ai tout compris !

Mis à jour le 25/09/2024

Créez votre conteneur

Découvrez les caractéristiques d’un conteneur

Maintenant que vous connaissez les différents personas interagissant avec Kubernetes, et leurs différentes missions, David vous demande de travailler avec Alice pour déployer votre premier conteneur.

Votre mission sera de créer le conteneur du site e-commerce de LiveCorp. Il faudra alors produire le fichier pour concevoir ce conteneur, puis le construire et enfin le stocker afin de le diffuser pour le déployer sur Kubernetes.

Mais c’est quoi un conteneur au fait ?

Avant la création des conteneurs, la façon de déployer une application dépendait souvent de l’environnement. Malheureusement, chaque équipe avait sa propre façon de déployer son application.

Mais plus les déploiements se rapprochent de la production, plus le déploiement doit être standardisé. Par exemple, pour le site e-commerce, il est possible que les développeurs n’aient pas la même version des frameworks sur l’environnement de développement que celui de production. De plus, l’environnement de production est souvent plus sécurisé que celui de développement, ce qui amène à des différences de comportements entre les environnements.

Essayons de faire un parallèle avec le transport maritime (puisqu’on parle de conteneurs). Avant les années 1960, le transport par cargos vivait la même chose. Chaque transporteur avait sa façon de transporter les biens. Par exemple, le transport sera différent si c’est du café, des céréales ou des voitures. Même chose pour le moyen de transport qui sera plus efficace par bateau que par camion.

En 1960 a été inventé le conteneur intermodal. Ceux qui envoyaient des biens s'occupaient de les conteneuriser et étaient responsables de ce qu’il y a à l’intérieur du conteneur. Les transporteurs étaient responsables du conteneur en lui-même. De plus, ce conteneur intermodal étant standardisé, cela a permis le développement de porte-conteneurs beaucoup plus efficaces.

Attendez, vous voulez dire que c’est la même chose pour les conteneurs informatiques ?

Tout à fait ! La création de la technologie des conteneurs a pu standardiser la façon de déployer une application, quel que soit l’environnement utilisé.

Les développeurs sont alors responsables de fournir un conteneur qui fonctionne, alors que les ops sont responsables de faire tourner correctement les conteneurs.

Un conteneur est une unité logicielle portable qui regroupe le code d'une application, ses ressources et ses dépendances dans un package isolé. Les conteneurs sont légers et rapides à exécuter, car ils ne contiennent pas leur propre système d'exploitation complet. Ils partagent le noyau du système d'exploitation hôte, ce qui réduit considérablement leur taille et leur complexité.

Et comment fabrique-t-on ces conteneurs ?

Maintenant que vous savez ce qu’est un conteneur et à quoi il sert, il est justement temps de voir comment le créer. C’est assez simple, vous allez voir.

Le Dockerfile

Afin d'utiliser un conteneur applicatif, vous devez créer tout d’abord une image à l’aide d’un Dockerfile. Une image est un package autonome contenant tout ce dont une application a besoin pour s'exécuter : le code, les dépendances, les bibliothèques, et les configurations.

Le Dockerfile est la recette de cuisine afin de créer une image. C’est un fichier texte qui définit les instructions pour construire l’image. Il consiste en une série d'instructions, chacune exécutée dans un environnement isolé. Chaque instruction exécutée va créer une nouvelle couche dans l’image, couche qui est immuable.

L’immuabilité de ces couches permet de réutiliser des couches qui existent déjà, ou de les utiliser comme cache à chaque reconstruction de l’image. Pour savoir si une couche a changé ou pas, les conteneurs s'appuient sur un algorithme de hachage, le SHA1 (Secure Hash Algorithm 1) de chaque couche. Si une instruction change, le SHA1 de la couche changera.

Le Dockerfile va alors créer une image immuable qui contiendra exactement ce qui a été spécifié dans le Dockerfile. Si une instruction change, il est donc nécessaire de reconstruire une nouvelle image en relançant le processus de build.

Dans ce modèle, les systèmes d'exploitation, les applications et les configurations sont définis comme des images immuables qui ne sont jamais modifiées une fois déployées en production.

Cela a plusieurs avantages comme la reproductibilité, l’automatisation et la facilité de gestion. Les modifications de l'infrastructure sont effectuées en déployant de nouvelles images, ce qui minimise les risques d'erreurs de configuration.

Comme les images ne changent pas, l'infrastructure peut être facilement reproduite dans différents environnements en déployant les mêmes images.

Enfin, ces images peuvent être facilement versionnées et gérées à l'aide d'outils tels qu’un registre d’images.

L'utilisation de conteneurs et l'adoption de l'infrastructure immuable permettent de créer des applications et des systèmes plus fiables, évolutifs et faciles à gérer.

Lors de la création de l’image du site e-commerce, vous allez donc créer une image immuable qui sera la même entre les différents environnements. Les problèmes inhérents aux différences entre environnements sont donc résolus.

Créez votre première image Docker

Installez Docker

Avant de pouvoir créer et construire votre première image Docker, vous devez installer Docker sur votre poste de travail. Vous pouvez télécharger Docker à ce lien.

Une fois l’installation terminée, il est maintenant temps de créer l’image du site e-commerce ! Pour créer cette image, il faut passer par deux étapes : la création d’un Dockerfile et le build de l’image à partir du Dockerfile.

Vous demandez à Alice de vous expliquer le contenu d’un Dockerfile. Alice vous guide alors pas à pas pour créer votre premier Dockerfile.

La création du Dockerfile

La première instruction d’un Dockerfile est toujours FROM. Cette instruction indiquera au conteneur de quelle image de base commencer. L’image de base la plus simple est scratch, qui ne contient absolument rien. Cependant, des images déjà préconçues et maintenues sont disponibles sur le hub Docker. Ces images peuvent être des images de base comme des systèmes d’exploitation comme Debian ou Ubuntu ; des langages de développement comme Python ou Java ; ou encore des middlewares comme Nginx ou MySQL.

La première étape est de créer un fichier Dockerfile à la racine du projet avec comme première instruction  FROM  . Cette instruction sera l’image de base de laquelle vous allez partir pour créer votre image. Dans notre cas, nous allons utiliser l’image  node:18-alpine

FROM node:18-alpine

Une fois l’image définie, il va falloir se placer dans le répertoire de travail du conteneur. C’est là qu’intervient l’instruction  WORKDIR  , qui définit le répertoire courant. Dans notre cas, le répertoire sera  /app  .

WORKDIR /app

Vous devez ensuite copier toute l’application présente à la racine du projet dans le conteneur avec l’instructionCOPY

COPY . .

Une fois l’application copiée, vous devez alors compiler et installer l’application dans le conteneur. Dans le cas de node, cela passe par la commandeyarn install --production  . L’instruction pour exécuter cette commande est  RUN

RUN yarn install --production 

Maintenant que l’application est compilée et installée, vous devez indiquer au conteneur quelle commande exécuter au lancement du conteneur. L’instruction  CMD  indiquera alors la commande à exécuter.

CMD ["node", "src/index.js"]

Enfin, la dernière instruction à ajouter est l’instruction  EXPOSE  qui indique sur quel port écoute l’application. Cela sera important pour la suite du cours.

EXPOSE 3000

Votre Dockerfile doit maintenant ressembler à cela :

FROM node:18-alpine
WORKDIR /app
COPY . .
RUN yarn install --production
CMD ["node", "src/index.js"]
EXPOSE 3000

La construction de l’image

La création du Dockerfile en lui-même n’est qu’une étape dans la création de votre image. Vous devez maintenant créer l’image.

La commande à exécuter est :

docker build .

Le  .  est le répertoire où se trouve le Dockerfile ; dans notre cas, à la racine de notre projet.

Vous pouvez vérifier que l’image a bien été créée avec la commande docker images

docker images
REPOSITORY   TAG   IMAGE ID   CREATED     SIZE
<none>   <none> 9f7322572dcd   7 seconds ago   220MB

Vous remarquez que votre image n’a pas de nom, ce qui va être compliqué au moment de l’exécuter.

Pour ajouter un nom à votre image, vous devez ajouter le flag  -t  et lui donner un nom :

docker build . -t mon_image

Maintenant, votre image a bien son petit nom :

docker images
REPOSITORY   TAG   IMAGE ID   CREATED     SIZE
mon_image latest 9f7322572dcd   2 minutes ago   220MB

Dans la suite du chapitre, vous découvrirez comment relier plusieurs conteneurs ensemble par le réseau.

Comprenez le fonctionnement du réseau dans les conteneurs

Quand vous exécutez une image, chaque conteneur va recevoir sa propre adresse IP, différente de celle de la machine sur laquelle il s’exécute.

Mais comment les conteneurs peuvent se voir et discuter entre eux ?

Par défaut, lorsque vous lancez un conteneur, il est créé sur un réseau "bridge". Ce réseau virtuel est indépendant du réseau physique de votre machine hôte et permet aux conteneurs de communiquer entre eux de manière isolée.

Cependant, ce réseau étant indépendant du réseau de la machine, seuls les conteneurs peuvent discuter entre eux via le port exposé dans le Dockerfile.

C’est là où l’instruction  EXPOSE  du Dockerfile prend tout son sens.

Afin d’exposer ce port publiquement, il faut le mapper sur un port de la machine. Seulement dans ce cas l’application conteneurisée pourra discuter avec le monde extérieur.

Voici la commande pour exposer le conteneur publiquement :

docker run -p 8080:80 nginx

Lister les réseaux existants

La commande suivante permet de lister tous les réseaux existants sur votre machine:

docker network ls
NETWORK ID NAME                   DRIVER SCOPE
d5f8055687f5   bridge                 bridge local
36ad987601e3  host                   host    local
a53fbadf9181    minikube               bridge local
2a2fd0fe463c   none                   null     local

Vous retrouvez les trois réseaux précédemment cités, “bridge”, “host” et “null”. Un quatrième réseau a été créé par minikube afin de pouvoir exposer vos futurs déploiements.

Créer un réseau

La commande suivante permet de créer votre propre réseau pour faire discuter vos différents conteneurs :

docker network create --driver bridge mon-bridge
cb2cfcb6b07220e308c6b292ba751deeab8a56ebbd5d6655008a01e1a82d9bf9
docker network ls
NETWORK ID NAME                   DRIVER SCOPE
d5f8055687f5   bridge                 bridge local
36ad987601e3  host                   host    local
a53fbadf9181    minikube               bridge local
cb2cfcb6b072   mon-bridge             bridge local
2a2fd0fe463c   none                   null     local

Ici, vous pouvez voir qu’un nouveau réseau a été créé. Vous pouvez alors l'inspecter afin de voir ses caractéristiques :

docker network inspect mon-bridge
[
{
"Name": "mon-bridge",
"Id": "cb2cfcb6b07220e308c6b292ba751deeab8a56ebbd5d6655008a01e1a82d9bf9",
"Created": "2024-05-26T15:57:11.116617718Z",
"Scope": "local",
"Driver": "bridge",
"EnableIPv6": false,
"IPAM": {
"Driver": "default",
"Options": {},
"Config": [
{
                 "Subnet": "172.20.0.0/16",
                 "Gateway": "172.20.0.1"
}
]
},
"Internal": false,
"Attachable": false,
"Ingress": false,
"ConfigFrom": {
"Network": ""
},
"ConfigOnly": false,
"Containers": {},
"Options": {},
"Labels": {}
}
]

La commande vous montre alors les différentes options appliquées pour la création des conteneurs dans ce réseau. Les deux options importantes ici sont Subnet et Gateway. Subnet est le sous réseau utilisé par les conteneurs. Lors de la création d’un conteneur, le conteneur prendra alors une adresse IP au hasard dans le pool disponible. Gateway est la passerelle de sortie si un des conteneurs dans le sous réseau ne connaît pas l’adresse IP recherchée.

Maintenant, si vous connectez deux conteneurs à ce réseau, les deux conteneurs pourront se parler entre eux grâce à leur adresse IP.

docker run -dit --name alpine1 --network mon-bridge alpine
docker run -dit --name alpine2 --network mon-bridge alpine
docker network inspect mon-bridge
[
{
"Name": "mon-bridge",
"Id": "cb2cfcb6b07220e308c6b292ba751deeab8a56ebbd5d6655008a01e1a82d9bf9",
"Created": "2024-05-26T15:57:11.116617718Z",
"Scope": "local",
"Driver": "bridge",
"EnableIPv6": false,
"IPAM": {
"Driver": "default",
"Options": {},
"Config": [
{
"Subnet": "172.20.0.0/16",
"Gateway": "172.20.0.1"
}
]
},
"Internal": false,
"Attachable": false,
"Ingress": false,
"ConfigFrom": {
"Network": ""
},
"ConfigOnly": false,
"Containers": {
"0fad59687105db4154a3f953e919039ac2146948288971a4d4616acd3c0380d2": {
"Name": "alpine1",
"EndpointID": "25449b5ea212cb310824e06b52ca37b43a648e304ef4829036af036bc7a1ee62",
"MacAddress": "02:42:ac:14:00:02",
"IPv4Address": "172.20.0.2/16",
"IPv6Address": ""
},
"2822a672c1bf4fb5f7e89fa21cddd03b89bd302aac614636eb2473df0cc4c1f0": {
"Name": "alpine2",
"EndpointID": "384e892be94c6a540b18c18f4728b9c5f13699e66d20b5f5201d4a1a1cb93dd3",
"MacAddress": "02:42:ac:14:00:03",
"IPv4Address": "172.20.0.3/16",
"IPv6Address": ""
}
},
"Options": {},
"Labels": {}
}
]

Les deux conteneurs peuvent maintenant discuter entre eux. Lors de l’inspection du réseau, le conteneur “alpine1” a l’adresse IP 172.20.0.2 et le conteneur “alpine2” a l’adresse IP 172.20.0.3.

Vous pouvez alors vous connecter sur les deux conteneurs et faire un ping de l’autre conteneur.

docker exec alpine1 ping -c 1 172.20.0.3
PING 172.20.0.3 (172.20.0.3): 56 data bytes
64 bytes from 172.20.0.3: seq=0 ttl=64 time=0.101 ms
docker exec alpine2 ping -c 1 172.20.0.2
PING 172.20.0.2 (172.20.0.2): 56 data bytes
64 bytes from 172.20.0.2: seq=0 ttl=64 time=0.101 ms

La communication entre les deux conteneurs passe alors par le réseau “mon-bridge”.

Mais le moteur d’exécution permet aussi de faire de la résolution de nom DNS automatiquement. Il n’est alors plus nécessaire de connaître l’adresse IP du conteneur, mais seulement son nom, généralement automatiquement généré par le moteur d’exécution.

docker exec alpine1 ping -c 1 alpine2
PING 172.20.0.3 (172.20.0.3): 56 data bytes
64 bytes from 172.20.0.3: seq=0 ttl=64 time=0.101 ms
docker exec alpine2 ping -c 1 alpine1
PING 172.20.0.2 (172.20.0.2): 56 data bytes
64 bytes from 172.20.0.2: seq=0 ttl=64 time=0.101 ms

Déployez votre conteneur

Maintenant que vous savez builder votre image, il est temps de la déployer dans minikube.

Tout d’abord, il va falloir indiquer à Docker où builder votre image. Effectivement, pour l’instant votre image n’est disponible qu’en local et minikube n’y a pas accès. Il va donc falloir rebuilder votre image afin que celle-ci soit disponible pour minikube. Pas d’inquiétude, cette étape est rapide !

À la racine de votre projet, où se trouve le Dockerfile que vous avez créé, exécutez les commandes suivantes :

eval $(minikube docker-env)
docker build . -t mon_image

Ces commandes vont alors reconstruire l’image et la rendre disponible pour minikube. Une fois l’image finie d’être construite, vous allez pouvoir la déployer dans minikube avec les commandes suivantes :

minikube kubectl -- create deployment hello-minikube --image=mon_image
minikube kubectl -- expose deployment hello-minikube --type=LoadBalancer --port=3000

Ces deux commandes vont indiquer à minikube de déployer votre image et de la rendre accessible sur le port 3000.

Une fois le déploiement terminé, l’application devrait être accessible sur le port 3000 de votre machine.

Dans le screencast qui suit, vous verrez comment j'ai fait pour créer un Docker file et la première image Docker :

Dans ce screencast, vous avez vu comment :

  • créer un Docker File

  • créer la première image Docker

Vous verrez la suite des opérations dans le chapitre suivant et le prochain screencast.

À vous de jouer

Contexte

Alice vous demande maintenant de créer l’image du site e-commerce. Le site est basé sur Java et écoute sur le port 8080.

Alice vous envoie un mail afin de conteneuriser la nouvelle livraison de l’application. Dans ce mail se trouvent les instructions ainsi qu’un zip contenant l’application.

Tu trouveras dans ce mail la nouvelle version de l’application. Ta mission est de créer une image contenant l’application afin de la déployer. L’application est une application Java qui écoute sur le port 8080. La commande à lancer estjava -jar monapplication.jar

Bon courage !

Consignes

  • Créez un Dockerfile ;

  • Buildez l’image ;

  • Démarrez l’image.

En résumé

  • Le Dockerfile sert à créer une image.

  • C’est une suite d’instructions qui indique comment créer l’image.

  • L’image doit être reconstruite à chaque changement.

  • Chaque conteneur reçoit sa propre adresse IP.

  • Le moteur d’exécution s’occupe de connecter les conteneurs entre eux par le réseau.

  • Les conteneurs se connectent par un port exposé sur la machine.

Dans le prochain chapitre, vous allez apprendre à rendre disponible publiquement votre image nouvellement créée dans une registry.

Exemple de certificat de réussite
Exemple de certificat de réussite