Skip to content

TP1 Part 2 : Dev environment

Dans cette partie, on va setup ce qu'on appelle "un environnement de dév Docker" : mettre le code d'une app dans un conteneur, et éventuellement tout ce dont elle a besoin dans des conteneurs aussi.

1. Introoo

D'abord, on va fabriquer notre conteneur qui va contenir notre application.

J'ai codé une app toute pourrie en Node pour illustrer cette partie. Vous la trouverez ici.

Dans la partie précédente, on a fait que se servir d'images officielles existantes : mysql:8.4 et phpmyadmin:latest.

Ici, pour faire les choses clean, on va fabriquer nous-mĂȘmes notre image.

Pour fabriquer une image, il faut :

  • Ă©crire un Dockerfile
  • lancer la commande docker build pour produire l'image Ă  partir du Dockerfile

Pour la construction de notre Dockerfile, on va :

  • partir d'une image officielle node
  • installer nos dĂ©pendances (mon app a juste besoin de Express)
  • dĂ©finir la commande Ă  lancer lorsqu'on docker run notre image

Let's goooo.


2. Dockerfile

➜ Dans votre dĂ©pĂŽt git de rendu, crĂ©er un nouveau sous-dossier

  • appelez-le part2/1/

🌞 RĂ©cupĂ©rez le package.json et app.js que je vous ai cook

  • dĂ©posez les fichiers comme suit :

    • part2/1/package.json
    • part2/1/src/app.js

🌞 Ajoutez un fichier part2/1/Dockerfile avec le contenu suivant :

# On part de l'image officielle node
FROM node

# On exécute des commandes mkdir pour préparer les dossiers qui vont accueillir notre code
RUN mkdir /app && mkdir /app/src

# On définit /app comme le "WORKDIR"
# A partir de cette ligne, toutes les commandes sont relatives au dossier /app
WORKDIR /app

# Copie du fichier package.json (de votre machine) dans le dossier "."
# "." fait référence au dossier actuel, qui est notre WORKDIR (donc c'est /app)
COPY ./package.json .

# Installation des dépendances
# Grùce à notre WORKDIR, cette commande est effectuée depuis le dossier /app de l'image
RUN npm install

# On copie le reste du code dans l'image
COPY ./src ./src

# On définit la commande à lancer lorsque le conteneur démarre
# Notez la syntaxe reloue (mais trĂšs secure) : sous forme de liste, pas d'espace
CMD [ "npm", "run", "dev" ]

🌞 Construire l'image shitty_app à partir de ce Dockerfile :

  • toujours depuis un shell, dĂ©placez vous dans le dossier qui contient ce Dockerfile
  • exĂ©cutez la commande docker build suivante :
# Déplacez vous dans le dossier qui contient vos fichiers
❯ cd part2/1

# Vous devriez avoir ça :
❯ tree -L2
.
├── Dockerfile
├── package.json
└── src
    └── app.js

# -t comme tag : on indique le nom de notre image (on la "tag")
# le "." c'est pour indiquer le chemin vers le "contexte de build" : lĂ  oĂč on trouve le Dockerfile notamment
❯ docker build -t shitty_app:1.0 .
[...]
Hint

