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 formatlifweb-1${ID_ETU}.lifweb.os.univ-lyon1.fr
. - La colonne
SSH-KEY
contient une clef privée SSH pour l’utilisateurp${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é par1
- 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.
- toujours sauvegarder les fichiers systèmes avant de les modifier (
- Les VMs ne sont pas routées sur Internet, il faut donc obligatoirement utiliser une IP UCBL :
- soit en étant connecté sur le campus ;
- soit en utilisant le VPN (Linux/openconnect, Windows) ;
- soit via un tunnel SSH.
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
avecsudo
, 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
?
- Parmi eux, lesquels sont
⚠️ 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 ouscp
depuis votre station, à votre convenance. - Ce sera pour la suite le dossier de travail courant (le retour de la commande
pwd
).
- Utiliser
- 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 scriptprod
du fichierpackage.json
?
- Quelles sont les différences entre le script
À 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 choixGET 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 avecsystemctl status node.service
.- Le service doit être inactif.
- Lancer le service avec
systemctl start node.service
(vérifier avecsystemctl status
), activez-le par défaut pour qu’il soit relancé automatiquement après reboot avecsystemctl 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
.
- Pour un affichage live du journal, utiliser l’option
- 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 exempleGET https://${VM}/status/418
renverra une erreur 418.- Pour cela restructurer le
switch
avecconst [first, ...paths] = request.url.slice(1).split("/");
.
- Pour cela restructurer le
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 ?