Exercice : Node.js en local vs Node.js dans Docker

Informations générales

Élément

Description

Compétences

Concevoir, conteneuriser et documenter un service applicatif en distinguant clairement exécution locale et en conteneur.

Objectifs

À la fin de cette séquence, vous saurez : (1) exécuter la même appli Node.js en local et dans Docker, (2) expliquer les différences d’environnement (réseau, FS, variables, versions), (3) choisir le mode adapté (dev vs prod).

Durée estimée

45–60 min

Répertoire de travail

C:\Docker\docker-dockerfile (Windows) ou ~/docker/docker-dockerfile (Linux)

Fichiers sources

📥 server.js📥 package.json

À produire

Un document (PDF ou papier) avec vos réponses + captures (commandes, sorties), et les fichiers Dockerfile, .dockerignore.

Moyens d’aide

Le support de cours

Pré-requis

Node.js installé en local (n’importe quelle LTS), Docker Desktop ou Docker Engine.


Partie A — Exécution locale (sans Docker)

  1. Installez les dépendances puis lancez l’appli :

    npm ci            # ou npm install si pas de package-lock.json
    npm run start
    
  2. Relevez :

    • Version de Node locale : node -v__________

    • Port d’écoute (attendu: 3000) : __________

    • Répertoire courant (process.cwd() affiché par l’appli) : __________

  3. Modifiez une ligne dans server.js. L’application se met-elle à jour sans relance ? Expliquez :

    Réponse synthétique (1–2 phrases) :
    
  4. Test réseau :

    • Depuis le poste : curl http://localhost:3000 → OK ? OUI/NON

    • Depuis une autre machine du LAN : http://<IP-poste>:3000 → OK ? OUI/NON (si NON, expliquez pourquoi).


Partie B — Conteneurisation (Docker)

B1. Dockerfile de base (prod simple)

Créez un Dockerfile (Node 22 Alpine) :

# syntax=docker/dockerfile:1
FROM node:22-alpine

WORKDIR /app
COPY package*.json ./
RUN npm ci --only=production

COPY . .
EXPOSE 3000
CMD ["npm","run","start"]

Ajoutez un .dockerignore minimal :

node_modules
npm-debug.log
Dockerfile
.dockerignore

B2. Build & run

  1. Construisez et taguez l’image :

    docker build -t node-vs-docker:1.0 .
    
  2. Démarrez le conteneur (nom: app1) et publiez le port :

    docker run -d --name app1 -p 3000:3000 node-vs-docker:1.0
    
  3. Vérifications (à noter dans le rendu) :

    • Accès depuis l’hôte : curl http://localhost:3000OUI/NON

    • Version de Node dans le conteneur :

      docker exec app1 node -v  # → ________
      
    • Répertoire courant vu par l’app (/app attendu) : __________

B3. Différences observées (local vs Docker)

Complétez le tableau :

Aspect

Local

Docker

Différence / Explication (1–2 phrases)

Version de Node

Hot reload après édition

Chemin de travail

Port d’accès depuis autre machine

Accès au FS hôte

Variables d’environnement


Partie C — Mode développement Docker (montage de volume)

Objectif : observer le hot reload et la séparation code/image.

  1. Relancez en montant le code local dans le conteneur :

    docker rm -f app1
    docker run -d --name app1      -p 3000:3000      -v "$PWD":/app      -w /app      node:22-alpine sh -c "npm ci && npm run start"
    
  2. Modifiez server.js et rechargez la page.

    • Les changements sont-ils visibles sans rebuild de l’image ? OUI/NON

    • Expliquez ce qui se passe (bind mount, couche RW) :

      Réponse :
      
  3. (Optionnel) Utilisez nodemon pour un vrai hot reload :

    npm i -D nodemon
    

    Adaptez package.json ("start": "nodemon server.js"). Testez.


Partie D — Réseau : hôte ↔ conteneur

  1. Depuis le conteneur, testez l’hôte :

    • Windows/Mac : ping host.docker.internal → OK ? OUI/NON

    • Linux : utilisez l’IP de la passerelle Docker (ex. 172.17.0.1) → OK ? OUI/NON

  2. Depuis une autre machine du LAN, testez http://<IP-hôte>:3000 quand l’app est :

    • (a) locale → OK ?

    • (b) dans Docker → OK ?
      Si non, identifiez le blocage (pare-feu, NAT, publication de port).


Partie E — Variables d’environnement et configuration

  1. Lancez l’image avec une variable :

    docker run -d --name app2 -p 3001:3000 -e APP_ENV=production node-vs-docker:1.0
    
  2. Affichez process.env.APP_ENV côté serveur (log) et copiez la sortie :

    Sortie :
    
  3. Comparez avec set APP_ENV=dev && npm run start (Windows) ou APP_ENV=dev npm run start (Linux/Mac) en local.
    Expliquez la différence de passage d’ENV (shell vs run args) :

    Réponse :
    

Partie F — Nettoyage et reproductibilité

  1. Supprimez conteneurs/images :

    docker rm -f app1 app2
    docker rmi node-vs-docker:1.0
    
  2. Expliquez en quoi npm ci + package-lock.json améliorent la reproductibilité entre local et Docker :

    Réponse :
    

Questions de synthèse (à rédiger)

  1. Quand préférez-vous local plutôt que Docker en développement, et pourquoi ?

  2. Qu’apporte Docker en production par rapport à un Node installé “à la main” sur un serveur ?

  3. Donnez deux pièges fréquents observés dans cet exercice et comment les éviter.


(Bonus) Multi‑stage build + user non-root

# syntax=docker/dockerfile:1
FROM node:22-alpine AS deps
WORKDIR /app
COPY package*.json ./
RUN npm ci --only=production

FROM node:22-alpine AS runner
WORKDIR /app
COPY --from=deps /app/node_modules /app/node_modules
COPY . .
USER node
EXPOSE 3000
CMD ["npm","run","start"]

Questions :

  • Avantages de USER node ?

  • Pourquoi séparer l’étape deps ?
    Réponses :

- …