Главная / Статьи / Централизованный репозиторий конфигураций для микросервисов: настройка и синхронизация одним командным приказом

Централизованный репозиторий конфигураций для микросервисов: настройка и синхронизация одним командным приказом

Создайте центральный репозиторий конфигураций с скриптами настройки, синхронизации и ежедневного обновления, чтобы все сервисы на NestJS и React использовали одинаковые настройки проверки кода, редактора и Docker.

2452 слов

Когда команда управляет дюжиной микросервисов на Node.js, самой сложной частью первой недели работы нового сотрудника зачастую бывает не код, а среда разработки: правильная версия Node, соответствующие правила ESLint и Prettier, корректные значения файла .env и настройки Docker, аналогичные у остальных. Каждый сервис со временем начинает отклоняться от стандартов, и старшие инженеры вынуждены выполнять функции службы поддержки. В этой статье рассказывается, как решить эту проблему с помощью единого хранилища конфигураций и нескольких скриптов на Bash, которые за одну команду настраивают машину, копируют общие параметры в каждый сервис и поддерживают их актуальность. Вы также узнаете, какие у этих скриптов требуются доработки перед тем, как можно будет полагаться на них.

Как постепенно возникает отклонение конфигурации

Представьте опытного разработчика, который присоединяется к команде, отвечающей за двенадцать сервисов. Первый день проходит на установке Node и Docker, настройке стиля ESLint в команде, попытках определить, какие переменные должны находиться в том или ином файле .env, и задавании в чате вопросов о версии NestJS, используемой сервисами. Затем auth-service начинает работать некорректно из-за ошибки CORS, вызванной незначительными отличиями в локальных настройках, а позже первый pull request терпит неудачу на этапе CI из-за того, что общая конфигурация Prettier была изменена месяц назад, в то время как локальная копия осталась прежней.

Ни одна из этих проблем не является критической, но в совокупности они сильно увеличивают затраты на работу. Когда каждый сервис имеет свои собственные, незначительно отличающиеся правила линтинга, Dockerfiles, структуру папок и настройки редактора, это приводит к потере времени у новичков и к тому, что опытные инженеры тратят по несколько часов в неделю на ответы на вопросы, связанные с настройкой.

Один репозиторий как источник истины

Решением является специальный репозиторий, здесь называемый dev-environment-config, в котором хранится всё, что необходимо разработочной машине: шаблоны проектов, правила проверки кода и форматирования, настройки VS Code, базовая конфигурация Docker, скрипты автоматизации и общая библиотека. Когда кто-то вносит изменения, все разработочные машины автоматически получают их уже на следующее рабочее утро.

Общая структура выглядит следующим образом: центральный репозиторий питает каждый сервис:

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   │
└──────────┘ └──────────┘ └──────────┘

Постепенное создание системы

Структура центрального репозитория

Внутри репозитория каждая категория имеет свою папку. В шаблонах хранятся готовые стартовые проекты NestJS и React, в папке shared/ — файлы для ESLint, Prettier, EditorConfig, правила игнорирования файлов в Git и commitlint, в папке vscode/ — настройки редактора, а в папке 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

Файл manifest.json обеспечивает возможность выполнения остальных автоматизированных операций. В нем указывается общая версия, дата и имя пользователя, совершивших последнюю правку, версии для каждой области конфигурации, а также список сервисов, принадлежащих рабочему пространству.

{
  "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"
  ]
}

Правило простое: любые изменения в общей конфигурации приводят к увеличению версии высшего уровня. Скрипт обновления затем сравнивает версии, чтобы определить, нужно ли что-то синхронизировать; если забыть увеличить версию, изменения никогда не будут применены. Проверка в CI, которая срабатывает при изменении файлов в папке shared/ без увеличения версии, является простым средством защиты.

Внедрение с помощью одной команды

Самым большим преимуществом является скрипт запуска. Новый разработчик устанавливает VS Code, а затем выполняет одну команду:

curl -sSL https://raw.githubusercontent.com/company/dev-environment-config/main/scripts/setup-new-developer.sh | bash

