Перейти к содержанию

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 Метрики
user 8001 через /metrics на 8001
review 8007 через /metrics на 8007
media 8008 через /metrics на 8008
notification 8013 через /metrics на 8013

Общие инфра-порты, когда сервис запущен единолично:

Компонент Порт
Postgres 5432 (в user — 3431, чтобы не конфликтовать, см. docker-compose.yml)
Redis 6379
Kafka (external) 9092
Kafka UI 8090
MinIO (media) 9000 (API), 9001 (console)
Prometheus 9090
Grafana 3000
Loki 3100
Tempo 3200

Если два сервиса надо запускать одновременно — в одном из них переопредели port через .env (для Postgres: DB_PORT=5433), либо меняй проброс в docker-compose.yml.

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

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>.)

С машины:

psql -h localhost -p 5432 -U <service>_user -d kazmaps

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

Полезные команды внутри psql:

\dn           — список схем
\dt <schema>.  — таблицы в схеме
\d <table>    — структура таблицы
SELECT * FROM <service>.outbox WHERE published_at 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.created \
    --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 другого сервиса. Либо останови его (make down там), либо переопредели порт в .env для текущего (DB_PORT=5433).

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

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

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.md.

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

  • .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.md, если ещё не.