Un dépôt de configuration central pour les microservices : intégration et synchronisation en un seul commandement
Créez un répertoire de configuration central avec des scripts d’installation, de synchronisation et de mise à jour quotidienne afin que chaque service NestJS et React partage les paramètres de linting, d’éditeur et de Docker.
Lorsqu’une équipe gère une douzaine de microservices Node.js, la partie la plus difficile de la première semaine d’un nouvel employé n’est souvent pas le code mais l’environnement : la bonne version de Node, des règles ESLint et Prettier compatibles, les valeurs correctes du .env ainsi qu’une configuration Docker fonctionnant comme celle des autres services. Chaque service a tendance à s’éloigner un peu de cette configuration, ce qui oblige les ingénieurs seniors à jouer le rôle d’un service d’assistance. Cet article montre comment résoudre ce problème grâce à un seul répertoire de configuration accompagné de quelques scripts Bash qui permettent de démarrer une machine en un seul commandement, de copier les paramètres partagés dans chaque service et de les maintenir à jour. Vous verrez également où ces scripts, tels qu’ils sont actuellement écrits, nécessitent des améliorations de sécurité avant de pouvoir être utilisés en toute confiance.
Comment l’écart de configuration se développe progressivement
Imaginez un développeur expérimenté qui rejoint une équipe gérant douze services. La première journée se passe à installer Node et Docker, à définir les règles de style ESLint propres à l’équipe, à deviner quelles variables doivent figurer dans tel ou tel fichier .env, et à demander en chat quelle version de NestJS sont utilisées par les services. Ensuite, le auth-service échoue en raison d’une erreur CORS causée par une configuration locale légèrement différente, et plus tard la première demande de pull request échoue au test d’intégration car la configuration Prettier partagée a été modifiée il y a un mois, sans que la copie locale ne soit mise à jour.
Aucun de ces échecs n’est dramatique, mais pris ensemble ils ont un coût important. Lorsque chaque service dispose de ses propres règles de lint légèrement différentes, de fichiers Dockerfiles, d’une structure de dossiers et de paramètres d’édition uniques, cela se traduit par des jours perdus pour les nouveaux arrivants et des heures perdues chaque semaine pour les ingénieurs seniors qui doivent répondre aux questions relatives à la configuration.
Un seul répertoire en tant que source de vérité
La solution consiste en un répertoire dédié, ici nommé dev-environment-config, qui contient tout ce dont a besoin une machine de développement : des modèles de projet, des règles de linting et de formatage, des paramètres VS Code, une configuration de base Docker, des scripts d’automatisation et une bibliothèque partagée. Lorsqu quelqu’un le met à jour, chaque machine de développeur intègre automatiquement la modification dès le lendemain matin.
La structure globale est la suivante, avec le répertoire central qui alimente chaque service :
dev-environment-config (Central Repo)
├── templates/ → Project scaffolding (NestJS, React)
├── shared/ → ESLint, Prettier, Git configs
├── vscode/ → Extensions & settings
├── docker/ → Base Docker configs
├── scripts/ → Automation (setup, sync, update)
├── common-lib/ → Shared NPM package
└── manifest.json → Version tracking
│ syncs to
▼
┌──────────┐ ┌──────────┐ ┌──────────┐
│ user-svc │ │ auth-svc │ │ pay-svc │ ... 12 services
│ .eslint │ │ .eslint │ │ .eslint │
│ .vscode │ │ .vscode │ │ .vscode │
│ Docker │ │ Docker │ │ Docker │
└──────────┘ └──────────┘ └──────────┘
Construction du système étape par étape
Structure du répertoire central
Dans le répertoire, chaque fonctionnalité dispose de son propre dossier. Les modèles contiennent des starters complets pour NestJS et React, shared/ abrite les fichiers dotfiles pour ESLint, Prettier, EditorConfig, les règles Git ignore et commitlint, vscode/ contient les paramètres de l’éditeur, et scripts/ regroupe les scripts d’automatisation.
dev-environment-config/
├── templates/
│ ├── nestjs-service/ # Complete NestJS starter
│ └── reactjs-service/ # Complete React starter
├── shared/
│ ├── .eslintrc.js
│ ├── .prettierrc
│ ├── .editorconfig
│ ├── .gitignore
│ └── commitlint.config.js
├── vscode/
│ ├── extensions.json
│ ├── settings.json
│ └── launch.json
├── scripts/
│ ├── setup-new-developer.sh
│ ├── sync-config.sh
│ ├── auto-update.sh
│ └── create-new-service.sh
├── common-lib/ # Shared NPM package
└── manifest.json # Version tracker
Le fichier manifest.json est ce qui rend possible le reste de l’automatisation. Il enregistre la version globale, la date et l’auteur de la dernière modification, les versions pour chaque zone de configuration ainsi que la liste des services appartenant à l’espace de travail.
{
"version": "1.2.0",
"lastUpdated": "2024-01-15T10:00:00Z",
"updatedBy": "senior-dev",
"configs": {
"eslint": "3.2.0",
"prettier": "2.1.0",
"docker": "1.5.0"
},
"services": [
"user-service",
"auth-service",
"payment-service"
]
}
La règle est simple : toute modification de la configuration partagée entraîne une augmentation de la version de niveau supérieur. Le script de mise à jour compare ensuite les versions pour déterminer s’il y a des éléments à synchroniser ; oublier d’augmenter la version signifie que la modification ne se propage pas silencieusement. Une vérification CI qui échoue lorsque des fichiers situés dans shared/ sont modifiés sans augmentation de version constitue une mesure de sécurité peu coûteuse.
Intégration en une seule commande
Le plus grand avantage réside dans le script de démarrage. Un nouveau développeur installe VS Code puis exécute une seule commande :
curl -sSL https://raw.githubusercontent.com/company/dev-environment-config/main/scripts/setup-new-developer.sh | bash
Environ dix minutes plus tard, l’espace de travail est prêt. Il est pratique d’exécuter directement un script distant depuis bash, mais cela exige prudence : le script s’exécute avec les permissions de l’utilisateur et ce qui se trouve sur la branche à ce moment-là. Cela est généralement acceptable pour un outil interne, mais de nombreuses équipes préfèrent que les personnes clonent le répertoire et exécutent le script localement afin de pouvoir le lire d’abord.
Au niveau technique, le script fonctionne selon une séquence fixe :
#!/bin/bash
set -e
WORKSPACE_DIR="$HOME/company-workspace"
CONFIG_REPO="https://github.com/company/dev-environment-config.git"
echo "🚀 Setting up your development environment..."
# 1. Check & install prerequisites
check_and_install "git" "Git"
check_and_install "node" "Node.js" "nvm install 20"
check_and_install "docker" "Docker"
# 2. Create workspace & clone everything
mkdir -p "$WORKSPACE_DIR" && cd "$WORKSPACE_DIR"
git clone "$CONFIG_REPO"
# 3. Clone all service repos from manifest
SERVICES=$(cat dev-environment-config/manifest.json | \
python3 -c "import sys,json; \
[print(s) for s in json.load(sys.stdin)['services']]")
for service in $SERVICES; do
git clone "https://github.com/company/${service}.git"
done
# 4. Install VS Code extensions
EXTENSIONS=$(cat dev-environment-config/vscode/extensions.json | \
python3 -c "import sys,json; \
[print(e) for e in json.load(sys.stdin)['recommendations']]")
for ext in $EXTENSIONS; do
code --install-extension "$ext" --force
done
# 5. Sync shared configs to all services
bash dev-environment-config/scripts/sync-config.sh
# 6. Install dependencies everywhere
for service in $SERVICES; do
cd "$service" && npm install && cp .env.example .env && cd ..
done
# 7. Set up daily auto-update cron (8 AM weekdays)
CRON="0 8 * * 1-5 cd $WORKSPACE_DIR && \
bash dev-environment-config/scripts/auto-update.sh"
(crontab -l 2>/dev/null; echo "$CRON") | crontab -
echo "✅ Setup complete! Open VS Code: code $WORKSPACE_DIR"
Quelques détails méritent d’être notés. set -e interrompt l’exécution dès la première commande qui échoue, ce qui empêche un espace de travail partiellement configuré d’avoir l’air complet. L’aide-mémoire check_and_install n’est pas montré et doit être écrit spécifiquement pour chaque système d’exploitation. La parsing de JSON à l’aide d’une ligne de commande Python intégrée fonctionne, mais le script de mise à jour utilise ensuite jq ; donc standardiser sur un seul outil réduit le nombre de prérequis. La copie de .env.example fournit des valeurs par défaut sûres, mais cela ne fonctionne que si les fichiers d’exemple restent complets et ne contiennent aucune information secrète réelle.
Synchronisation de la configuration partagée dans chaque service
Le script de synchronisation parcourt les services un par un et copie les fichiers dotfiles partagés ainsi que les paramètres de VS Code dans chacun d’eux, écrasant ce qui s’y trouve déjà.
#!/bin/bash
# sync-config.sh
CONFIG_ROOT="$(dirname "$SCRIPT_DIR")"
WORKSPACE_DIR="$(dirname "$CONFIG_ROOT")"
for service in $SERVICES; do
SERVICE_DIR="$WORKSPACE_DIR/$service"
# Copy shared configs
cp -f "$CONFIG_ROOT/shared/.eslintrc.js" "$SERVICE_DIR/"
cp -f "$CONFIG_ROOT/shared/.prettierrc" "$SERVICE_DIR/"
cp -f "$CONFIG_ROOT/shared/.editorconfig" "$SERVICE_DIR/"
cp -f "$CONFIG_ROOT/shared/.gitignore" "$SERVICE_DIR/"
# Copy VS Code settings
mkdir -p "$SERVICE_DIR/.vscode"
cp -f "$CONFIG_ROOT/vscode/settings.json" "$SERVICE_DIR/.vscode/"
cp -f "$CONFIG_ROOT/vscode/extensions.json" "$SERVICE_DIR/.vscode/"
echo "✅ $service synced"
done
Ce fragment suppose que SCRIPT_DIR et SERVICES sont déjà définis, ce qui signifie que le script réel doit les déterminer lui-même (par exemple à partir de $0 et du manifeste). Les appels à cp -f sont intentionnels : l’écrasement des fichiers permet d’éliminer les écarts. Le compromis est que toute modification locale de ces fichiers sera perdue lors du prochain exécution, c’est pourquoi la personnalisation par service doit se faire dans des fichiers séparés, comme expliqué ci-dessous. Envisagez également si un fichier tel que .gitignore devrait vraiment être identique partout, étant donné que les services ont souvent besoin de leurs propres entrées.
Tâche d’auto-mise à jour quotidienne
C’est cette partie qui maintient les machines en synchronisation sans que personne n’ait à se souvenir de faire quoi que ce soit. Chaque jour ouvré à 8 heures du matin, la tâche cron exécute auto-update.sh:
#!/bin/bash
# auto-update.sh — Runs daily at 8 AM
cd "$CONFIG_ROOT"
# 1. Check for config updates
OLD_VERSION=$(cat manifest.json | jq -r '.version')
git pull origin main
NEW_VERSION=$(cat manifest.json | jq -r '.version')
if [ "$OLD_VERSION" != "$NEW_VERSION" ]; then
echo "🆕 Config updated: $OLD_VERSION → $NEW_VERSION"
bash "$SCRIPT_DIR/sync-config.sh"
fi
# 2. Pull latest code for all services
for service in $SERVICES; do
cd "$WORKSPACE_DIR/$service"
# Stash local changes safely
git stash push -m "auto-update-$(date +%Y%m%d)" 2>/dev/null
# Pull latest
git pull origin $(git branch --show-current) --rebase
# Update dependencies if package.json changed
if git diff HEAD~1 --name-only | grep -q "package.json"; then
npm install
fi
# Update shared library
npm update @company/common-lib
done
# 3. Notify the developer
notify-send "Dev Environment" "✅ Updated to config v$NEW_VERSION"
Considérez ce script comme un point de départ et non comme quelque chose à déployer tel quel :
- Les modifications sont mises de côté mais ne sont jamais récupérées, ce qui oblige un développeur ayant quitté son poste sans valider ses changements à savoir exécuter
git stash pop; sinon, ces modifications semblent disparaître. git diff HEAD~1ne vérifie que le dernier commit. Si la récupération a intégré plusieurs commits, une modification apportée àpackage.jsondans un commit antérieur peut être manquée ; comparer avec le commit existant avant la récupération est plus fiable.- Un rebase rencontrant des conflits laisse le répertoire en cours de rebase sans que personne ne le surveille. Utiliser
git pull --ff-onlyet ignorer le service en cas d’échec est plus sûr pour un travail non supervisé.
notify-send n’existe que sur les ordinateurs de bureau Linux, et cron ne s’exécute pas sur une machine en veille à 8 heures du matin, donc les utilisateurs de macOS peuvent avoir besoin de launchd ou d’un déclencheur différent.Le flux de bout en bout relie la action d’un ingénieur senior à chaque développeur le lendemain matin :
Senior Dev pushes config change
│
▼
GitHub Actions
│
┌────┴────┐
│ Slack │ Notify team
│ Publish │ common-lib to NPM
└────┬────┘
│
Next 8 AM
│
▼
Every developer's cron runs
│
┌────┴────────────────┐
│ Pull config repo │
│ Sync shared configs │
│ Pull service repos │
│ Update dependencies │
│ Desktop notification│
└─────────────────────┘
Une bibliothèque partagée plutôt que du copier-coller
Les gardiens, les intercepteurs, les filtres d’exception et les DTO ont tendance à être dupliqués entre les services avant de diverger. En les regroupant dans un module npm privé, @company/common-lib, chaque service dispose d’une seule importation :
// In any service, just import and use:
import {
AuthGuard,
RolesGuard,
ResponseInterceptor,
LoggingInterceptor,
HttpExceptionFilter,
PaginationDto,
McpService, // Qwen MCP integration
} from '@company/common-lib';
@Controller('users')
@UseGuards(AuthGuard, RolesGuard)
@UseInterceptors(ResponseInterceptor)
@UseFilters(HttpExceptionFilter)
export class UserController {
constructor(private mcpService: McpService) {}
@Get()
findAll(@Query() pagination: PaginationDto) {
return this.userService.findAll(pagination);
}
@Post('analyze')
async analyze(@Body() dto: AnalyzeDto) {
return this.mcpService.callTool('text-analysis', {
text: dto.text,
});
}
}
McpService pour une intégration Qwen MCP. Lorsque la bibliothèque intègre un nouveau mécanisme de protection ou une correction pour le filtre d’exception, la tâche d’auto-mise à jour l’applique à tous les services.
Il convient de noter que npm update respecte la plage semver indiquée dans chaque package.json ; par conséquent, une version majeure ne sera pas automatiquement prise en compte, ce qui est généralement souhaitable. Les bibliothèques partagées créent également un couplage entre les services ; l’article sur les cas où du code partagé doit exister explique comment déterminer ce qui appartient à l’une ou l’autre catégorie.
Création de nouveaux services
Créer un nouveau service signifiait autrefois passer une journée à copier celui existant et à supprimer ce qui n’était pas applicable. Un script générateur transforme cela en une courte commande interactive :
bash scripts/create-new-service.sh
# Prompts:
# Service name: order-service
# Description: Handles order processing
# Port: 3002
# Type: nestjs
# ✅ Done! Complete service with:
# - src/common/ (filters, guards, interceptors, DTOs)
# - Docker setup (Dockerfile + docker-compose)
# - VS Code settings
# - ESLint + Prettier
# - Health check endpoint
# - Swagger docs
# - MCP integration
# - Auto-update scripts
Comme chaque nouveau service part du même modèle, il commence sans dérive.
Avantages pour l’équipe
Une équipe ayant adopté cette configuration a constaté les effets suivants :
- Intégration en quelques minutes : une seule commande a remplacé une page wiki de quarante étapes, réduisant le temps nécessaire pour lancer chaque service de environ trois jours à une quinzaine de minutes, et les questions relatives à la configuration en chat ont presque disparu.
- Aucune dérive : les services risquaient autrefois de diverger silencieusement (ESLint 8 dans l’un, 7 dans un autre, fichiers Docker obsolètes) ; les écrans de sauvegarde planifiés les maintiennent identiques.
src/common/, configuration Docker et chemin de vérification de l’état dans chaque service.Leçons pour sa mise en œuvre progressive
Commencer par la plus petite partie utile
Il n’est pas nécessaire de mettre en place tout cela d’un coup. Synchroniser uniquement les paramètres d’ESLint et de Prettier apporte rapidement des avantages visibles. La configuration Docker, les paramètres de l’éditeur, la bibliothèque partagée et les mises à jour planifiées peuvent être ajoutées une fois que l’équipe fait confiance à cette approche.
Laisser de la place pour des modifications locales
Une base commune ne doit pas être contraignante. Un service ayant besoin d’une règle supplémentaire peut ajouter un fichier .eslintrc.local.js qui étend la configuration partagée ; comme le script de synchronisation ne touche jamais ce fichier, la modification locale reste en vigueur à chaque exécution.
Rendre les mises à jour visibles
La notification sur l’ordinateur semble être un détail mineur, mais elle informe les développeurs que le système existe et qu’il y a eu un changement. Les mises à jour silencieuses ont tendance à troubler les gens lorsque leurs outils se comportent différemment du jour au lendemain.
Versionner tout
Sans version manifeste, la tâche de mise à jour ne peut pas déterminer s’il y a eu des changements et effectuerait une synchronisation à chaque exécution. La versionning vous fournit également un référent concret lorsque des modifications causent des problèmes.
La technologie sous-jacente à l’exemple
L’environnement d’exemple combine NestJS et React (tous deux en TypeScript), Qwen accessible via MCP, Docker et Docker Compose, Node.js 20 LTS, PostgreSQL 16, Redis 7, GitHub Actions, VS Code, ainsi que Bash et cron pour la synchronisation. Ces versions correspondent à l’environnement de l’époque ; vérifiez les dernières versions LTS avant de les reproduire.
Points clés
- Lorsque vous disposez de plus d’une poignée de services et de développeurs, un répertoire de configuration centralisé s’avère rapidement rentable.