Écrire un devcontainer.json depuis zéro
Ce que vous allez faire
Jusqu'ici, vous avez ouvert et lu des devcontainer.json déjà écrits par quelqu'un d'autre. Ce module inverse la situation : vous partez d'un dossier vide et vous écrivez, champ par champ, un devcontainer.json complet pour un vrai petit projet — une API Node.js minimale. À la fin, vous rebuildez le conteneur et vous prouvez, depuis le navigateur de votre machine hôte, que l'application tourne bien dedans.
Le projet reste volontairement simple sur toute la suite de ce module et le suivant : une API Express en Node.js. Gardez ce même projet ouvert, vous y ajouterez un outil supplémentaire au module 2.
Étape 1 — le projet, avant le conteneur
Créez le dossier et initialisez un projet Node minimal :
mkdir mon-api-node
cd mon-api-node
npm init -y
npm install express
Créez ensuite index.js à la racine :
const express = require("express");
const app = express();
const port = 3000;
app.get("/", (req, res) => {
res.send("Hello depuis l'intérieur du conteneur !");
});
app.listen(port, () => {
console.log(`En écoute sur le port ${port}`);
});
Et un .gitignore avec au moins :
node_modules/
C'est important pour la suite : si node_modules était versionné, un rebuild ne prouverait jamais que postCreateCommand a réellement réinstallé quoi que ce soit — vous testeriez juste la présence d'un dossier déjà là. En l'excluant, chaque rebuild repart d'un projet sans node_modules, et seul postCreateCommand peut le faire réapparaître.
Étape 2 — le fichier devcontainer.json, champ par champ
Créez .devcontainer/devcontainer.json :
{
"name": "Mon API Node",
"image": "mcr.microsoft.com/devcontainers/javascript-node:20",
"postCreateCommand": "npm install",
"forwardPorts": [3000],
"customizations": {
"vscode": {
"extensions": ["esbenp.prettier-vscode"]
}
}
}
image
On choisit une image officielle déjà construite plutôt qu'un build avec un Dockerfile local : mcr.microsoft.com/devcontainers/javascript-node:20 fournit Node.js 20 préinstallé sur une base Debian, ce qui suffit largement pour une API Express. build (avec un dockerfile et un context) est l'alternative — utile quand une image publique ne couvre pas vos besoins précis (une combinaison d'outils système spécifique, par exemple), mais c'est plus de maintenance pour un gain nul ici.
postCreateCommand
"npm install" s'exécute automatiquement une seule fois, juste après la création du conteneur — pas à chaque démarrage. C'est ce qui rend le projet reproductible : n'importe qui clone ce dépôt, ouvre le Dev Container, et obtient node_modules installé sans taper une seule commande ni lire un README qui dit « n'oubliez pas de faire npm install ».
forwardPorts
[3000] rend le port 3000 du conteneur joignable depuis localhost:3000 sur la machine hôte. Sans ce champ, le serveur Express tournerait très bien à l'intérieur du conteneur, mais resterait invisible de l'extérieur — vous ne pourriez jamais l'ouvrir dans votre navigateur.
customizations.vscode.extensions
Un tableau d'identifiants d'extensions installées automatiquement dans l'instance VS Code connectée au conteneur. esbenp.prettier-vscode (Prettier) est pertinente pour un projet JavaScript : chaque personne qui ouvre ce Dev Container a le même outillage éditeur, pas seulement le même runtime Node.
Étape 3 — rebuild et vérification
Ouvrez le dossier dans VS Code (code .), puis depuis la palette de commandes (Ctrl+Shift+P / Cmd+Shift+P) : Dev Containers: Reopen in Container.
Une fois la fenêtre relancée à l'intérieur du conteneur, ouvrez un terminal intégré et lancez le serveur :
node index.js
Sortie attendue :
En écoute sur le port 3000
Étape 4 — la preuve
Deux vérifications, l'une suffit mais faites les deux pour être sûr·e :
- Dans VS Code, ouvrez l'onglet Ports (à côté du terminal). Le port 3000 doit apparaître comme transféré. Cliquez sur l'icône de globe à côté : votre navigateur s'ouvre sur
http://localhost:3000et affiche « Hello depuis l'intérieur du conteneur ! ». - Depuis un terminal de votre machine hôte (pas celui du conteneur), lancez
curl http://localhost:3000: vous devez obtenir le même texte, alors que le processus Node tourne exclusivement à l'intérieur du conteneur.
Si les deux fonctionnent, forwardPorts fait bien son travail : un processus isolé dans un conteneur Linux est joignable depuis votre machine hôte, quel que soit son système d'exploitation.
Ce que vous venez de faire
Vous avez écrit un devcontainer.json complet à partir de rien : une image de base choisie en connaissance de cause, une commande de post-création qui rend le projet reproductible pour n'importe qui d'autre, un port ouvert vers l'hôte, et une extension partagée par toute l'équipe. Au module suivant, vous gardez ce même projet et vous lui ajoutez un outil supplémentaire — pas en modifiant l'image de base, mais avec une Dev Container Feature.
Vérifiez votre compréhension
Pourquoi placer `npm install` dans postCreateCommand plutôt que de simplement l'exécuter une fois à la main après avoir ouvert le conteneur ?
Dans devcontainer.json, quel champ rend un serveur qui écoute sur le port 3000 à l'intérieur du conteneur accessible depuis le navigateur de la machine hôte ?
Quelle est la syntaxe correcte pour installer automatiquement une extension VS Code dans le conteneur ?
Vous choisissez `image` plutôt que `build` dans ce module. Quelle affirmation est correcte ?
Envie d'être prévenu des prochains modules ?
L'Académie reste gratuite et en accès libre, sans inscription. Si vous voulez juste être averti par email à la sortie d'un nouveau module, c'est ici — aucune obligation, désinscription en un clic.