Toujours : se déplacer dans le dossier qui contient le Dockerfile pour build (c'est le plus simple).

Info

Ici l'image s'appelle shitty_app et son tag c'est 1.0. Souvent on se sert du tag pour indiquer la version du programme.
Quand tu vas sur un README.md bien foutu du Hub, tu peux voir tous les tags disponibles pour une image donnée : https://hub.docker.com/_/python
Dans la vraie vie, on tag TOUJOURS les images explicitement, et on utilise jamais latest en production : on veut "pin" les versions : préciser explicitement quelle version on souhaite utiliser.

Docker build

🌞 Lancer l'application avec une commande docker run, cette commande :

  • lance l'image shitty_app
  • utilise option -p pour partager le port 3000 et ainsi accĂ©der Ă  l'interface web
  • utilise option -d pour mettre en fond

🌞 Prouvez que ça tourne, dans le compte-rendu, vous me mettez :

  • un docker ps qui montre que le conteneur est actif
  • un docker logs qui montre les logs du programme (on doit voir qu'il tourne sur le port 3000)
  • un curl vers la WebUI

3. Hot reload pleaaaase

A. Intro

Note

La lib nodemon déclarée dans le package.json va nous permettre de supporter le hot-reload.

Ok c'est cool tout est conteneurisé, mais grave chiant si on doit relancer docker build à chaque fois qu'on modifie le code.

Bah ouais, actuellement, le code est dans l'image : c'est dans le Dockerfile avec un COPY qu'on met notre app.js.

➜ Pour avoir l'environnement idĂ©al, on veut :

  • lancer notre conteneur
  • coder sur notre PC avec notre IDE de fouuu
  • que ça modifie en temps rĂ©el le code dans le conteneur qui tourne
  • que la page qui soit served soit mise Ă  jour si on actualise

Hot reload quoi !

➜ Pour ça, on va utiliser les volumes Docker qui permettent exactement ça :

  • partager un dossier (ou un fichier) de notre PC au conteneur
  • on le fait au moment du docker run (pas dans le Dockerfile)
  • le dossier est partagĂ© en temps rĂ©el avec le conteneur pendant que le conteneur vit

B. Do it

🌞 Lancer un nouveau conteneur à partir de l'image shitty_app

  • cette fois, vous ajoutez une option -v pour utiliser un volume
  • l'option -v doit partager le dossier src/ qui contient mon app.js de votre PC vers le dossier /app/src/ dans le conteneur
  • ça va donner un truc du genre :
docker run -p 3000:3000 -d -v "~/TP1/app/src:/app/src" shitty_app

🌞 VĂ©rifier que ça fonctionne

  • modifier le fichier app.js depuis votre PC (ajoutez un machin dans l'contenu HTML qui soit visible facilement sur un refresh)
  • vĂ©rifier que votre modif est visible en visitant la WebUI
  • pour le compte-rendu, je veux un docker logs sur votre conteneur : avec la lib nodemon il log quand il effectue un hot-reload (il dĂ©tecte la modification de fichiers)

4. Compose please

🌞 Transformer ce docker run en compose.yml

  • tout est dit : ajoutez un fichier compose.yml Ă  cĂŽtĂ© du Dockerfile
  • il lance shitty_app avec toutes nos ptites options (partage de ports + volume)
Note

On commence à s'approcher d'un truc vraiment clean et pratique là : tu veux dév ? Tu fais juste un docker compose up et go.
Aucune dépendance installée sur la machine.
Partage d'environnement super facile : suffit de partager le Dockerfile et le compose.yml deux simples fichiers texte, y'a tout dedans.
Et ca s'executera exactement pareil ailleurs.

Works on my machine


5. DB please

A. Ptite intro

On finit ce TP avec l'ajout d'une DB MySQL à cÎté d'une app Node, histoire d'avoir une stack un peu complÚte.

Au menu :

  • je vous file un nouveau app.js
  • vous construisez une nouvelle image shitty_app_with_db
  • vous Ă©crivez un compose.yml qui lance : une DB, un PHPMyAdmin, la shitty_app_with_db

On fait ça clean :

  • du hot-reload (donc vous dĂ©finissez un volume dans le compose.yml)
  • mon app attend les infos de connexions Ă  la base en variable d'environnement, il faudra les dĂ©finir dans le compose.yml

B. Feu

➜ Dans votre dĂ©pĂŽt git de rendu, crĂ©er un nouveau sous-dossier

  • appelez-le part2/5/

🌞 RĂ©cupĂ©rer la nouvelle app

Note

Respectez la mĂȘme structure qu'avant : avec le sous-dossier src/ qui contient app.js.

🌞 CrĂ©er un Dockerfile

  • dans part2/5/Dockerfile pour le dĂ©pĂŽt git de rendu
  • le mĂȘme Dockerfile qu'avant ça fait l'affaire nickel

🌞 CrĂ©er un compose.yml

  • rĂ©cupĂ©rez celui de la partie 1 et celui de la partie 2 : fusioooon
  • ce compose.yml doit dĂ©clarer trois conteneurs :

    • une DB MySQL en 8.4

      • dĂ©clarĂ©e avec le nom db
    • un PHPMyAdmin

      • partage de ports pour accĂ©der Ă  l'interface
    • la shitty_app_with_db accessible sur votre port 3000

      • partage de ports pour accĂ©der Ă  l'interface
      • volume qui partage le fichier app.js pour supporter le hot-reload
      • variables d'environnement pour donner les infos de connexion Ă  la db (voir la section "Info" juste en dessous)
Info

Les variables à déclarer sont les classiques hein : adresse IP de la base, le port, nom du user et password pour se connecter, nom de la database.
Ca donne donc une section environment comme ça avec les variables que j'utilise dans le code :

environment:
  - DB_HOST=db # en supposant que vous avez bien déclaré votre conteneur MySQL avec le nom "db"
  - DB_USER=root
  - DB_PASSWORD=<TON_PASSWORD>
  - DB_NAME=<NOM_DE_DB>
  - DB_PORT=3306
  - PORT=3000 
Remplacez bien <TON_PASSWORD> par la mĂȘme valeur que le MYSQL_ROOT_PASSWORD du conteneur MySQL.
Et aussi remplacer <NOM_DE_DB> par un nom de votre choix : la database est créée par app.js quand le programme démarre :)

➜ Vous devriez donc avoir quelque chose comme :

❯ cd part2/5

❯ tree -L2
.
├── compose.yml
├── Dockerfile
├── package.json
└── src
    └── app.js

2 directories, 4 files

🌞 Allumer la stack et prouver que ça fonctionne

  • docker compose up ! (depuis le dossier part2/5/)
  • visitez l'interface : http://localhost:3000
  • pour le compte-rendu, je veux :

    • un docker ps qui montre les conteneurs actifs
    • un docker compose logs qui montre les logs de nos 3 conteneurs
    • un curl vers la WebUI de PHPMyAdmin
    • un curl vers la WebUI de la shitty_app_with_db

C. Données persistentes

Haaaaa avant de partir. Si on supprime la DB on perd toutes les data lĂ .

Info

Quand vous faites un docker compose down ça supprime les conteneurs.
C'est ce que vous voulez : les conteneurs permettent une approche "lancer, détruire, relancer".
Et c'est désirable : t'as pas un truc qui marche mais tu sais plus trop comment.
Là c'est reproductible et maßtrisé.

Ca serait bien de pouvoir conserver les datas, mĂȘme si notre conteneur de db il meurt ou est supprimĂ©.

L'idée est simple : on utilise un volume pour que le conteneur stocke en réalité les données de db sur notre machine hÎte.

🌞 Mettre en place des donnĂ©es persistentes pour la db


T'as fini le TP je crois, gg :d
Oublie pas de m'envoyer le lien vers ton repo git par Discord 🐈