Примерно через десять минут рабочее пространство готово. Передача удаленного скрипта непосредственно в bash удобна, но требует осторожности: скрипт выполняется с правами пользователя и с тем содержимым ветки, которое имеется в данный момент. Для внутреннего инструмента это обычно приемлемо, однако многие команды предпочитают, чтобы сотрудники клонировали репозиторий и запускали скрипт локально, чтобы сначала его прочитать.

По сути скрипт работает по фиксированной последовательности:

#!/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"

Стоит обратить внимание на несколько деталей. Команда set -e прерывает выполнение при первой неудачной команде, что предотвращает создание неполностью настроенной рабочей среды, выглядящей как готовая. Вспомогательная функция check_and_install не показана и должна быть написана отдельно для каждой операционной системы. Разбор JSON с помощью встроенного однострочного скрипта на Python работает, но позже скрипт обновления использует jq, поэтому использование одного инструмента сокращает список необходимых компонентов. Копирование файла .env.example предоставляет безопасные значения по умолчанию, но это работает только в том случае, если примерные файлы сохраняются полными и не содержат реальных секретов.

Синхронизация общей конфигурации во все сервисы

Скрипт синхронизации проходит по всем сервисам и копирует общие файлы с расширением .dotfile и настройки VS Code в каждый из них, перезаписывая существующие данные.

#!/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

В этом фрагменте предполагается, что переменные SCRIPT_DIR и SERVICES уже определены, поэтому сам скрипт должен вычислять их самостоятельно (например, на основе $0 и манифеста). Вызовы команды cp -f сделаны намеренно: именно перезапись позволяет устранять отклонения. Недостаток заключается в том, что любые локальные изменения в этих файлах будут потеряны при следующей загрузке, поэтому настройки для каждого сервиса должны храниться в отдельных файлах, как описано ниже. Также стоит подумать, действительно ли файл вроде .gitignore должен быть одинаковым везде, поскольку сервисам часто требуются собственные записи.

Ежедневная задача автоматического обновления

Это тот механизм, который поддерживает согласованность устройств без необходимости постоянного вмешательства человека. Каждый рабочий день в 8 утра задача cron запускает скрипт 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"

