Skip to content

Latest commit

 

History

History
112 lines (79 loc) · 6.86 KB

J2.md

File metadata and controls

112 lines (79 loc) · 6.86 KB

Jour 2 : conteneurs, Compose et reverse proxy Nginx

Objectif de la journée : transformer l'application en un artefact unique (une image Docker) que le pipeline pourra construire à chaque commit, puis la faire tourner avec sa base de données derrière un reverse proxy Nginx, en une seule commande.

1. Pourquoi une image

Aujourd'hui, pour lancer l'API il faut « le bon Node.js, les bonnes dépendances, la bonne commande ». Une image Docker emballe tout cela une fois pour toutes. Demain, le pipeline construira cette image à chaque commit ; après-demain, c'est elle qui sera déployée. Tout ce que vous écrivez aujourd'hui sera réutilisé.

2. Écrire le Dockerfile multi-stage

Créez un Dockerfile à la racine qui respecte ces exigences :

Exigence Pourquoi
Une première étape nommée deps qui copie package.json et package-lock.json puis lance npm ci --omit=dev installer uniquement les dépendances de production, et profiter du cache tant que ces deux fichiers ne changent pas
Une étape finale basée sur node:24-alpine image de base légère
L'étape finale copie node_modules depuis deps, puis package.json et le dossier src ni les tests, ni les outils de développement en production
USER node ne pas exécuter l'application en root
EXPOSE 3000 documenter le port d'écoute
Un HEALTHCHECK qui appelle /health Docker sait si l'application répond, pas seulement si le processus tourne
CMD ["node", "src/server.js"] la commande de démarrage

Créez aussi un .dockerignore qui exclut au minimum node_modules, coverage, .git, .github, tests, docs, ainsi que .env et .env.* : sans cette exclusion, un fichier de secrets présent dans le dossier serait copié dans l'image.

3. Construire et mesurer

docker build -t devops-tasktracker:local .
docker image ls devops-tasktracker

Résultat attendu : l'image se construit. Dans la sortie de docker image ls, elle ne pèse pas plus de 20 Mo de plus que node:24-alpine (lancez docker image ls node:24-alpine pour comparer) : votre application et ses dépendances de production ne doivent presque rien ajouter à l'image de base. Modifiez une ligne de src/app.js et reconstruisez : les étapes npm ci doivent apparaître comme CACHED. Si ce n'est pas le cas, revoyez l'ordre des instructions.

4. Décrire la stack avec Compose

Créez compose.yml avec trois services :

Service Image Rôle Port publié sur l'hôte
nginx nginx:1.28-alpine point d'entrée unique, reverse proxy 80:80
app construite depuis . l'API aucun
db postgres:16-alpine stockage des tâches aucun

Contraintes :

  • db reçoit les variables POSTGRES_USER=tasks, POSTGRES_PASSWORD=tasks et POSTGRES_DB=tasks : sans POSTGRES_PASSWORD, l'image officielle PostgreSQL refuse de démarrer ;
  • app reçoit DATABASE_URL=postgres://tasks:tasks@db:5432/tasks (dans un réseau Compose, un service se joint par son nom, ici db) ;
  • db a un volume nommé pour /var/lib/postgresql/data et un healthcheck basé sur pg_isready -U tasks -d tasks ;
  • app attend que db soit en bonne santé, et nginx attend app (depends_on avec condition: service_healthy).

5. Configurer Nginx en reverse proxy

Créez nginx/default.conf : un bloc server qui écoute sur le port 80 et relaie toutes les requêtes vers app:3000 avec proxy_pass, en transmettant les en-têtes Host, X-Real-IP, X-Forwarded-For et X-Forwarded-Proto. C'est ce que vous avez fait ce matin dans le lab configurer-reverse-proxy-nginx. Pour X-Forwarded-For, utilisez la variable $proxy_add_x_forwarded_for (elle ajoute l'adresse du client à l'en-tête existant) et non $http_x_forwarded_for (vide si le client n'en envoie pas). Montez ce fichier en lecture seule dans le conteneur Nginx, à la place de /etc/nginx/conf.d/default.conf.

6. Vérifier

Dans l'Atelier, la machine est neuve chaque après-midi : après atelier-init, relancez docker compose up -d --build --wait avant de vérifier ou d'évaluer.

docker compose up -d --build --wait
docker compose ps
curl -i localhost/health
curl -X POST localhost/tasks -H 'content-type: application/json' -d '{"title":"survivre au redemarrage"}'
docker compose restart app
docker compose up -d --wait
curl localhost/tasks
docker compose logs nginx | tail -5

--wait attend que les services soient en bonne santé (healthcheck) avant de rendre la main : sans lui, un curl lancé trop tôt répond « connection refused » alors que tout va bien.

Si un téléchargement d'image échoue avec toomanyrequests, c'est la limite de téléchargements anonymes de Docker Hub : attendez quelques minutes et relancez, ou connectez-vous avec un compte Docker Hub gratuit (docker login).

Résultat attendu : curl -i montre Server: nginx ; la tâche est toujours là après le redémarrage de app (elle est dans PostgreSQL) ; seul nginx publie un port dans docker compose ps ; vos requêtes apparaissent dans les journaux Nginx.

7. Sobriété : mesurer le poids des images

Complétez ce tableau avec docker image ls :

Image Taille
node:24
node:24-alpine
devops-tasktracker:local

Chaque déploiement transfère l'image sur un serveur. Multipliez l'écart de taille par le nombre de déploiements d'une équipe qui livre dix fois par jour : c'est de la bande passante, du stockage et de l'énergie en moins.

8. Fin de journée

  • Dockerfile multi-stage et .dockerignore commités
  • Image à moins de 20 Mo au-dessus de node:24-alpine
  • compose.yml avec nginx, app, db, seul nginx publie un port
  • Les données survivent à docker compose restart app
  • Le CI du jour 1 est toujours au vert après votre git push
  • git status propre (rien à committer) et git push fait, puis Évaluer et Terminer l'Atelier : ce qui n'est pas poussé est perdu

9. Rattrapage

Si vous n'avez pas terminé, récupérez la correction au début de l'Atelier du jour 3, après atelier-init, dans ~/devops-tasktracker. La branche start-jN n'existe dans le dépôt du cours qu'à partir du matin du jour N. Commencez par enregistrer votre travail, puis fusionnez-la en gardant sa version en cas de conflit, et créez un commit à vous :

git add -A
git commit -m "wip: avant rattrapage"      # ignorez le message si rien n'est à committer
git fetch upstream
git merge -X theirs --no-edit upstream/start-j3
git commit --allow-empty -m "rattrapage J3"
npm ci
npm test
git push origin main

-X theirs garde vos modifications qui n'entrent pas en conflit, et prend la version du cours là où vous aviez écrit la même chose différemment. Le commit vide rattrapage J3 garantit que votre fork contient au moins un commit à vous, même quand la fusion n'en crée aucun.