arm-ka-docs/dev/docker-compose.yml
Andrey Epifancev a2811ebc52 docs(context): Срез 3a — правки по ревью (OQ-149, устаревшие описания эмулятора, locator, решения №28–35)
- 07-open-questions: OQ-149 переписан (пустой код у «БАЛАНС»/«Выручка», предупреждения №29, `|` экранирован), новый OQ-152
  (повтор кода 2410 в aep_6), уточнены OQ-137 и OQ-144 (Input)
- 05-domain-rules: §11 locator `file:<имя json>;table:<i>`; §11.1 п. 2 (длина даты №31), п. 4 (предупреждения о пропущенных
  строках), п. 7 (все точки удаляются, Money и confidence №30), п. 8 (ИНН неуверенно), п. 9 (№28), п. 10 (редакция не определена)
- 03-architecture: формулировка про `.part`; 06-test-data, dev/README (сценарии 2/2а), repo-templates/scan-gateway.CLAUDE.md —
  раскладка `<job_id>.<ext>` и пары по sha256
- dev/compose, qa-guide §6.8/§8 (ожидаемые предупреждения по эталонам, OQ-151), 09-delivery-plan (статус 3a)

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012vJkmBGKKqMoEMzvsXG3y2
2026-10-07 03:49:05 +04:00

