Загрузка
Все статьи

Docker Compose для локальной разработки Kotlin-микросервисов — от 10 минут до 30 секунд старта

Как мы оптимизировали локальное окружение из 10+ микросервисов с помощью Docker Compose, multi-stage Dockerfile и правильных health checks.

Представьте: понедельник утро, standup через 15 минут, а локальное окружение ещё не поднялось. Десять микросервисов, у каждого свой PostgreSQL, Redis, Kafka — и всё это стартовало последовательно, без health checks. Я засекал: 10 минут 23 секунды от docker-compose up до первого ответа от API Gateway. Сегодня то же самое — 28 секунд. Рассказываю, что мы изменили.

Исходная ситуация

Наш docker-compose.yml выглядел примерно так: 15 сервисов без depends_on, без health checks, все тома привязаны к bind mounts всего проекта. Gradle собирал каждый сервис из исходников, скачивая зависимости каждый раз заново. Kafka стартовала раньше Zookeeper, микросервисы пытались подключиться к PostgreSQL до того, как он принимал соединения — и падали с connection refused.

Каждый второй день у кого-то что-то не поднималось. Разработчики тратили по 15-20 минут на «починить локалку» вместо написания кода.

Шаг 1: Health checks и правильный порядок старта

Первая и самая impactful-оптимизация — добавили health checks на все dependency-сервисы и настроили depends_on с condition: service_healthy.

version: "3.9"

services:
  postgres-order:
    image: postgres:16-alpine
    environment:
      POSTGRES_DB: orders_db
      POSTGRES_USER: orders
      POSTGRES_PASSWORD: orders_pass
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U orders -d orders_db"]
      interval: 2s
      timeout: 3s
      retries: 10
    volumes:
      - pg_order_data:/var/lib/postgresql/data
    ports:
      - "5432:5432"

  redis-cache:
    image: redis:7-alpine
    healthcheck:
      test: ["CMD", "redis-cli", "ping"]
      interval: 1s
      timeout: 2s
      retries: 5

  order-service:
    build:
      context: ./services/order-service
      dockerfile: Dockerfile.dev
    depends_on:
      postgres-order:
        condition: service_healthy
      redis-cache:
        condition: service_healthy
      kafka:
        condition: service_healthy
    environment:
      SPRING_DATASOURCE_URL: jdbc:postgresql://postgres-order:5432/orders_db
      SPRING_REDIS_HOST: redis-cache
    volumes:
      - ./services/order-service/src:/app/src

volumes:
  pg_order_data:

Результат: микросервисы больше не падают при старте. Они ждут, пока все зависимости станут доступны. Но время старта не изменилось — просто теперь оно надёжное.

Шаг 2: Multi-stage Dockerfile для быстрой сборки

Оригинальный Dockerfile копировал весь проект, запускал gradle build и потом java -jar. Каждый restart — полная пересборка. Мы переписали на multi-stage с кэшированием зависимостей:

# Dockerfile.dev
FROM eclipse-temurin:21-jdk-jammy AS builder

WORKDIR /app

# Кэшируем зависимости — этот слой меняется редко
COPY gradle.gradlele.kts build.gradle.kts settings.gradle.kts ./
COPY gradle/ gradle/
RUN gradle dependencies --no-daemon || true

# Исходники меняются часто — этот слой пересобирается
COPY src/ src/
RUN gradle bootJar --no-daemon -x test

FROM eclipse-temurin:21-jre-jammy
WORKDIR /app
COPY --from=builder /app/build/libs/*.jar app.jar
EXPOSE 8080
ENTRYPOINT ["java", "-agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=*:5005", "-jar", "app.jar"]

Ключевой приём: копируем файлы Gradle отдельно от исходников. При изменении кода Docker использует кэш слоя с зависимостями — и пересборка занимает 15-20 секунд вместо 2-3 минут.

Шаг 3: Hot reload через bind mounts

Мы примонтировали ./services/order-service/src:/app/src и включили Spring Boot DevTools. При изменении Kotlin-файла приложение перезагружается за 3-5 секунд — не нужно пересобирать Docker-образ. Для этого в build.gradle.kts добавили:

dependencies {
    developmentOnly("org.springframework.boot:spring-boot-devtools")
}

tasks.bootJar {
    // DevTools нужен только при запуске через gradle, не в jar
    // В dev-режиме запускаем через gradle bootRun в контейнере
}

Для dev-режима мы изменили entrypoint в compose.override.yml на gradle bootRun вместо java -jar. Это позволило DevTools работать корректно с live-reload.

Шаг 4: compose.override.yml для персональных настроек

Файл docker-compose.override.yml gitignore'd — каждый разработчик может подменить порты, переменные окружения или включить профилировщик без влияния на общую конфигурацию. У меня, например, там:

services:
  order-service:
    environment:
      SPRING_PROFILES_ACTIVE: "dev,debug"
      JAVA_TOOL_OPTIONS: "-Xmx256m -agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=*:5005"
    ports:
      - "5005:5005"  # Debugger
      - "8081:8080" # Избегаем конфликтов портов

Шаг 5: Параллельный старт

Docker Compose по умолчанию стартует сервисы параллельно, но наши микросервисы ждали друг друга из-за Kafka. Мы выделили инфраструктурные сервисы (PostgreSQL, Redis, Zookeeper, Kafka) в отдельный profile и стартовали их первым:

# Стартуем инфраструктуру (один раз)
docker-compose --profile infra up -d

# Ждём, пока всё станет здоровым
docker-compose --profile infra ps

# Поднимаем микросервисы
docker-compose up -d

Итог: до и после

До: 10 мин 23 сек, рандомные падения, 2-3 перезапуска в среднем. После: 28 секунд до готовности всех сервисов, ноль падений при старте, hot reload за 3-5 секунд. Команда сэкономила в среднем 40 минут в день на каждого разработчика. Если умножить на 6 человек и 22 рабочих дня — это почти 90 часов в месяц, которые теперь идут на разработку, а не на ожидание локалки.

Поделиться статьёй

Комментарии

0
0 / 2000

Пока нет комментариев

Будьте первым, кто оставит комментарий к этой статье

Читайте также

Terraform vs Pulumi — Infrastructure as Code для Kotlin-команды

Сравниваем Terraform на HCL и Pulumi на Kotlin для управления Kubernetes-инфраструктурой — с реальными примерами кода, болью миграции и нашим итоговым выбором.

14 июня 2026 11 мин

Nginx + PHP-FPM: тюнинг под высокую нагрузку — от 500 RPS до 5000 RPS

Сайт на PHP + Nginx выдерживал 500 RPS, а при 1000 начинал отдавать 502. Проблема была не в PHP-коде (он был оптимизирован), а в конфигурации Nginx и PHP-FPM: неправильный worker_connections, отсутствие keepalive к upstream, слишком маленький pm.max_children. Рассказываю пошагово, как мы увеличили пропускную способность в 10 раз.

23 мая 2026 11 мин

Gradle Kotlin DSL: кэширование и configuration avoidance — ускоряем сборку

Сборка Kotlin-проекта занимала 6 минут. После включения build cache, configuration cache и перехода на configuration avoidance API — 45 секунд. Рассказываю, что именно мы сделали, почему большинство туториалов по ускорению Gradle не работают, и как измерять реальный эффект от каждой оптимизации.

22 мая 2026 10 мин