Порты хоста ARM_CORE_PORT/TABULAR_PARSER_PORT/SCAN_GATEWAY_PORT (8000/8001/8002, в контейнере 8000); раздел «Профиль services»: команды, таблица портов, порядок старта, фактический вывод healthcheck, коды возврата up -d --wait (в т.ч. scan-gateway scan-emulator — 0), пересборка; сброс только с --profile services; предупреждение об интеграционных тестах при поднятом профиле. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_012vJkmBGKKqMoEMzvsXG3y2
Окружение разработки АРМ КА (docs/dev/)
Локальная инфраструктура workspace одной командой: PostgreSQL 16, NATS 2.10 без JetStream и MinIO с бакетом arm-ka.
Профиль services добавляет к ней сервисы системы, собранные из соседних репозиториев workspace: arm-core,
tabular-parser, scan-gateway и эмулятор ИС Сканирование scan-emulator (см. «Профиль services»). Без профиля
поднимается только инфраструктура, а сервисы можно запускать из venv (см. «Запуск сервисов из venv»).
Предварительные требования
- Docker и compose v2 (проверено на docker 28 / compose 2.34).
uvи Python 3.11 — для запуска сервисов из venv (версия Python — как на платформе).- Свободные порты хоста (по умолчанию доступны только с
127.0.0.1, см.BIND_ADDR): 5432 (PostgreSQL), 4222 и 8222 (NATS и его мониторинг), 9000 и 9001 (MinIO API и консоль); для профиляservicesещё 8000 (arm-core), 8001 (tabular-parser), 8002 (scan-gateway). Занятые порты меняются в.env(см. «Типичные проблемы»). - Для профиля
services: соседние каталоги../../arm-core,../../tabular-parser,../../scan-gatewayи../../fixturesотносительноdocs/dev/(в workspace и в зеркальном worktree.wt/<срез>/это одни и те же соседиdocs). Сборка образов ставит зависимости из PyPI (строкиraisa_*отфильтрованы, Nexus не нужен).
Запуск инфраструктуры
Команды — из каталога docs/dev/:
cp .env.example .env # .env в git не попадает
docker compose up -d # вернётся сразу; healthy — примерно через 6 с
Ожидание готовности (для скриптов и Makefile):
docker compose up -d && docker compose up -d --wait postgres nats minio
Голый docker compose up -d --wait возвращает код 1: одноразовый minio-init (создаёт бакет и завершается с
кодом 0) compose 2.34 считает сбоем (container arm-ka-minio-init-1 exited (0)). Поэтому --wait нужно давать со
списком долгоживущих сервисов — как выше.
Проверка:
docker compose ps # postgres, nats, minio — Up (healthy); minio-init — Exited (0)
docker compose exec postgres sh -c 'psql -U "$POSTGRES_USER" -d "$POSTGRES_DB" -c "select version()"'
curl -s localhost:8222/healthz # {"status":"ok"}
curl -s localhost:8222/varz | grep -A1 jetstream # "jetstream": {} — JetStream не включён
docker compose run --rm --entrypoint sh minio-init -c 'mc alias set local http://minio:9000 "$MINIO_ROOT_USER" "$MINIO_ROOT_PASSWORD" >/dev/null && mc ls local'
# [...] 0B arm-ka/
Консоль MinIO — http://localhost:9001 (по умолчанию minioadmin / minioadmin); S3 API — localhost:9000.
Схема БД (dkb) создаётся миграциями arm-core, не compose.
Профиль services
Сервисы системы в контейнерах против той же инфраструктуры. Образы собираются из Dockerfile в корне соседних
репозиториев (../../arm-core, ../../tabular-parser, ../../scan-gateway) и получают теги arm-core:dev,
tabular-parser:dev, scan-gateway:dev. Настройки сервисов задаёт сам compose (переменные OPENBAO__* шаблона с
адресами внутри сети compose — postgres:5432, nats:4222, minio:9000, DEBUG=true); второй блок .env и
api/.env сервисов на контейнеры не влияют.
Команды — из каталога docs/dev/:
cp .env.example .env # если ещё не сделано
docker compose --profile services up -d --build # сборка образов и запуск; первая сборка — около минуты
docker compose --profile services up -d --wait arm-core tabular-parser scan-gateway scan-emulator # ждать healthy
| Сервис | Порт хоста (.env) |
Порт в контейнере | Что делает |
|---|---|---|---|
arm-core |
8000 (ARM_CORE_PORT) |
8000 | ядро: API, outbox/inbox recognition_job, подписка recognition.result |
tabular-parser |
8001 (TABULAR_PARSER_PORT) |
8000 | подписка recognition.tabular.request (queue group tabular-parser) |
scan-gateway |
8002 (SCAN_GATEWAY_PORT) |
8000 | подписка recognition.image.request (queue group scan-gateway), горячая папка — том hotfolder |
scan-emulator |
— | — | эмулятор ИС Сканирование: следит за Input/ тома hotfolder, ответы — из ../../fixtures/ocr (только чтение) |
arm-core-migrate |
— | — | одноразовый: alembic upgrade head и python -m scripts.seed --dev, затем arm-core стартует |
Все три сервиса внутри контейнера слушают 8000 (значение по умолчанию SERVER__PORT); различаются только порты
хоста. Сервис, запущенный из venv, по умолчанию тоже слушает 8000 на хосте — он конфликтует с контейнером arm-core
(остановите контейнер или задайте SERVER__PORT в api/.env).
Порядок старта задан depends_on: arm-core-migrate ждёт healthy postgres; arm-core — postgres, nats, minio,
успешного завершения minio-init и arm-core-migrate; tabular-parser и scan-gateway — nats, minio и
minio-init. Одноразовые minio-init и arm-core-migrate выполняются при каждом up профиля (миграции и seed
идемпотентны: повторный seed пишет 0 / 0 / N — без изменений) и остаются в ps -a как Exited (0).
Проверка (фактический вывод на чистых томах, 2026-10-04, сокращён):
docker compose --profile services ps -a
# arm-core Up (healthy) 127.0.0.1:8000->8000/tcp
# arm-core-migrate Exited (0)
# minio-init Exited (0)
# scan-emulator Up 8000/tcp (healthcheck нет: процесс без HTTP)
# scan-gateway Up (healthy) 127.0.0.1:8002->8000/tcp
# tabular-parser Up (healthy) 127.0.0.1:8001->8000/tcp
curl -s localhost:8000/healthcheck # {"uptime":"…","version":"1.0.0","nats":"ok"}
curl -s localhost:8001/healthcheck # {"uptime":"…","version":"1.0.0","nats":"ok"}
curl -s localhost:8002/healthcheck # {"uptime":"…","version":"1.0.0","nats":"ok","hotfolder":"ok"}
docker compose --profile services logs arm-core-migrate # Running upgrade -> 0001_dbml_v5_13 …; Seed применён …
docker compose --profile services logs scan-emulator # … корень /data/hotfolder …; Фикстуры: /fixtures/ocr, фикстур: 2
/healthcheck всех трёх сервисов всегда отвечает 200, поэтому healthcheck контейнеров читает поля ответа:
nats = ok (и hotfolder = ok у scan-gateway). Статус healthy означает, что сервис подключён к NATS.
Dev-токен arm-core (в compose включён dev-эмитент, AUTH_DEV_ISSUER=true, DEBUG=true; пользователи — из
seed --dev, см. ../../arm-core/README.md):
curl -s -X POST localhost:8000/api/v1/dev/token -H 'Content-Type: application/json' -d '{"email":"ka_rf@armka.local"}'
curl -s localhost:8000/api/v1/me -H "Authorization: Bearer <JWT>" # email, роли KA_RF, права
Образ arm-core содержит только бэкенд: фронт (web/) в Dockerfile не собирается, GET / отвечает 404. UI —
npm run dev из ../../arm-core/web (см. README arm-core).
Ожидание готовности и коды возврата --wait (compose 2.34, проверено 2026-10-04):
| Команда | Код | Комментарий |
|---|---|---|
docker compose --profile services up -d --wait arm-core tabular-parser scan-gateway scan-emulator |
0 | рекомендуемая форма |
docker compose --profile services up -d --wait scan-gateway scan-emulator |
0 | и на чистых томах, и на работающем окружении: minio-init поднимается как зависимость scan-gateway (service_completed_successfully) и его выход с кодом 0 сбоем не считается |
docker compose --profile services up -d --wait (без списка) |
0 | все одноразовые контейнеры профиля — зависимости с service_completed_successfully |
docker compose up -d --wait (без профиля и списка) |
1 | container arm-ka-minio-init-1 exited (0): без профиля у minio-init нет зависимых сервисов |
Для скриптов указывайте долгоживущие сервисы явно — форма не зависит от состава профиля.
Пересборка после изменения кода сервиса: docker compose --profile services up -d --build (или
up -d --build <сервис>). Образ arm-core:dev собирает только сервис arm-core, образ scan-gateway:dev — только
scan-gateway; arm-core-migrate и scan-emulator используют готовый тег (pull_policy: never): две сборки одного
тега давали разные ID образа, и каждый следующий up пересоздавал контейнеры. Поэтому arm-core-migrate или
scan-emulator в одиночку (up -d scan-emulator) на машине без образа не стартуют — сначала соберите его
(docker compose --profile services build scan-gateway) или поднимайте профиль целиком.
Остановка и сброс профиля — только с --profile services: без него compose не видит контейнеры профиля
(см. «Сброс окружения»).
Переменные .env
Файл читают docker compose (из этого каталога) и, вторым блоком, сервисы при запуске из venv. Значения по умолчанию —
только для dev.
Блок compose:
| Переменная | По умолчанию | Для чего |
|---|---|---|
BIND_ADDR |
127.0.0.1 |
адрес привязки всех портов хоста; 0.0.0.0 — доступ с других машин (небезопасно, см. «Примечания») |
POSTGRES_DB |
armka |
имя БД |
POSTGRES_USER |
armka |
пользователь БД |
POSTGRES_PASSWORD |
armka |
пароль БД |
POSTGRES_PORT |
5432 |
порт PostgreSQL на хосте |
NATS_PORT |
4222 |
клиентский порт NATS на хосте |
NATS_MONITOR_PORT |
8222 |
порт мониторинга NATS (/varz, /healthz) |
MINIO_ROOT_USER |
minioadmin |
администратор MinIO |
MINIO_ROOT_PASSWORD |
minioadmin |
пароль администратора MinIO |
MINIO_API_PORT |
9000 |
порт S3 API на хосте |
MINIO_CONSOLE_PORT |
9001 |
порт консоли MinIO на хосте |
MINIO_BUCKET |
arm-ka |
единственный бакет, создаётся minio-init |
ARM_CORE_PORT |
8000 |
порт хоста arm-core (профиль services; в контейнере 8000) |
TABULAR_PARSER_PORT |
8001 |
порт хоста tabular-parser (профиль services; в контейнере 8000) |
SCAN_GATEWAY_PORT |
8002 |
порт хоста scan-gateway (профиль services; в контейнере 8000) |
Блок сервисов (запуск из venv против compose по localhost, формат шаблона команды):
| Переменная | По умолчанию | Для чего |
|---|---|---|
DEBUG |
true |
dev-режим: пакеты raisa_* импортируются условно |
OPENBAO__DATABASE__DATABASE_URL |
postgresql+asyncpg://armka:armka@localhost:5432/armka |
подключение к PostgreSQL (asyncpg) |
OPENBAO__MINIO__MINIO_URL |
localhost:9000 |
адрес MinIO без схемы (host:port, так ждёт miniopy-async) |
OPENBAO__MINIO__MINIO_ADMIN_LOGIN |
minioadmin |
логин MinIO |
OPENBAO__MINIO__MINIO_ADMIN_PASSWORD |
minioadmin |
пароль MinIO |
OPENBAO__MINIO__MINIO_BUCKET |
arm-ka |
бакет |
OPENBAO__NATS__NATS_URL |
nats://localhost:4222 |
адрес NATS |
OPENBAO__NATS__NATS_SEED |
пусто | nkey seed; пусто — подключение без nkeys |
OPENBAO__NATS__NATS_JWT |
пусто | JWT NATS; пусто — подключение без nkeys |
OPENBAO__SMB__SMB_HOST |
localhost |
заглушка SMB (в Срезе 0 не используется); блок обязателен для Settings arm-core |
OPENBAO__SMB__SMB_PATH |
share |
заглушка SMB |
OPENBAO__SMB__SMB_LOGIN |
user |
заглушка SMB |
OPENBAO__SMB__SMB_PASSWORD |
password |
заглушка SMB |
NATS_ENABLED |
true |
включает подключение arm-core к NATS; без него /healthcheck показывает nats=off |
Два блока описывают одни и те же ресурсы: при смене порта, пароля, пользователя или бакета меняйте обе части.
Сброс окружения
docker compose --profile services down # останавливает все контейнеры проекта; тома сохраняются
docker compose --profile services down -v # полный сброс: тома postgres-data, minio-data и hotfolder (БД, бакет, горячая папка — с нуля)
Без --profile services команда down (с -v или без) не трогает контейнеры профиля: проверено — после
docker compose down -v контейнеры arm-core/tabular-parser/scan-gateway/scan-emulator продолжают работать,
том hotfolder и сеть arm-ka_default остаются (Network arm-ka_default Resource is still in use). Если профиль не
поднимался, docker compose down [-v] достаточно.
Имя проекта compose фиксировано (name: arm-ka), поэтому тома общие для любого worktree (.wt/<срез>/docs/dev):
сброс из одного worktree затрагивает все.
Запуск сервисов из venv без Nexus (черновик)
Черновик: будет дописан и проверен вручную в задаче 5.4 (сквозная проверка).
make venvиmake test-integrationвtabular-parserиscan-gatewayбудут доступны после задач этих сервисов; сейчас они есть вarm-core.
Пакеты raisa_* из Nexus банка вне банка недоступны (D-10). Поэтому make venv ставит requirements.txt без строк
raisa_* (фильтр grep -v '^raisa_'), код импортирует raisa_* условно (при DEBUG=true), а npm-пакеты raisa-*
в web/package.json вынесены в optionalDependencies.
Шаги для сервиса (пути — относительно каталога его репозитория; в workspace docs/ лежит рядом, ../docs/dev/):
# 1. поднять инфраструктуру (из docs/dev/, см. выше)
# 2. из корня репозитория сервиса:
cp ../docs/dev/.env.example api/.env
make venv # api/.venv: Python 3.11 + зависимости без raisa_*
cd api && .venv/bin/python run_server.py # сервис подключается к PostgreSQL/NATS/MinIO по localhost
# 3. в другом терминале:
curl -s localhost:<порт сервиса>/healthcheck
Из venv каждый сервис слушает порт из своих настроек (SERVER__PORT, по умолчанию 8000 у всех трёх) — порты
хоста 8001/8002 относятся только к контейнерам профиля services (см. таблицу портов выше). Два сервиса из venv
одновременно или сервис из venv рядом с профилем services требуют разных SERVER__PORT. Для arm-core в ответе
/healthcheck ожидаются version и nats = ok — при NATS_ENABLED=true (задан в .env.example; без него
nats = off). Поля /healthcheck в контейнерах: tabular-parser — uptime, version, nats; scan-gateway — ещё
hotfolder (шлюзу нужен SCAN_HOTFOLDER_PATH). Переменные сервисов из venv уточняются в задаче 5.4.
Интеграционные тесты сервисов против compose (черновик)
Черновик: уточняется после задач 3.x и 5.x.
Общий набор целей (up, test, test-integration, lint, check, check-full) есть во всех сервисных репозиториях
(arm-core, tabular-parser, scan-gateway); семантика, состав check-full, порядок перед PR и пропуск тестов без
docs/fixtures описаны в checks.md; эталон рецептов общих целей — Makefile.common. Кратко: make check — быстрые проверки без Docker, make check-full —
check + интеграционные тесты против этого compose + образ (для arm-core ещё сверка с DBML, миграции и web);
выполняется перед созданием PR.
Интеграционные тесты требуют поднятой инфраструктуры: сначала docker compose up -d && docker compose up -d --wait postgres nats minio, затем make test-integration (или make check-full) в репозитории сервиса. Для scan-gateway
эмулятор ИС Сканирование поднимается в процессе интеграционного теста; контейнер scan-emulator профиля services
тестами не используется. Общее описание проверок — checks.md.
Не запускайте интеграционные тесты при поднятом профиле
services(не проверялось, вывод из устройства обмена): контейнеры подписаны на те же subject'ы общего NATS в тех же queue group (arm-coreнаrecognition.result,tabular-parserиscan-gatewayна свои*.request), поэтому NATS может отдать сообщение теста контейнеру, а не экземпляру из теста. Перед тестами:docker compose --profile services stop arm-core tabular-parser scan-gateway scan-emulator.
Проверка контракта
Из корня репозитория docs: python scripts/validate_contracts.py — валидирует примеры в
contracts/examples/ тремя схемами из contracts/ (3 схемы, 8 примеров;
нужен jsonschema). Или make venv && make check (ещё openspec validate), см. checks.md.
Примечания
- Образы MinIO неофициальные. Официальные
minio/minioиminio/mcнедоступны (Docker Hub — denied, на quay.io нет нужных тегов), поэтому в compose стоятalpine/minio:RELEASE.2025-10-15T17-29-55Zиbitnamilegacy/minio-client:2025.7.21, а сервисminioзапущен сuser: "0:0"(иначе образ не пишет в свежий том) — только для dev. Решения R-5/R-6:design.md, раздел «Decisions made without discussion», решения №16–17. Замена на официальные образы — две строкиimage:вdocker-compose.yml(и удалениеuser: "0:0"). - Безопасность. По умолчанию порты публикуются только на
127.0.0.1(BIND_ADDR), снаружи машины они недоступны. Чтобы открыть их для других машин, задайте в.envBIND_ADDR=0.0.0.0— но пароли по умолчанию (armka,minioadmin) годятся только для dev, поэтому сначала смените их (в обоих блоках.env) и не делайте этого в недоверенной сети.
Типичные проблемы
- Порт занят (
address already in use): измените*_PORTв.envи одновременно соответствующий порт вOPENBAO__*(DATABASE_URL,MINIO_URL,NATS_URL); перезапуститеdocker compose up -d. Для портов сервисов (ARM_CORE_PORT,TABULAR_PARSER_PORT,SCAN_GATEWAY_PORT) достаточно.env. - Сервис профиля не становится healthy:
docker compose --profile services logs <сервис>.nats = down— NATS недоступен;hotfolder = downуscan-gateway— каталогSCAN_HOTFOLDER_PATH(томhotfolder) недоступен.arm-coreне стартует, покаarm-core-migrateне завершился с кодом 0:docker compose --profile services logs arm-core-migrate. No such image: arm-core:dev/scan-gateway:devпри запускеarm-core-migrateилиscan-emulatorпо отдельности: образ ещё не собран —docker compose --profile services build(см. «Профильservices»).minio-initзавис или завершился с ошибкой:docker compose logs minio-init; чаще всегоminioне стал healthy —docker compose logs minio. Повторныйdocker compose up -dидемпотентен.- Остаточные тома от прошлых запусков (старый пароль БД, бакет не тот):
docker compose down -vи запуск заново. up -d --waitвозвращает код 1: см. раздел «Запуск инфраструктуры» — указывайте сервисы явно.