Ein zentrales Konfigurationsrepositorium für Microservices: Ein-Kommando-Onboarding und Synchronisierung
Erstellen Sie ein zentrales Konfigurationsrepository mit Setup-, Synchronisierungs- und täglichen Aktualisierungsskripten, damit alle NestJS- und React-Dienste dieselben Linting-, Editor- sowie Docker-Einstellungen teilen.
Wenn ein Team Dutzende von Node.js-Mikroservices betreibt, besteht der schwierigste Teil der ersten Woche für einen Neuzugang oft nicht im Code, sondern in der Umgebung: die richtige Node-Version, passende ESLint- und Prettier-Regeln, die korrekten .env-Werte sowie eine Docker-Einrichtung, die genauso funktioniert wie bei allen anderen. Jeder Service neigt dazu, sich etwas zu verändern, wodurch Senior-Entwickler letztendlich als Helpdesk fungieren müssen. Dieser Artikel zeigt, wie man das mit einem einzigen Konfigurationsrepository sowie einigen Bash-Skripten beheben kann, die eine Maschine mit einem Befehl initialisieren, gemeinsame Einstellungen in jeden Service kopieren und diese auf dem neuesten Stand halten. Sie werden außerdem erkennen, wo die derzeitigen Skripte vor dem Einsatz weiter verbessert werden müssen.
Wie sich Konfigurationsabweichungen einschleichen
Stellen Sie sich einen erfahrenen Entwickler vor, der zu einem Team stößt, das zwölf Dienste betreut. Der erste Tag vergeht mit dem Installieren von Node und Docker, der Einstellung des ESLint-Stils im Team, der Vermutung, welche Variablen in welches .env-Datei gehören sollen, sowie mit Fragen im Chat nach der von den Diensten verwendeten NestJS-Version. Danach versagt der auth-service aufgrund eines CORS-Fehlers, verursacht durch eine leicht abweichende lokale Einstellung, und später scheitert der erste Pull Request in der CI-Prüfung, weil sich die gemeinsame Prettier-Konfiguration vor einem Monat geändert hat, während die lokale Kopie dies nicht getan hat.
Niemand dieser Fehler ist besonders dramatisch, doch zusammen sind sie kostspielig. Wenn jeder Dienst seine eigenen, leicht unterschiedlichen Lint-Regeln, Dockerfiles, Ordnerstrukturen und Editor-Einstellungen hat, zeigt sich der Aufwand in verlorenen Tagen für Neueinsteiger sowie in wöchentlich verlorenen Stunden für die erfahrenen Entwickler, die Setup-Fragen beantworten müssen.
Ein Repository als einzige Quelle der Wahrheit
Die Lösung ist ein spezielles Repository, hier dev-environment-config genannt, das alles enthält, was ein Entwicklungsrechner benötigt: Projektvorlagen, Lint- und Formatierungsregeln, VS Code-Einstellungen, eine Basis-Docker-Konfiguration, Automatisierungsscripts sowie eine gemeinsame Bibliothek. Wenn jemand es aktualisiert, übernimmt jeder Entwicklungsrechner die Änderung automatisch am nächsten Arbeitsmorgen.
Die Gesamtstruktur sieht so aus, wobei das zentrale Repository jede Dienstkomponente versorgt:
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 │
└──────────┘ └──────────┘ └──────────┘
Schritt für Schritt das System aufbauen
Struktur des zentralen Repositories
Innen im Repository erhält jede Aufgabe ihren eigenen Ordner. Die Vorlagen enthalten vollständige NestJS- und React-Startpakete, shared/ enthält Dotfiles für ESLint, Prettier, EditorConfig, Git-Ignore-Regeln und commitlint, vscode/ enthält Editor-Einstellungen und scripts/ enthält die Automatisierungs scripts.
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
Die Datei manifest.json macht den Rest der Automatisierung möglich. Sie enthält die Gesamtversion, an welchem Datum und von wem sie zuletzt geändert wurde, Versionen für jeden Konfigurationsbereich sowie die Liste der Dienste, die zum Arbeitsbereich gehören.
{
"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"
]
}
Die Regel ist einfach: Jede Änderung an der gemeinsamen Konfiguration erhöht die oberste Version. Das Update-Skript vergleicht anschließend die Versionen, um festzustellen, ob etwas synchronisiert werden muss; wenn man vergisst, die Version zu erhöhen, wird die Änderung stillschweigend nicht weitergegeben. Eine CI-Prüfung, die fehlschlägt, wenn sich Dateien unter shared/ ändern, ohne dass die Version erhöht wird, dient als einfache Sicherheitsmaßnahme.
Einfache Einrichtung mit einem Befehl
Der größte Vorteil ist das Bootstrap-Skript. Ein neuer Entwickler installiert VS Code und führt anschließend einen einzigen Befehl aus:
curl -sSL https://raw.githubusercontent.com/company/dev-environment-config/main/scripts/setup-new-developer.sh | bash
Ungefähr zehn Minuten später ist die Arbeitsumgebung bereit. Das Direktausführen eines entfernten Skripts in bash ist praktisch, erfordert jedoch Vorsicht: Das Skript wird mit den Berechtigungen des Benutzers sowie dem Inhalt der Branch zu diesem Zeitpunkt ausgeführt. Für ein internes Tool ist das in der Regel akzeptabel, doch viele Teams bevorzugen es, dass die Mitarbeiter den Repository klonen und das Skript lokal ausführen, um es zunächst lesen zu können.
Im Hintergrund arbeitet das Skript nach einer festgelegten Abfolge:
#!/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"
Es gibt einige Details, die man beachten sollte. set -e beendet den Ablauf bereits bei dem ersten fehlerhaften Befehl, wodurch eine halb konfigurierte Arbeitsumgebung nicht als vollständig erscheint. Der Hilfsprogramm check_and_install wird nicht gezeigt und muss für jedes Betriebssystem separat implementiert werden. Das Parsen von JSON mit einem eingebetteten Python-One-Liner funktioniert, doch das Update-Skript verwendet später jq; daher verringert die Standardisierung auf ein Werkzeug die erforderlichen Voraussetzungen. Das Kopieren von .env.example liefert sichere Standardwerte, funktioniert aber nur, wenn die Beispieldateien vollständig erhalten bleiben und keine echten Geheimnisse enthalten.
Synchronisierung der gemeinsamen Konfiguration in alle Dienste
Das Synchronisierungs-Skript durchläuft nacheinander die Dienste und kopiert die gemeinsamen Dotfiles sowie die VS Code-Einstellungen in jeden von ihnen, wobei vorhandene Inhalte überschrieben werden.
#!/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
Der Auszug geht davon aus, dass SCRIPT_DIR und SERVICES bereits definiert sind, weshalb das eigentliche Skript sie selbst ermitteln muss (zum Beispiel aus $0 und dem Manifest). Die Aufrufe von cp -f sind beabsichtigt: Durch Überschreiben wird die Abweichung beseitigt. Der Nachteil ist, dass alle lokalen Änderungen an diesen Dateien beim nächsten Ausführungsvorgang verloren gehen, weshalb die Anpassungen pro Service in separaten Dateien gespeichert werden müssen, wie weiter unten erläutert. Überlegen Sie außerdem, ob eine Datei wie .gitignore tatsächlich überall identisch sein sollte, da Services oft eigene Einträge benötigen.
Die tägliche automatische Aktualisierungsaufgabe
Dies ist der Mechanismus, der die Maschinen ohne dass jemand etwas erinnert werden muss auf dem neuesten Stand hält. Jeden Werktag um 8 Uhr morgens führt der Cron-Job auto-update.sh aus:
#!/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"
Betrachten Sie dieses Skript eher als Ausgangspunkt und nicht als etwas, das unverändert eingesetzt werden soll:
- Änderungen werden gespeichert, aber nie wiederhergestellt. Ein Entwickler, der die Arbeit über Nacht unkommitet liegen lässt, muss daher wissen, dass er
git stash popausführen muss; andernfalls scheint die Arbeit verschwunden zu sein. git diff HEAD~1prüft nur den letzten Commit. Wenn der Pull mehrere Commits mitgebracht hat, wird eine Änderung inpackage.jsonaus einem früheren Commit übersehen. Ein Vergleich mit dem Commit vor dem Pull ist zuverlässiger.- Ein Rebase, bei dem Konflikte auftreten, lässt das Repository mitten im Rebase zurück, ohne dass jemand darauf achtet.
git pull --ff-onlyauszuführen und bei Fehler die Dienste zu überspringen, ist für unaufsichtige Aufgaben sicherer.
notify-send existiert nur auf Linux-Desktops, und cron läuft nicht auf einem Rechner, der um 8 Uhr morgens in den Ruhezustand geht, sodass macOS-Nutzer möglicherweise launchd oder einen anderen Auslöser benötigen.Der End-zu-End-Fluss verbindet den Vorschlag eines Senior Engineers mit der Arbeit jedes Entwicklers am nächsten Morgen:
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│
└─────────────────────┘
Eine gemeinsame Bibliothek anstelle von Kopieren und Einfügen
Schutzmechanismen, Interceptor-Objekte, Ausnahmefilter und DTOs werden oft in verschiedenen Services dupliziert und entwickeln sich anschließend auseinander. Durch das Verpacken dieser Komponenten als privates npm-Modul, @company/common-lib, erhält jeder Service nur einen einzigen Import-Punkt:
// 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,
});
}
}
Ein Controller kann gemeinsame Authentifizierungs- und Rollenschutzmechanismen, einen Antwort-Interceptor sowie den HTTP-Ausnahmefilter verwenden, ein gemeinsames Paginierungs-DTO akzeptieren und einen McpService-Wrapper für eine Qwen MCP-Integration nutzen. Wenn die Bibliothek neue Schutzmechanismen oder Verbesserungen am Ausnahmefilter erhält, übernimmt die automatische Aktualisierungsfunktion diese in alle Dienste.
Bedenken Sie, dass npm update den SemVer-Bereich in jedem package.json berücksichtigt, wodurch große Versionenveröffentlichungen nicht automatisch übernommen werden – was in der Regel gewünscht ist. Gemeinsam genutzte Bibliotheken führen außerdem zu einer Kopplung zwischen den Diensten; der Artikel unter wann gemeinsamer Code vorhanden sein sollte erklärt, wie man entscheidet, was in welche Bibliothek gehört.
Neue Dienste erstellen
Früher bedeutete die Erstellung eines neuen Services einen ganzen Tag damit, einen bestehenden zu kopieren und das Unnötige zu löschen. Ein Generierskript verwandelt dies in eine kurze, interaktive Anweisung:
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
Weil jeder neue Service von demselben Template ausgeht, beginnt er ohne Abweichungen.
Vorteile für das Team
Ein Team, das diese Einrichtung übernommen hat, berichtete von folgenden Effekten:
- Onboarding in Minuten: Ein Befehl ersetzte eine vierzig Schritte umfassende Wiki-Seite, wodurch die Zeit zum Starten jedes Services von etwa drei Tagen auf rund fünfzehn Minuten verkürzt wurde und die Anfragen per Chat fast vollständig verschwanden.
- Keine Abweichungen mehr: Services hatten sich stillschweigend voneinander entfernt (ESLint 8 in einem, 7 im anderen, veraltete Dockerfiles); geplante Überschreibungen halten sie identisch.
src/common/, dieselbe Docker-Einrichtung sowie derselbe Health-Check-Pfad in jedem Service.Lektionen für die Einführung
Fangen Sie mit dem kleinsten, nützlichen Teil an
Es ist nicht notwendig, all das auf einmal zu implementieren. Das Synchronisieren nur der ESLint- und Prettier-Einstellungen bringt bereits schnell sichtbare Vorteile. Die Docker-Konfiguration, Editor-Einstellungen, die gemeinsame Bibliothek sowie die geplanten Updates können hinzugefügt werden, sobald das Team diesem Ansatz vertraut.
Lassen Sie Raum für lokale Übernahmen
Eine gemeinsame Grundlage sollte keine Fessel sein. Ein Service, der eine zusätzliche Regel benötigt, kann ein .eslintrc.local.js hinzufügen, das die gemeinsame Konfiguration erweitert; da das Synchronisierungsskript dieses Datei niemals berührt, bleibt die Übernahme bei jedem Ausführungsvorgang bestehen.
Machen Sie Updates sichtbar
Die Desktop-Benachrichtigung mag wie eine kleine Ergänzung erscheinen, doch sie informiert die Entwickler darüber, dass das System existiert und sich etwas geändert hat. Stille Updates führen oft zu Verwirrung, wenn die Tools über Nacht anders funktionieren.
Verschönern Sie alles mit Versionen
Ohne die Manifest-Version kann der Update-Vorgang nicht erkennen, ob sich etwas geändert hat, und würde bei jedem Ausführungsvorgang neu synchronisieren. Die Versionierung bietet außerdem etwas Konkretes, auf das man sich beziehen kann, wenn eine Änderung Probleme verursacht.
Die Technologien hinter dem Beispiel
In der Beispielausstattung kamen NestJS und React (beide in TypeScript), Qwen über MCP, Docker sowie Docker Compose, Node.js 20 LTS, PostgreSQL 16, Redis 7, GitHub Actions, VS Code sowie Bash zusammen mit cron für die Synchronisierung zum Einsatz. Diese Versionen spiegeln die Ausstattung zu dem Zeitpunkt wider; prüfen Sie vor der Nachahmung die aktuellen LTS-Versionen.
Haupterkenntnisse
- Sobald man mehr als nur wenige Dienste und Entwickler hat, lohnt sich ein zentrales Konfigurationsrepository schnell.