Skip to Content
Онбординг03. Local stack

03. Локальный стек

Как поднять локальное окружение для работы над конкретным сервисом и как прогонять кросс-сервисные сценарии.

Подход

Каждый сервис-репо имеет свой docker-compose.yml, который поднимает его собственную инфру (postgres + redis + kafka + сам сервис). Когда ты работаешь над сервисом — ты работаешь с одним компоузом.

Для тестов кросс-сервисных сценариев со всеми сервисами одновременно будет отдельный full-stack compose в infra-репозитории (TBD). Пока его нет — поднимай per-service compose’ы рядом, при необходимости объединяй через host.docker.internal (подробности ниже).

Запуск

Первый раз:

cd ~/projects/<service> make bootstrap

make bootstrap:

  1. Копирует .env.example в .env, если .env отсутствует.
  2. Скачивает Go-модули (go mod download && go mod tidy).
  3. docker compose up -d — поднимает инфру и сам сервис.
  4. Ждёт healthcheck’и postgres/redis/kafka.
  5. Применяет миграции (либо через встроенный runner в main.go, либо через отдельный migrate-контейнер — зависит от сервиса).
  6. Сервис стартует и начинает слушать порт.

Последующие запуски:

make up # docker compose up -d без rebuild, если образ уже собран

Rebuild после правок кода:

make rebuild

Порты

Текущие порты сервисов (см. docker-compose.yml и internal/config/ каждого репо):

СервисHTTPМетрики
user8001через /metrics на 8001
review8007через /metrics на 8007
media8008через /metrics на 8008
notification8013через /metrics на 8013

Общие инфра-порты, пробрасываемые наружу контейнера. Это host-порты по умолчанию; конкретное значение фиксируется в docker-compose.yml сервиса и может быть переопределено через .env. Внутри compose-сети контейнеры всё равно общаются по именам (postgres:5432, kafka:9092) независимо от host-проброса.

КомпонентHost-порт (default)Переопределяется в .env
Postgres5432<SVC>_DB_HOST_PORT (например, USER_DB_HOST_PORT=3431)
Redis6379<SVC>_REDIS_HOST_PORT
Kafka (external)9092<SVC>_KAFKA_HOST_PORT
Kafka UI8090
MinIO (media)9000 (API), 9001 (console)
Prometheus9090
Grafana3000
Loki3100
Tempo3200

Если у тебя локально уже занят 5432 (системный Postgres) или запускаешь несколько сервис-compose’ов одновременно — переопредели host-порт через .env перед make up, проброс внутри контейнера не меняется. Сверяйся с docker-compose.yml своего сервиса: именно он — источник истины, какой host-порт открыт именно у тебя.

Проверка здоровья

curl -sf http://localhost:<port>/healthz # {"status":"ok"} curl -sf http://localhost:<port>/readyz # {"status":"ready"}

/healthz — liveness, отвечает 200, пока процесс жив. /readyz — readiness, проверяет pool/redis/kafka. Если вернул 503 — какая-то зависимость не отвечает, смотри логи.

Postgres

Открыть psql внутри контейнера:

make psql

(Реализовано как docker compose exec postgres psql -U <user> -d <db>.)

С машины (host-порт смотри в docker-compose.yml своего сервиса / в .env; 5432 — дефолт, но может отличаться):

psql -h localhost -p "${DB_HOST_PORT:-5432}" -U <service>_user -d <service>_db

У каждого сервиса — своя отдельная БД (user_db, review_db, media_db, notification_db). Локально допустимо поднимать их в одном Postgres-инстансе (в рамках одного docker-compose.yml), в проде каждая БД живёт в своём инстансе. Общей БД (типа kazmaps) со схемами-per-service у нас нет.

Пароль — из .env (локальный dev).

Полезные команды внутри psql (таблицы сервиса живут в схеме public его собственной БД, без префикса с именем сервиса):

\dn — список схем \dt — таблицы текущей БД (в схеме public) \d <table> — структура таблицы SELECT * FROM outbox WHERE offset_acked IS NULL;

Kafka