247 lines
12 KiB
YAML
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# Инфраструктура разработки АРМ КА: PostgreSQL 16, NATS 2.10 (без JetStream, как на платформе), MinIO + бакет.
# Запуск: cp .env.example .env && docker compose up -d (из этого каталога)
# Ожидание готовности: docker compose up -d --wait postgres nats minio. Голый `up -d --wait` возвращает код 1, потому что
# одноразовый minio-init завершается (хотя и с кодом 0) — для скриптов указывайте сервисы явно.
# Все учётные данные, порты хоста и имя бакета берутся из .env (значения по умолчанию — dev).
# Порты публикуются только на 127.0.0.1; адрес привязки меняется через BIND_ADDR (см. .env.example).
# Профиль `services` — сервисы системы, собранные из соседних репозиториев workspace (../../<repo>, Dockerfile в корне):
# docker compose --profile services up -d --build
# docker compose --profile services up -d --wait arm-core tabular-parser scan-gateway scan-emulator
# Одноразовые minio-init и arm-core-migrate (миграции + seed) в список --wait не входят: они завершаются с кодом 0.
# Внутри контейнера каждый сервис слушает 8000; на хост публикуются 8000 (arm-core), 8001 (tabular-parser), 8002 (scan-gateway).
# Фиксированное имя проекта: одинаковые тома и сети из любого worktree (.wt/<срез>/docs/dev).
name: arm-ka
# Общие переменные сервисов профиля `services` (формат шаблона команды; адреса — внутри сети compose, не localhost).
x-service-env: &service-env
DEBUG: "true"
OPENBAO__MINIO__MINIO_URL: minio:9000
OPENBAO__MINIO__MINIO_ADMIN_LOGIN: ${MINIO_ROOT_USER:-minioadmin}
OPENBAO__MINIO__MINIO_ADMIN_PASSWORD: ${MINIO_ROOT_PASSWORD:-minioadmin}
OPENBAO__MINIO__MINIO_BUCKET: ${MINIO_BUCKET:-arm-ka}
OPENBAO__MINIO__MINIO_SECURE: "false"
OPENBAO__NATS__NATS_URL: nats://nats:4222
NATS_ENABLED: "true"
# arm-core и его одноразовые миграции: те же переменные плюс БД и dev-эмитент токенов.
x-arm-core-env: &arm-core-env
<<: *service-env
OPENBAO__DATABASE__DATABASE_URL: postgresql+asyncpg://${POSTGRES_USER:-armka}:${POSTGRES_PASSWORD:-armka}@postgres:5432/${POSTGRES_DB:-armka}
# Dev-эмитент JWT (POST /api/v1/dev/token) — только при DEBUG=true; секрет публичный, только для dev.
# Порт хоста по умолчанию на 127.0.0.1 (BIND_ADDR): при BIND_ADDR=0.0.0.0 токен может получить любой в сети.
AUTH_DEV_ISSUER: "true"
AUTH_DEV_SECRET: dev-only-secret-do-not-use-in-production-0123456789
services:
postgres:
image: postgres:16.10-alpine
environment:
POSTGRES_DB: ${POSTGRES_DB:-armka}
POSTGRES_USER: ${POSTGRES_USER:-armka}
POSTGRES_PASSWORD: ${POSTGRES_PASSWORD:-armka}
ports:
- "${BIND_ADDR:-127.0.0.1}:${POSTGRES_PORT:-5432}:5432"
volumes:
- postgres-data:/var/lib/postgresql/data
healthcheck:
test: ["CMD-SHELL", "pg_isready -h 127.0.0.1 -U $${POSTGRES_USER} -d $${POSTGRES_DB}"]
interval: 3s
timeout: 3s
retries: 20
start_period: 5s
# Core NATS без JetStream: флаг --jetstream не передаётся. Мониторинг — порт 8222 (/varz, /healthz).
nats:
image: nats:2.10.29-alpine
command: ["-m", "8222"]
ports:
- "${BIND_ADDR:-127.0.0.1}:${NATS_PORT:-4222}:4222"
- "${BIND_ADDR:-127.0.0.1}:${NATS_MONITOR_PORT:-8222}:8222"
healthcheck:
test: ["CMD-SHELL", "wget -q --spider http://127.0.0.1:8222/healthz || exit 1"]
interval: 3s
timeout: 3s
retries: 20
start_period: 2s
# Образы minio/minio и minio/mc сняты с Docker Hub и quay.io; используется сборка alpine/minio с RELEASE-тегом.
minio:
image: alpine/minio:RELEASE.2025-10-15T17-29-55Z
# Образ по умолчанию работает от пользователя minio и не может писать в свежий именованный том — в dev запускаем от root.
user: "0:0"
command: ["server", "/data", "--console-address", ":9001"]
environment:
MINIO_ROOT_USER: ${MINIO_ROOT_USER:-minioadmin}
MINIO_ROOT_PASSWORD: ${MINIO_ROOT_PASSWORD:-minioadmin}
ports:
- "${BIND_ADDR:-127.0.0.1}:${MINIO_API_PORT:-9000}:9000"
- "${BIND_ADDR:-127.0.0.1}:${MINIO_CONSOLE_PORT:-9001}:9001"
volumes:
- minio-data:/data
healthcheck:
test: ["CMD-SHELL", "wget -q --spider http://127.0.0.1:9000/minio/health/live || exit 1"]
interval: 3s
timeout: 3s
retries: 20
start_period: 3s
# Одноразовое создание бакета; идемпотентно (--ignore-existing), при повторном up снова завершается с кодом 0.
minio-init:
image: bitnamilegacy/minio-client:2025.7.21
depends_on:
minio:
condition: service_healthy
restart: "no"
environment:
MINIO_ROOT_USER: ${MINIO_ROOT_USER:-minioadmin}
MINIO_ROOT_PASSWORD: ${MINIO_ROOT_PASSWORD:-minioadmin}
MINIO_BUCKET: ${MINIO_BUCKET:-arm-ka}
entrypoint: ["/bin/sh", "-c"]
command:
- >-
mc alias set local http://minio:9000 "$$MINIO_ROOT_USER" "$$MINIO_ROOT_PASSWORD" &&
mc mb --ignore-existing "local/$$MINIO_BUCKET"
# --- Профиль `services` -------------------------------------------------------------------------------------------
# Одноразово: миграции arm-core (alembic upgrade head) и справочники с dev-пользователями (seed --dev).
# Оба шага идемпотентны; при каждом `up` профиля контейнер снова выполняется и завершается с кодом 0.
# Образ собирает сервис arm-core (build только у него): две сборки одного тега дают разные ID образа, и каждый
# следующий `up` пересоздавал бы arm-core-migrate и arm-core. pull_policy: never — образ локальный, не из реестра.
arm-core-migrate:
profiles: ["services"]
image: arm-core:dev
pull_policy: never
restart: "no"
depends_on:
postgres:
condition: service_healthy
environment: *arm-core-env
command: ["sh", "-c", "alembic upgrade head && python -m scripts.seed --dev"]
arm-core:
profiles: ["services"]
build:
context: ../../arm-core
image: arm-core:dev
restart: unless-stopped
depends_on:
postgres:
condition: service_healthy
nats:
condition: service_healthy
minio:
condition: service_healthy
minio-init:
condition: service_completed_successfully
arm-core-migrate:
condition: service_completed_successfully
environment: *arm-core-env
ports:
- "${BIND_ADDR:-127.0.0.1}:${ARM_CORE_PORT:-8000}:8000"
healthcheck:
# curl в python:3.11-slim нет; /healthcheck всегда отвечает 200, поэтому читаем поле nats
test:
- CMD
- python
- -c
- "import json,sys,urllib.request as u; d=json.load(u.urlopen('http://127.0.0.1:8000/healthcheck', timeout=3)); sys.exit(0 if d['nats']=='ok' else 1)"
interval: 5s
timeout: 5s
retries: 12
start_period: 10s
tabular-parser:
profiles: ["services"]
build:
context: ../../tabular-parser
image: tabular-parser:dev
restart: unless-stopped
depends_on:
nats:
condition: service_healthy
minio:
condition: service_healthy
minio-init:
condition: service_completed_successfully
environment: *service-env
ports:
- "${BIND_ADDR:-127.0.0.1}:${TABULAR_PARSER_PORT:-8001}:8000"
healthcheck:
test:
- CMD
- python
- -c
- "import json,sys,urllib.request as u; d=json.load(u.urlopen('http://127.0.0.1:8000/healthcheck', timeout=3)); sys.exit(0 if d['nats']=='ok' else 1)"
interval: 5s
timeout: 5s
retries: 12
start_period: 10s
# Эмулятор ИС Сканирование: тот же образ, что у шлюза (собирает сервис scan-gateway, см. arm-core-migrate); общий
# том hotfolder с шлюзом. Переменные MinIO/NATS не нужны.
# Фикстуры ответов ИС — ../../fixtures/ocr (только чтение). Входной файл шлюз называет <uuid>.<ext> (имя файла
# фикстуры не несёт); эмулятор считает sha256 содержимого и ищет пару PDF + набор <имя>.json / <имя>_*.json по хешу
# (aep_3.pdf, aep_6.pdf, ...). Файл без пары по содержимому — ответ-ошибка INVALID_DOCUMENT, пока SCAN_EMULATOR_DEFAULT
# пуст; чтобы такой файл получал набор по умолчанию, задайте SCAN_EMULATOR_DEFAULT=<имя набора>, например aep_6.
# PDF из fixtures/ocr без JSON-набора (aep_4/7/9/30–34) остаются INVALID_DOCUMENT и при заданном default (OQ-151).
scan-emulator:
profiles: ["services"]
image: scan-gateway:dev
pull_policy: never
command: ["python", "-m", "src.scan_emulator"]
restart: unless-stopped
environment:
SCAN_HOTFOLDER_PATH: /data/hotfolder
SCAN_FIXTURES_DIR: /fixtures/ocr
SCAN_EMULATOR_DELAY: "2"
# Набор по умолчанию для файлов без пары по содержимому. Пусто — такой файл получает INVALID_DOCUMENT.
# Файл, совпавший по хешу с PDF без JSON-набора, default не получает (OQ-151).
SCAN_EMULATOR_DEFAULT: ""
volumes:
- hotfolder:/data/hotfolder
- ../../fixtures/ocr:/fixtures/ocr:ro
scan-gateway:
profiles: ["services"]
build:
context: ../../scan-gateway
image: scan-gateway:dev
restart: unless-stopped
depends_on:
nats:
condition: service_healthy
minio:
condition: service_healthy
minio-init:
condition: service_completed_successfully
environment:
<<: *service-env
SCAN_HOTFOLDER_MODE: local
SCAN_HOTFOLDER_PATH: /data/hotfolder
# Очистка Output/<job_id>/ и входящего файла после чтения ответа (dev-том принадлежит нам). На шаре контура по
# умолчанию выключена: права на удаление не подтверждены (OQ-144).
SCAN_CLEANUP: "true"
volumes:
- hotfolder:/data/hotfolder
ports:
- "${BIND_ADDR:-127.0.0.1}:${SCAN_GATEWAY_PORT:-8002}:8000"
healthcheck:
# /healthcheck всегда отвечает 200, поэтому читаем поля nats и hotfolder
test:
- CMD
- python
- -c
- "import json,sys,urllib.request as u; d=json.load(u.urlopen('http://127.0.0.1:8000/healthcheck', timeout=3)); sys.exit(0 if d['nats']=='ok' and d['hotfolder']=='ok' else 1)"
interval: 5s
timeout: 5s
retries: 12
start_period: 10s
volumes:
postgres-data:
minio-data:
# Общая горячая папка scan-gateway и scan-emulator (Input/, Output/)
hotfolder: