# 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](material/server.js) • [📥 package.json](material/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 : ```bash 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://: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) : ```dockerfile # 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 : ```bash docker build -t node-vs-docker:1.0 . ``` 2. Démarrez le conteneur (nom: `app1`) et publiez le port : ```bash 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:3000` → `OUI/NON` - Version de Node **dans** le conteneur : ```bash 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 : ```bash 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 : ```bash 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://: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 : ```bash 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 : ```bash 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 ```dockerfile # 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 : ``` - … ```