Москва: проверенные б/у ноутбуки с гарантией и проверкой перед оплатой
+7 906 722-94-45

Главная → Статьи → Локальный стек на Docker Compose: сервисы, лимиты и I/O

Docker Compose на ноутбуке: как собрать локальный стек сервисов и не утонуть в ресурсах

Практика локального Docker Compose-стека на ноутбуке: профили и healthchecks, лимиты CPU/RAM, volumes против bind mounts, порядок запуска и диагностика медленного I/O.

Прямой ответ

Локальный dev-стек (API + БД + кэш + очереди) на Docker Compose собирается надёжно, если соблюдать четыре правила: тяжёлые сервисы стартуют по профилям, а не всегда; у каждого сервиса заданы лимиты памяти; данные живут в именованных volumes, а не в bind mounts из Windows; порядок запуска задан healthcheck'ами, а не depends_on: condition: service_started. Эти четыре строки конфигурации устраняют 90% «почему ноутбук лёг» и «почему БД не поднялась» в локальной разработке.

Структура compose-файла, которая масштабируется

Базовый стек «API + Postgres + Redis» разрастается до десятка сервисов; держите его управляемым:

services:
  api:
    build: .
    ports: ["8000:8000"]
    depends_on:
      db: { condition: service_healthy }
    mem_limit: 1g
  db:
    image: postgres:16-alpine
    environment:
      POSTGRES_PASSWORD: ${DB_PASSWORD}
    volumes: [pgdata:/var/lib/postgresql/data]
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U postgres"]
      interval: 5s
      retries: 10
    mem_limit: 1g
  redis:
    image: redis:7-alpine
    mem_limit: 256m
volumes:
  pgdata:

Ключевые моменты: pg_isready в healthcheck — приложение стартует, когда БД реально принимает подключения, а не когда контейнер «создан»; mem_limit не даёт одному сервису выдавить остальные; секреты приходят из .env, а не хардкодом — правила обращения с ними в статье про секреты и .env.

Профили: тяжёлое — по запросу

Elasticsearch, Kafka, мониторинг нужны не каждый день. Профили Compose решают это штатно:

  elasticsearch:
    image: elasticsearch:8.14.0
    profiles: ["full"]
    mem_limit: 2g

docker compose up поднимает базовый стек; docker compose --profile full up — расширенный. Типичная экономия на 16 ГБ ноутбуке — 3–6 ГБ памяти, которые Elasticsearch/Kafka/Prometheus съедали бы постоянно. Альтернатива профилям — отдельные compose-файлы (-f base.yml -f observability.yml), удобная для CI.

Volumes против bind mounts: вопрос скорости

Два сценария, два разных механизма:

  1. Данные сервисов (БД, кэши) — только именованные volumes (pgdata:). Внутри WSL2/VM они живут в ext4 и работают на полной скорости NVMe.
  2. Ваш код — bind mount допустим для hot-reload, но на Windows держите проект внутри WSL (~/projects): bind mount из NTFS (/mnt/c/...) проходит через трансляцию файловых вызовов и замедляет I/O в разы — это же правило объяснено в статье про WSL2 и Docker.

Симптом ошибки — миграции и тесты, которые «на сервере летают, на ноутбуке ползают»: почти всегда это bind-mounted БД-каталог или код на NTFS.

Лимиты и приоритеты на ноутбуке

  • Считайте бюджет: сумма mem_limit активных сервисов + 1–2 ГБ на Docker-движок должна влезать в лимит WSL2/Docker Desktop.
  • Для Elastic/JVM-сервисов задавайте и heap (ES_JAVA_OPTS=-Xms512m -Xmx512m) — иначе JVM заберёт память под себя сверх лимита контейнера.
  • docker compose up -d --scale worker=3 для локальной проверки конкурентности; не забывайте, что каждая реплика — своя память.
  • Мониторинг: docker stats в отдельном терминале; контейнер с OOMKilled: true в docker inspect — признак слишком жёсткого mem_limit.

Порядок отладки стека

  1. docker compose ps — что живо, что restarting.
  2. docker compose logs -f <svc> — ошибка сервиса; у БД смотрите первые 50 строк (инициализация).
  3. Порт занят на хосте? docker compose config покажет итоговые порты; конфликт лечится сменой внешнего порта ("8080:8000").
  4. «Работало вчера, сегодня не поднимается» — чаще всего испорченный volume после аварийного останова: docker compose down (без -v), затем проверка логов инициализации; -v (удаление данных) — только осознанно.
  5. Полный сброс без потери образа: docker compose down --rmi local --remove-orphans.

Когда сервисов становится больше десяти и появляются сервис-дискавери/ингрессы — это сигнал посмотреть в сторону локального Kubernetes: сравнение k3s/minikube/kind и их бюджеты памяти — в отдельной статье про Kubernetes на ноутбуке.

Сценарий дня: экономим ресурсы без отказа от стека

Реалистичный рабочий цикл на 16 ГБ ноутбуке выглядит так, и это экономит 2–4 ГБ в течение дня:

  1. Утром: docker compose up -d db redis — только то, что нужно сейчас; API-сервис запускаете из IDE, а не контейнером (hot-reload быстрее, память дешевле).
  2. Понадобились очереди/поиск: docker compose --profile full up -d — тяжёлые сервисы стартуют на время задачи.
  3. Обед/встреча: docker compose stop — данные в volumes сохраняются, память освобождается мгновенно; возобновление — start за секунды.
  4. Вечером: docker compose down (без -v!) плюс раз в неделю docker builder prune -f — кэш сборок растёт незаметно.
  5. Раз в месяц: проверка docker system df и удаление висячих образов (docker image prune -a — осторожно, удалит неиспользуемые теги; при следующем запуске они скачаются заново).

Эта дисциплина важнее любой разовой настройки: «вечно поднятый» стек из десяти сервисов — главная причина, по которой разработчики на 16 ГБ живут в swap и не понимают почему. Для контроля суммарного бюджета (Docker + IDE + браузер) используйте методику измерения из статьи про WSL2 и Docker — там же описаны лимиты на стороне гипервизора.

FAQ

Нужен ли Docker Desktop для Compose? Нет: Compose v2 — плагин Docker Engine, на Linux/в WSL2 работает без Desktop. Desktop даёт GUI и интеграцию с Windows/macOS — вопрос удобства, не функциональности.

Как обновить образы стека без простоя данных? docker compose pull && docker compose up -d: пересоздадутся только изменившиеся сервисы, volumes не тронутся. Мажорные версии БД обновляйте по документации вендора (dump/restore или штатный апгрейд).

Почему depends_on не гарантирует готовность БД? По умолчанию он ждёт только старта контейнера. Готовность определяет healthcheck + condition: service_healthy — без него приложение стартует раньше БД и падает.

Можно ли держать стек запущенным постоянно? Можно, но считайте память: «дежурные» Postgres+Redis — это ~0,7–1,5 ГБ постоянно. На 16 ГБ ноутбуке разумнее поднимать стек на время работы: docker compose stop сохраняет данные и освобождает память.

Чем заменить тяжёлые сервисы в локальном стеке? Для поиска вместо Elasticsearch часто хватает Postgres FTS или Meilisearch (в 10 раз легче по памяти); вместо Kafka в dev-сценариях — Redis Streams; вместо полноценного мониторинга — точечные логи и docker stats. Принцип: локальный стек должен проверять логику приложения, а не воспроизводить прод один в один.

Источники: документация Docker Compose (профили, healthchecks), Docker Engine storage (ссылки в шапке).