LIFWEB TP5 - Introduction à Node.js et déploiement

L’objectif de ce TP est de réaliser un serveur web Node.js minimaliste et de le déployer sur une machine virtuelle Linux. Ce TP met en œuvre les connaissances de LIFSE de L2 et fait écho à la partie Administration de LIFPCA (CM4, TD5 et TP6) qui a lieu à peu près en parallèle.

Machine cible

Vous disposez d’une VM Ubuntu Server 22.04 pré-installée avec Node.js 20.x, PostgreSQL 16 et Nginx 1.25 monté en reverse proxy. Les informations de connexion sont dans Tomuss :

  • La colonne DNS-VM donne le nom de votre VM au format lifweb-1${ID_ETU}.lifweb.os.univ-lyon1.fr.
  • La colonne SSH-KEY contient une clef privée SSH pour l’utilisateur p${ID_ETU}.
  • La colonne SSH-CI contient une clef privée SSH pour un utilisateur non-privilégié.

Par exemple, pour un⋅e étudiant⋅e dont le numéro ID_ETU=1234567, l’accès SSH pour l’utilisateur privilégié se fait comme suit :

# -o IdentitiesOnly=yes est utile si vous avez beaucoup de clefs dans votre ~/.ssh
ssh -o IdentitiesOnly=yes -i p1234567.pem p1234567@lifweb-11234567.lifweb.os.univ-lyon1.fr

⚠️ Attention ⚠️

Problèmes classiques que vous pourrez rencontrer.

  • Le compte utilisateur privilégié est la variante de votre numéro d’étudiant⋅e préfixé par p mais celui de la machine est préfixé par 1
  • Une clef SSH doit avoir des droits Unix 600 ou moins ;
  • Votre compte privilégié est sudoer sans mot de passe, ainsi :
    • toujours sauvegarder les fichiers systèmes avant de les modifier (cp file.conf file.conf.original) ;
    • avant de vous déconnecter de la session SSH, vérifier que vous arrivez toujours à ouvrir une autre session.
  • Les VMs ne sont pas routées sur Internet, il faut donc obligatoirement utiliser une IP UCBL :

Exercice 0 (PRÉPARATION) : accès à la VM

On prend en main l’environnement de travail. Ce sera aussi celui des TPs suivants.

  • Se connecter à votre VM et mettre à jour l’OS.
    • Redémarrer la VM si nécessaire.
  • Déterminer le nombre de CPUs et la quantité de RAM de la VM.
  • Relever les versions exactes et noter les commandes pour les obtenir :
    • Ubuntu
    • Node.js
    • Nginx
    • PostgreSQL
      • Par défaut, PostgreSQL n’autorise que les connexions locales par sockets Unix.
      • Se faire passer pour l’utilisateur postgres avec sudo, e.g., sudo -u postgres -i pour tester.
  • Déterminer le ping moyen vers le serveur CTF lifweb.univ-lyon1.fr depuis la VM.
    • Est-ce plus facile ou plus difficile de réaliser les défis depuis la VM ?
  • Quels sont les autres utilisateurs du système qui disposent d’un home directory ?
    • Parmi eux, lesquels sont sudoers ?

⚠️ Enregistrer ces informations dans un fichier au format Markdown si on devait les demander. ⚠️

Exercice 1 : Configurations Nginx et PostgreSQL

On étudie la configuration déjà fournie pour se l’approprier. Sauf précision contraire, on suppose qu’on exécute depuis la VM.

  • Faire une requête HTTP sur le port HTTP/80 de votre VM avec https://httpie.io/ (préféré) ou https://curl.se/.
    • Quel code HTTP obtenez-vous ?
  • Idem, mais cette fois-ci envoyer la requête sur le port HTTPS/443, quel code HTTP obtenez-vous ?
    • Sans option supplémentaire, vous aurez un échec, il faut autoriser les connections SSL non sûres.
    • Utiliser --verify no pour https://httpie.io/ et --insecure pour https://curl.se/
  • Maintenant avec un navigateur, depuis une autre machine, saisir le nom de la VM. Quel est le message d’erreur ? L’expliquer.
    • Accepter l’exception de sécurité, c’est une machine que vous contrôlez.
  • Consulter les fichiers contenus dans /etc/nginx/conf.d/ et identifier les lignes qui expliquent le comportement des requêtes HTTP précédentes.
  • Un utilisateur et une base de donnée ont déjà été créées dans PostgreSQL. Comme s’appelle l’utilisateur ?
  • Se connecter avec cet utilisateur avec le client psql -U utilisateur -h localhost utilisateur et créer une table de test avec quelques tuples avec le script ci-après.
    • Le mot de passe est le même que le nom de l’utilisateur et une base éponyme dont il est propriétaire est déjà créée.
    • Pour éviter de saisir le mot de passe à chaque fois dans psql, vous pouvez utiliser la variable d’environnement adaptée, export PGPASSWORD=le_mot_de_passe
create table test(id int generated always as identity, content text);
insert into test(content) (select md5(i::text) from generate_series(1, 16) as g(i));

⚠️ Assurez-vous que toutes les étapes précédentes sont bien réalisées, sinon vous serez bloqués sur la suite des TPs. ⚠️

Exercice 2 : serveur Node.js