Быстрый просмотр — redpanda/kafdrop UI в compose (если подключён):

http://localhost:8090

Либо через CLI:

docker compose exec kafka /opt/kafka/bin/kafka-topics.sh \ --bootstrap-server localhost:9092 --list docker compose exec kafka /opt/kafka/bin/kafka-console-consumer.sh \ --bootstrap-server localhost:9092 \ --topic kazmaps.review.review \ --from-beginning

Если в Makefile есть make kafka-console — используй его.

Redis

make redis-cli # внутри контейнера redis-cli -h localhost -p 6379 # с машины

Полезные команды:

KEYS idempotency:* TTL rl:login:127.0.0.1 MONITOR # поток всех команд (выключи Ctrl+C)

MinIO (media-сервис)

  • API: http://localhost:9000 (S3-совместимый endpoint для SDK).
  • Console: http://localhost:9001 (веб-UI).

Логин/пароль — из .env (MINIO_ROOT_USER/MINIO_ROOT_PASSWORD).

Логи

Follow логи самого сервиса:

make logs

Другие контейнеры:

docker compose logs -f postgres docker compose logs -f kafka docker compose logs -f redis

Все контейнеры одновременно:

docker compose logs -f

Остановка

make down # stop, volumes сохраняются — данные на месте make clean # down -v, volumes удаляются — чистый старт

Volumes

Postgres-данные живут в named volume <service>_postgres_data (точное имя зависит от compose project). make down их не удаляет — в следующий раз поднимется та же БД с теми же записями.

Чистый старт:

make clean # или docker compose down -v && docker compose up -d

После make clean Postgres стартует пустым, миграции применяются заново.

Troubleshooting

Порт занят

Error: ports are not available: 0.0.0.0:5432 -> listen tcp 0.0.0.0:5432: bind: address already in use

Найти, кто держит порт:

  • macOS / Linux: lsof -i :5432
  • Windows: netstat -ano | findstr 5432

Обычно это уже запущенный Postgres другого сервиса (или системный Postgres на 5432). Либо останови его (make down в том проекте), либо переопредели host-порт в .env для текущего compose’а (например, <SVC>_DB_HOST_PORT=5433) и сделай make rebuild.

Миграции не применяются

См. ../troubleshooting/migration-fails.

Healthcheck висит

Смотри docker compose ps — кто unhealthy? Обычно Kafka дольше всех поднимается (до 30 секунд в первый раз).

«Connection refused» от сервиса в pool/redis

Сервис стартанул раньше инфры. Должен быть depends_on: condition: service_healthy в compose; если отсутствует — добавь и сделай make rebuild.

Cross-service сценарии

Два варианта, когда нужно проверить «сервис A зовёт сервис B»:

Вариант 1: оба compose’а рядом

# терминал 1 cd ~/projects/user && make up # терминал 2 cd ~/projects/review && make up

Чтобы review дозвонился до user внутри контейнера, используй host.docker.internal:8001 (на macOS/Windows) или 172.17.0.1:8001 (на Linux) в env-переменной USER_SERVICE_URL:

USER_SERVICE_URL=http://host.docker.internal:8001 make up

Портовые конфликты разруливай через .env per-compose.

Вариант 2: mock downstream в integration-тесте

Когда нужен не живой сервис, а просто детерминированный ответ — подними httptest.NewServer в тесте и подставь его URL в конфиг тестируемого сервиса. Подробнее — ../conventions/testing.

Переменные окружения

  • .envне коммитится, живёт локально. Сюда ты кладёшь свои dev-значения.
  • .env.example — коммитится, содержит placeholder’ы. При смене переменных в internal/config/ обновляй оба: пример и документацию в README.md сервиса.
  • docker-compose читает .env автоматически (с корня compose-файла).

Проверь, что всё ок

cd ~/projects/<service> make bootstrap curl -sf http://localhost:<port>/healthz curl -sf http://localhost:<port>/readyz make logs # смотри, что сервис пишет "starting ..." и без ERROR

Если до этого момента всё проходит — инфра поднята корректно. Переходи к 02-first-pr, если ещё не.

Last updated on