Рассматривайте этот скрипт как отправную точку, а не как что-то, что можно развернуть без изменений:

  • Изменения сохраняются в резерве, но никогда не применяются, поэтому разработчику, который ушел с работы, не сохранив изменения, необходимо знать, как выполнить команду git stash pop; в противном случае его работа кажется исчезнувшей.
  • git diff HEAD~1 анализирует только последнюю коммитацию. Если при синхронизации было применено несколько коммитов, изменения в файле package.json, сделанные ранее, могут остаться незамеченными; более надежным является сравнение с версией перед синхронизацией.
  • При ребейсе, возникающем из-за конфликтов, репозиторий остается в процессе ребейса без наблюдения. Для автономных задач более безопасно использовать команду git pull --ff-only и пропускать запуск сервиса в случае ошибки.
  • notify-send существует только в Linux-десктопных системах, а cron не работает на устройстве, которое выключено в 8 утра, поэтому пользователям macOS может потребоваться launchd или другой механизм запуска.
  • Целостный процесс связывает действия старшего инженера с работой каждого разработчика на следующее утро:

    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│
        └─────────────────────┘
    

    Общая библиотека вместо копирования-вставки

    Механизмы защиты, интерцепторы, фильтры исключений и DTO часто дублируются между сервисами, после чего начинают отличаться друг от друга. Их объединение в приватный модуль npm @company/common-lib позволяет каждому сервису использовать один и тот же импорт:

    // 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,
        });
      }
    }
    

    Контроллер может использовать общие механизмы аутентификации и проверки ролей, интерцептор ответов и фильтр исключений HTTP, принимать общий DTO для пагинации и использовать обертку McpService для интеграции с Qwen MCP. Когда библиотека получает новый механизм защиты или исправление фильтра исключений, задача автоматического обновления внедряет их во все сервисы.

    Следует помнить, что команда npm update учитывает диапазон версий в каждом файле package.json, поэтому крупные обновления не будут автоматически применены, что обычно и желательно. Общие библиотеки также создают связь между сервисами; статья о том, когда следует использовать общий код рассказывает, как определить, что должно находиться в одном сервисе.

    Создание новых сервисов

    Раньше создание нового сервиса означало целый день копирования существующего варианта и удаления ненужных элементов. Скрипт-генератор превращает этот процесс в короткую интерактивную команду:

    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
    

    Поскольку каждый новый сервис создается на основе одного и того же шаблона, он с самого начала не имеет отклонений.

    Что получает команда

    Команда, внедрившая такую схему, отметила следующие преимущества:

    • Интеграция за несколько минут: одна команда заменила страницу вики с сорока шагами, что сократило время запуска каждого сервиса с примерно трех дней до примерно пятнадцати минут; вопросы по настройке через чат практически исчезли.
    • Отсутствие отклонений: сервисы раньше постепенно отличались друг от друга (ESLint 8 в одном, 7 — в другом, устаревшие Dockerfiles); запланированные перезаписи сохраняют их идентичность.
  • Возвращение к активной работе: примерно по часу в день на настройку поддержки и устранение сбоев CI, связанных только с форматированием; примерно пять часов в неделю — на работу над новыми функциями.
  • Стабильные показатели качества: одинаковые исправления ошибок форматирования при сохранении, обязательные сообщения о коммитах от Husky и автоматически устанавливаемые расширения, благодаря чему во время ревью обсуждается дизайн, а не форматирование.
  • Обновления без объявлений: новое правило, образ Docker, исправление расширения или библиотеки становятся доступны всем уже на следующий день.
  • Прогнозируемая структура: одинаковые шаблоны в папке src/common/, настройка Docker и путь для проверки работоспособности в каждом сервисе.
  • Начинайте с самого маленького полезного элемента

    Не нужно создавать всё это сразу. Синхронизация только настроек ESLint и Prettier позволяет быстро получить ощутимую пользу. Настройки Docker, параметры редактора, общая библиотека и планируемые обновления могут быть добавлены позже, когда команда уверится в эффективности данного подхода.

    Оставьте возможность для локальных изменений

    Общая базовая конфигурация не должна быть слишком строгой. Служба, которой требуется дополнительное правило, может создать файл .eslintrc.local.js, расширяющий общую конфигурацию; поскольку скрипт синхронизации никогда не трогает этот файл, локальные изменения сохраняются при каждом запуске.

    Сделайте обновления заметными

    Уведомление на рабочем столе кажется незначительной деталью, но оно сообщает разработчикам о существовании системы и о том, что что-то изменилось. Безусловные обновления часто вызывают путаницу, когда инструменты внезапно начинают вести себя иначе.

    Версионируйте всё

    Без указания версии манифеста задача обновления не сможет определить, изменилось что-либо или нет, и будет выполнять синхронизацию при каждом запуске. Версионирование также предоставляет конкретную основу для справки, когда изменения вызывают проблемы.

    Технологическая стековая схема примера

    В примере использовались NestJS и React (оба на TypeScript), Qwen с доступом через MCP, Docker и Docker Compose, Node.js 20 LTS, PostgreSQL 16, Redis 7, GitHub Actions, VS Code, а также Bash и cron для синхронизации. Указанные версии соответствуют настройкам в момент создания примера; перед копированием проверьте актуальные версии LTS.

    Основные выводы

    • Когда у вас более нескольких сервисов и разработчиков, централизованный репозиторий конфигураций быстро окупает себя.
  • Основные компоненты — это репозиторий конфигурации с шаблонами и общими файлами, скрипт настройки за одну команду, скрипт синхронизации, запланированный обновитель, общая библиотека npm и генератор сервисов.
  • Замена общих файлов помогает избежать отклонений, поэтому с самого начала следует создавать специальные файлы для замены.
  • Операции в Git без присмотра представляют наибольший риск; лучше использовать метод fast-forward pull, восстанавливать временные сохранения и выявлять сбои, а не скрывать их.
  • Результатом является то, что разработчики тратят время на написание кода продукта, а не на воссоздание среды друг друга.