On crée un premier serveur Node.js qui va se connecter à la base de donnée et vérifier ainsi que toute la chaîne est fonctionnelle. On utilise l’API native node:http et le driver postgres pg.

On utilisera un style basé sur les événements, comme l’API Node.js native, sans Promise ni async/await pour l’instant.

  • Télécharger les fichiers package.json et http-health.js dans un dossier ~/node/ sur votre VM.
    • Utiliser wget depuis la VM ou scp depuis votre station, à votre convenance.
    • Ce sera pour la suite le dossier de travail courant (le retour de la commande pwd).
  • Installer le projet avec npm install dans le dossier de travail.
  • Créer un fichier .env à partir de l’exemple ci-dessous, en remplaçant les valeurs de connexion à la BD.
  • Lancer le serveur avec la commande npm start.
    • Quelles sont les différences entre le script start et le script prod du fichier package.json ?

À partir de là, vous pouvez vérifier depuis une machine cliente autre que la VM (par exemple, celle depuis laquelle vous vous êtes connectés en SSH), via navigateur ou ligne de commande, que les routes suivantes sont accessibles, où ${VM} désigne le nom DNS de la VM serveur (de la forme lifweb-11234567.lifweb.os.univ-lyon1.fr) :

  • GET https://${VM}/
  • GET https://${VM}/todo
  • GET https://${VM}/echo
  • POST https://${VM}/echo avec un corps JSON de votre choix
  • GET https://${VM}/health
  • GET https://${VM}/nothing-here

Pour chacune des routes précédentes, noter :

  • Les réponses HTTP obtenues sur le client.
  • Les logs d’activité côté serveur.
  • La ligne de http-health.js qui explique le code HTTP de retour.

Ensuite, fermer la connexion SSH qui a lancé l’application sur la VM. Sauf à avoir suivi d’autres étapes que celles demandées, votre application ne devrait plus être en cours d’exécution. Pourquoi ? Le vérifier.

Exemple de fichier .env

PORT=3000
PGHOST=localhost
PGUSER=l_utilisateur_identifié
PGDATABASE=l_utilisateur_identifié
PGPASSWORD=l_utilisateur_identifié
PGPORT=5432

Exercice 3 : déploiement daemon

L’utilisateur nommé gitlabci est non privilégié dans votre VM. Il dispose d’un compte déjà configuré et de sa propre clef SSH (colonne Tomuss SSH-CI).

C’est avec ses droits que l’application Node.js doit être exécuté en tâche de fond (daemon).

  • S’authentifier en tant que gitlabci, tester les deux façons :
    • Directement avec sa clef SSH.
    • Depuis le compte privilégié, avec sudo -u gitlabci -i.
  • Installer votre application dans le dossier /home/gitlabci/node comme dans l’exercice précédent.
  • Vérifier qu’elle fonctionne.

Créer un service systemd pour que votre serveur soit exécuté en mode daemon, pour cela en root ou via sudo :

  • Copier le fichier node.service dans le dossier /etc/systemd/system/.
  • Relancer le gestionnaire avec systemctl daemon-reload et vérifier que le service existe bien avec systemctl status node.service.
    • Le service doit être inactif.
  • Lancer le service avec systemctl start node.service (vérifier avec systemctl status), activez-le par défaut pour qu’il soit relancé automatiquement après reboot avec systemctl enable node.service.
  • Vérifier que vous avez accès aux logs avec journalctl --unit node.service.
    • Pour un affichage live du journal, utiliser l’option --follow.
  • Depuis un autre utilisateur, faites une requête HTTP sur votre serveur.
  • Vérifier que cette requête a bien généré une trace dans le journal.
  • Déconnectez-vous et vérifier que le service est toujours disponible.
  • Reconnectez-vous et redémarrez la machine avec reboot.
  • Attendez un peu que la machine redémarre et vérifiez à nouveau la disponibilité du service.

À ce stade, vous êtes capable de déployer une application Node.js en mode résidant sur un serveur distant avec un compte non-privilégié.

Notez dans un coin de votre tête que vous avez ce service activé sur votre VM, vous aurez sans doute besoin de le désactiver pour un prochain TP (systemctl disable node.service) si vous réutilisez le port 3000…

Vérification automatique

⚠️ Un script vérifiera automatiquement l’état de votre machine le lundi 1er avril à 23:59. Cette évaluation participera au bonus/malus de l’UE. ⚠️

Exercice 3 (BONUS) : refactoring de l’application

  • Réorganiser le handler de l’événement "request" avec une fonction définie à l’extérieur de .on("request", handler).
  • Assurer que le format des réponses HTTP est bien "application/json".
  • Lever une exception qui arrêtera le programme si la connexion à PostgreSQL échoue.
  • Créer une nouvelle route /status/{ID} sur le modèle de httpbin.org : si {ID} est un code de réponse HTTP, alors on retournera une réponse avec ce code et une erreur 400 sinon. Par exemple GET https://${VM}/status/418 renverra une erreur 418.
    • Pour cela restructurer le switch avec const [first, ...paths] = request.url.slice(1).split("/");.

Exercice 4 (BONUS) : évaluation empirique des performances

Utiliser https://github.com/mcollina/autocannon pour évaluer la capacité de traitement de votre machine sur la route /health. Vous devriez obtenir quelques centaines de requêtes par seconde. Idem pour la route /. Comment expliquer la différence de capacité de traitement ?