Projet de Fin d'Études : Partie 1 - Dockerizer une API Node.JS pour le développment et la production

Voici le premier article sur mon travail dans le cadre de mon projet de fin d’études d’ingénieur.

Nous sommes une équipe de 7 étudiants, travaillant sur le projet Ecobol. Comme le projet n’est pas encore public, je m’en tiendrai à mon travail sur la mise en œuvre des technologies et des infrastructures.

Aperçu de l’infrastructure

Structure et technologies utilisées.

Structure et technologies utilisées.

Dockerizer une API REST Node.js

Objectifs : Vitesse et harmonisation des environnements

Le projet ne dure que 2 mois, nous ne voulons pas perdre de temps à installer l’environnement de développement sur les machines de chaque développeur. Le développement dans des conteneurs docker permet également d’éviter les problèmes liés aux systèmes d’exploitation, tous les développeurs travaillant sur le même environnement que le serveur de production.

Gérer les différents environnements

Nous avons deux environnements : development et production, qui sont stockés dans la variable d’environnement NODE_ENV. Le choix de l’environnement a un impact sur les dépendances chargées, et sur le comportement de l’API (par exemple le niveau de logs, l’utilisation de pm2).

Le conteneur doit être capable de fonctionner correctement avec chaque environnement. Il y a 3 éléments majeurs à prendre en compte lors du démarrage de l’API :

  1. Ne charger que les dépendances nécessaires
  2. Passer la bonne variable d’environnement NODE_ENV
  3. Utiliser la bonne commande pour démarrer l’API (nodemon en développement, pm2 en production)

La solution implique un Dockerfile conditionnel, et plusieurs docker-compose.yml capables d’utiliser ce Dockerfile.

Dockerfile conditionnel

Voici le Dockerfile conditionnel :

FROM node:10.16.3
# Création d'un répertoire pour l'application
WORKDIR /usr/src/api-ecobol

# Installation des dépendances de l'application
# Un astérisque est utilisé pour s'assurer que package.json ET package-lock.json sont tous deux copiés
COPY package*.json ./

# Construction du code pour la production uniquement si NODE_ENV est défini à "production".
RUN if [ "$NODE_ENV" = "development" ]; \
	then npm install;  \
	else npm ci --only=production; \
	fi

# Intégration du code source
COPY . .

EXPOSE 3000

Le point essentiel ici est la ligne RUN : si le NODE_ENV n’est pas développement, il n’installera que des dépendances de production npm ci --only=production.

Notez également que ce conteneur ne démarre aucun processus à la fin, ce sera le travail du docker-compose.

Docker-Compose

Une fois que le Dockerfile est prêt, Docker-Compose est utilisé pour le builder et fournir une instance de MongoDB.

docker-compose.yml

version: '3'
services:
  api-ecobol:
    env_file:
      - .env
    restart: on-failure
    build: .
    ports:
      - '127.0.0.1:3002:3000'
    links:
      - mongo
  mongo:
    image: 'mongo:4'
    volumes:
      - './data:/data/db'
    ports:
      - '127.0.0.1:27017:27017'
    restart: on-failure

Cela transmet le fichier .env à notre application correctement, mais ne démarre pas encore l’application.

Pour démarrer l’application pour l’environnement de development, un autre fichier docker-compose est créé pour gérer certains paramètres spécifiques au développement :

docker-compose.dev.yml

version: '3'

services:
  api-ecobol:
    environment:
      - NODE_ENV=development
    volumes:
      - .:/usr/src/api-ecobol/
    command: ./node_modules/.bin/nodemon server.js

Cela :

  • Force NODE_ENV=development
  • Monte le code source sous forme de volume pour que nodemon puisse le surveiller
  • Démarre le processus nodemon

De la même manière, l’environnement de production dispose de son propre fichier docker-compose :

docker-compose.prod.yml

version: '3'

services:
  api-ecobol:
    command: ./node_modules/.bin/pm2-runtime server.js
    environment:
      - NODE_ENV=production

Cela :

  • Force NODE_ENV=production
  • Démarre le processus pm2

Utiliser ces fichiers docker-compose

Pour lancer l’API de development, il suffit simplement d’exécuter :

docker-compose -f docker-compose.yml -f docker-compose.dev.yml up -d

Et pour lancer l’API de production :

docker-compose -f docker-compose.yml -f docker-compose.prod.yml up -d

That’s it ! 🚀