Hey, content de vous retrouver dans cette seconde partie de notre tutoriel. Nous allons parler particulièrement de conteneurs linux et docker.

N° 2 CONTAINERIZATION

Prérequis:

C'est quoi un container d'application ?

Les conteneurs Linux vous permettent d'assembler et d'isoler des applications avec leur environnement d'exécution complet contenant tous les fichiers nécessaires à leur exécution. Les applications conteneurisées sont plus faciles à déplacer d'un environnement à un autre (développement, test, production, etc.), tout en conservant l'intégralité de leurs fonctions.

by source redhat site officiel (je vous conseille fortement de jeter un coup d'oeil )

C'est quoi docker ?

Le terme « Docker » désigne plusieurs choses : le projet d'une communauté Open Source, les outils issus de ce projet Open Source, l'entreprise Docker Inc. qui constitue le principal soutien de ce projet, ainsi que les outils que l'entreprise prend officiellement en charge. Des technologies et une entreprise qui partagent le même nom, cela peut prêter à confusion.

Voici donc une rapide explication de cet état de fait.

  • Le logiciel « Docker » est une technologie de conteneurisation qui permet la création et l'utilisation de conteneurs Linux®.
  • La communauté Open Source Docker travaille à l'amélioration de cette technologie disponible gratuitement pour tout le monde.
  • L'entreprise Docker Inc. s'appuie sur le travail de la communauté Docker, sécurise sa technologie et partage ses avancées avec tous les utilisateurs. Elle prend ensuite en charge les technologies améliorées et sécurisées pour ses clients professionnels.

Avec la technologie Docker, vous pouvez traiter les conteneurs comme des machines virtuelles très légères et modulaires. En outre, ces conteneurs vous offrent une grande flexibilité : vous pouvez les créer, déployer, copier et déplacer d'un environnement à un autre, ce qui vous permet d'optimiser vos applications pour le cloud.

by redhat source officiel (je vous conseille fortement de jeter un coup d'œil )

Alors comment conteneurisé notre application avec docker ?

Pour qu'une application puisse tourner a l'intérieur d'un conteneur docker, il va falloir créer une image docker.

Une image docker c'est le snapshot de votre application a l'état ready to use (prêt a se lancer partout où il y a un docker-daemon peut importe le système d'exploitation/).

Comment créer une image docker de notre application ?

Pour créer une image docker, il faut qu'on décrive comment notre application va tourner avec toutes ses dépendances et metadata.

Pour décrire comment l'application vas tourner, nous avons besoin de fichier de description a base de (key:value) clé:valeur qu'on appelle communément le Dockerfile. apprendre plus

Sans plus tarder, créons notre Dockerfile, touch Dockerfile  ou avec votre éditeur.

$ touch Dockerfile
#La directive from telecharge une 
#image sur le dockerhub ici node
FROM node:12.18.3-alpine3.9

#Cette directive permet de 
#mettre des infos sur la personne
#ou la societe qui maintient l'image
MAINTAINER yayamombe090@gmail.com

#WORDIR creer un dossier app a partir
#de la racine principal du conteneur
WORKDIR /app 

#COPY comme son nom l'indiqu
#il copy des donnees de l'ordinateur
#physique dans le conteneur
COPY package.json .

#RUN permet d'executer une commande
#linux dans le conteneur
RUN yarn

COPY . .

#Permet d'ouvrir le port 
#sur lequel notre serveur web ecoute
EXPOSE 3000

#CMD c'est la commande qui sera exec
#lorsque le conteneur vas monte
CMD ["yarn", "start"]

Jusque là tout va bien (so far so good), nous avons notre Dockerfile, comment créer une image a partir de ce fichier ?

C'est très simple, mais avant qu'on oublie, créons rapidement un autre fichier qui s'appellera .dockerignore, oui ça vous rappelle quelque chose je vous vois venir.

Et bien comme .gitignore, ce fichier permet de dire à Docker d'ignorer les dossiers et fichiers qu'on vas lui donner. Cela nous permettra de gagner en temps de build car docker ne va pas chargé les dossiers lourd comme le node_modules, vendors dans le daemon avant de build le script etc.

$ touch .dockerignore

.dockerignore ajouter le contenu ci-dessous.

node_modules
idea

Pour créer une image de notre application, nous allons builder le Dockerfile avec la commande suivante:

 $ build -t simple-node-app-image .

$ docker images | grep simple-node-app-image

Le résultat :

simple-node-app-image latest [img id] [created time ago] 104MB

Alors, si vous voyez une ligne qui ressemble à celle ci-haut, félicitations vous avez Builder votre image docker avec succès. Bravo !!!!

Oui maintenant qu'on a notre image, comment démarrer un conteneur avec Docker?

Rien de plus simple, avant de vous montrer la commande, essayons d'expliquer le résultat du filtre de nos images mais si vous venez d' installer docker vous ne risquez pas d'avoir beaucoup. Dans mon cas il y en a déjà pas mal d'où le filtre. 

  • simple-node-app-image est le nom ou tag qu'on avait choisi lors du build de l'image.
  • latest c' est la version de l'image si vous ne mettez rien lors du build alors docker choisit latest pour vous, nous y reviendrons plus tard
  • la valeur hashée est l'ID de votre image (identifiant unique de ce dernier)
  • x [minutes,hours,days,etc... ago] date de création de l'image
  • XMB est la taille de votre image.
docker run --name simple-node-app-container -p 3001:3000 -d simple-node-app-image:latest 230e6b2690dd7e47e86a8df1f4f28087a6099e234ad5c2d2f1a451fdc9d2cc11

Si vous recevez un id comme ça, c'est que tout s'est bien passé. Pour se rassurer encore d'avantage, on vas logger le conteneur pour être sûr qu'il y a aucune erreur.

$ simple-node-app docker logs simple-node-app-container
$ yarn run v1.22.4
$ node src/app.js
{"level":30,"time":1604199921438,"pid":27,"hostname":"230e6b2690dd","msg":"Server listening at http://127.0.0.1:3000"}
{"level":30,"time":1604199921439,"pid":27,"hostname":"230e6b2690dd","msg":"Server a bien demarrer sur l'adresse suivante http://127.0.0.1:3000"}

Super, maintenant qu'on est rassuré on vas essayer d'envoyer une requête http a l'adresse http://127.0.0.1:3000 

http localhost:3000

http: error: ConnectionError: HTTPConnectionPool(host='localhost', port=3000): 
Max retries exceeded with url: / (Caused by NewConnectionError(
'<urllib3.connection.HTTPConnection object at 0x107330850>: Failed to establish a new connection: [Errno 61] Connection refused')) 
while doing GET request to URL: http://localhost:3000/

http 127.0.0.1:3000

http: error: ConnectionError: HTTPConnectionPool(host='127.0.0.1', port=3000):
 Max retries exceeded with url: / (Caused by NewConnectionError(
'<urllib3.connection.HTTPConnection object at 0x10b5ba8d0>: Failed to establish a new connection: [Errno 61] Connection refused')) 
while doing GET request to URL: http://127.0.0.1:3000/

oups, ça ne marche pas et pourtant les logs nous disent bien que le serveur est up et attend des requêtes fresh. Ok revoyons la commande du run encore

$ docker run --name simple-node-app-container -p 3001:3000 -d simple-node-app-image:latest
  • docker c'est le client docker, plus besoin de vous réexpliquer ça, vous êtes des experts fresh
  • run c'est l'option qui permet de lancer le conteneur 
  • --name on baptise notre conteneur, si on le fait pas docker le fera pour nous avec un joli nom du genre angris_bird
  • -p hum toi la, tu es bizarre hein il y a 3001:3000 c'est quoi ça encore ? En fait c'est ce qu'on appel le port forwarding, en gros le port sur lequel tourne le serveur web dans le conteneur est 3000 et nous on l'a mappé le port 3001 sur notre ordinateur physique. Donc la requête doit venir sur le 3001 non sur 3000, faites très attentions c'est des erreurs de débutant qui fatiguent un peu. Réessayons voir 
http localhost:3001

http: error: ConnectionError: 
('Connection aborted.', RemoteDisconnected('Remote end closed connection without response')) 
while doing GET request to URL: http://localhost:3001/

Nous qui croyions avoir fini avec les erreurs de connexion. Oups c'est quoi ça encore, mais quand on analyse bien les logs sur serveur #fastify dans le conteneur il nous dit ceci :

{"level":30,"time":1604199921439,"pid":27,"hostname":"230e6b2690dd","msg":"Server a bien demarrer sur l'adresse suivante http://127.0.0.1:3000"}

127.0.0.1 ou localhost dans le conteneur veut dire tout simplement que le serveur web n'est pas ouvert à tout le monde ici nous sur la machine physique, alors rentrons dans le conteneur voyons ce qu'il en est:

$ simple-node-app docker exec -it simple-node-app-container /bin/sh/app # hostname 230e6b2690dd

A présent, nous sommes à l'intérieur du conteneur, essayons #curl, mais on nous dit que curl est absent. Normal les conteneurs se la jouent toujours minimaliste c'est à dire qu'ils n'Install que le juste nécessaire pour démarrer votre application a moins que vous le mentionnez dans le Dockerfile.

/app # curl
/bin/sh: curl: not found

Ici je veux juste installer curl a l'intérieur du conteneur avec le gestionnaire de package de la distro linux en l'occurrence ici #alpine.

/app # apk update
fetch http://dl-cdn.alpinelinux.org/alpine/v3.9/main/x86_64/APKINDEX.tar.gz
fetch http://dl-cdn.alpinelinux.org/alpine/v3.9/community/x86_64/APKINDEX.tar.gz
v3.9.6-115-g99db9460e9 [http://dl-cdn.alpinelinux.org/alpine/v3.9/main]
v3.9.6-114-ga7f52c345a [http://dl-cdn.alpinelinux.org/alpine/v3.9/community]
OK: 9775 distinct packages available

/app # apk add curl
(1/5) Installing ca-certificates (20191127-r2)
(2/5) Installing nghttp2-libs (1.35.1-r2)
(3/5) Installing libssh2 (1.9.0-r1)
(4/5) Installing libcurl (7.64.0-r4)
(5/5) Installing curl (7.64.0-r4)
Executing busybox-1.29.3-r10.trigger
Executing ca-certificates-20191127-r2.trigger
OK: 9 MiB in 21 packages

Maintenant réessayons avec curl -X GET http://127.0.0.1:3000 a l'intérieur du conteneur.

/app # curl http://127.0.0.1:3000
{"name":"Simple NodeJs App","deploy_type":"Cloud Native","deploy_on":"230e6b2690dd","Author":"mombe090"}
/app # 

Hum donc il écoute à l'intérieur du conteneur, maintenant disons à #fastify de laisser les requêtes externes rentrer en ajoutant 0.0.0.0 juste après le 3000 listen port.

//Demarrer le serveur fastify.
fastify.listen(3000, '0.0.0.0', (err, address) => {
    if (err) throw err
    fastify.log.info(`Server a bien demarrer sur l'adresse suivante ${address}`)
});
#Executer les cmd ci-dessous
docker build -t simple-node-app-image .  
docker rm simple-node-app-container -f
docker run --name simple-node-app-container -p 3001:3000 -d simple-node-app-image:latest

Réessayons la requête depuis l'ordinateur physique:

➜  simple-node-app http localhost:3001
HTTP/1.1 200 OK
Connection: keep-alive
Date: Sun, 01 Nov 2020 03:43:24 GMT
content-length: 104
content-type: application/json; charset=utf-8

{
    "Author": "mombe090",
    "deploy_on": "0f05d42cd0b7",
    "deploy_type": "Cloud Native",
    "name": "Simple NodeJs App"
}

Yes Working like a Charm, bravoo vous venez de builder votre image docker et de démarrer votre conteneur 

1. Précedant #RestAPI #nodejs avec fastify


Partager cet article