iac/apps/mapper/CONFIGURATION.md

16 KiB
Raw Permalink Blame History

Конфигурация проекта mapper (flows mapper)

Документ описывает все переменные окружения и способы конфигурирования сервиса mapper — mini-HTTP-сервиса, который объединяет (мапит) данные сервисов документации (documentations) и процессов (flows), а также заметок (notes) и Django-бэкенда Sarex.

Способы конфигурирования

Сервис настраивается только через переменные окружения. Разбор выполняется в src/app/config.py через библиотеку pydantic-settings.

В отличие от многих сервисов, здесь нет единого корневого префикса и нет вложенного делимитера (env_nested_delimiter). Вместо этого каждая секция описана отдельным классом BaseSettings со своим env_prefix (задаётся во вложенном классе Config):

Класс env_prefix Секция
Settings — (без префикса) Корневые настройки (API_PREFIX)
LoggerSettings LOG_ Логирование
RedisSettings REDIS_ Кеш Redis
DocumentationSettings DOCUMENTATION_ HTTP-клиент сервиса документации
FlowSettings FLOW_ HTTP-клиент сервиса процессов
DjangoSettings DJANGO_ HTTP-клиент Django-бэкенда
NoteSettings NOTE_ HTTP-клиент сервиса заметок

DocumentationSettings, FlowSettings, DjangoSettings и NoteSettings наследуются от общего класса AsyncSessionManager (поля host, timeout, retries), поэтому у каждого из них одинаковый набор из трёх переменных: <PREFIX>_HOST, <PREFIX>_TIMEOUT, <PREFIX>_RETRIES.

Отдельного конфиг-файла (yaml/toml) у приложения нет. Приложение не загружает .env автоматическиconfig.py не задан env_file, зависимости python-dotenv нет) — переменные нужно экспортировать в окружение самому, напр. set -a && . ./.env && set +a, либо пробрасывать через контейнер/оркестратор.

Источники переменных по способам запуска:

Способ запуска Откуда берутся переменные
Локально (uvicorn/gunicorn) Переменные окружения процесса. Redis поднимается через docker-compose.yaml (только сервис redis)
Контейнер Dockerfileentrypoint.sh: gunicorn -w 4 -k uvicorn.workers.UvicornWorker -b 0.0.0.0:8000 app.main:app
Kubernetes (Helm, из репозитория сервиса) .helm/values.yaml, блок universal-chart.services.backend.envs; деплой из .gitlab-ci.yml
Kubernetes (GitOps, инфра-репозиторий) iac/apps/mapper/*: Kustomize-база base/deployment.yaml (env + секреты Vault) и Flux HelmRelease в overlay'ах brusnika-*

Точки входа:

Команда Назначение
uvicorn main:app / python app/main.py Локальный запуск (в main.py порт 8002, host 0.0.0.0)
gunicorn -w 4 -k uvicorn.workers.UvicornWorker -b 0.0.0.0:8000 app.main:app Прод-запуск (entrypoint.sh, порт 8000)

Стек: Python 3.10 (python:3.10-slim-buster), FastAPI, httpx (асинхронные клиенты), Redis (кеш), PyJWT (разбор токенов).

Переменные приложения

Дефолт означает, что значение обязательно (иначе ошибка старта). Все дефолты ниже соответствуют коду config.py.

App (класс Settings)

Переменная Тип Значение по умолчанию Назначение
API_PREFIX string /api/v1 Префикс маршрутов API

Logger (LOG_*)

Переменная Тип Значение по умолчанию Назначение
LOG_LEVEL string INFO Уровень логирования (имя уровня logging; при неизвестном значении используется INFO)
LOG_FORMAT string [%(asctime)s] [%(levelname)s] [%(filename)s]: %(message)s Формат строки лога

Redis (REDIS_*)

Кеширует JSON-ответы внешних сервисов (по ключу "{user_id}_{url}").

Переменная Тип Значение по умолчанию Назначение
REDIS_USE bool True Включить кеш. При True на старте выполняется PING (падение при недоступном Redis)
REDIS_HOST string localhost Хост Redis
REDIS_PORT int 6379 Порт Redis
REDIS_DB int 0 Номер базы Redis
REDIS_EXPIRE_DAYS int 1 TTL записей кеша в днях (в секундах — expire_days * 24 * 3600)

HTTP-клиенты внешних сервисов

Все четыре клиента наследуют AsyncSessionManager (host, timeout, retries). Клиент httpx создаётся с verify=False (проверка TLS-сертификата отключена) и транспортом с числом ретраев retries. Токены пробрасываются заголовками Authorization (всегда) и Identity (в режиме Zitadel).

Секция / префикс Назначение Дефолт HOST
DOCUMENTATION_* Сервис документации (диски, документы, бандлы) https://stage-api.sarex.io/documentations/api/v1
FLOW_* Сервис процессов (flows, review-данные) https://stage-api.sarex.io/flows/api/v1
DJANGO_* Django-бэкенд Sarex (target-links) https://stage.sarex.io/api
NOTE_* Сервис заметок (notes) https://stage-api.sarex.io/notes/api/v1

Для каждого — три переменные (пример для FLOW):

Переменная Тип Значение по умолчанию Назначение
FLOW_HOST string см. выше Базовый URL сервиса
FLOW_TIMEOUT int 30 Таймаут запроса (сек)
FLOW_RETRIES int 3 Число повторов транспорта httpx

Аутентификация

Аутентификация выполняется в src/app/dependensies.py (get_user_data) на основе заголовков запроса и без проверки подписи токена (jwt.decode(..., options={"verify_signature": False})). Публичный ключ не используется, отдельных переменных для ключа нет.

Условие Режим Как извлекается user_id
Есть заголовки Authorization и Identity zitadel Из payload Identity-токена, поле urn:zitadel:iam:user:metadata.user_id (base64)
Есть только Authorization sarex Из payload основного токена, поле user_id
Заголовков нет 401 Unauthorized

Переменные инфраструктуры и сборки

Не читаются кодом приложения, но участвуют в сборке/запуске.

Переменная Где используется Назначение
SERVICE_NAME .gitlab-ci.yml Имя сервиса (mapper), используется как CHART_NAME
DOCKERFILE_PATH .gitlab-ci.yml Путь к Dockerfile
BUILD_ARGS, CI_TRIGGER_SOURCE .gitlab-ci.yml Аргументы сборки / источник триггера (app)
IMAGE_NAME, CI_COMMIT_SHA, CI_PROJECT_URL, CI_JOB_URL, CI_PROJECT_NAMESPACE .gitlab-ci.yml (HELM_SET_ARGS) Прокидываются в universal-chart (образ, commit, ссылки, owner)

Переменные из Helm-чарта репозитория сервиса (.helm/values.yaml)

Чарт зависит от universal-chart (oci://cr.yandex/.../charts, версия 0.1.7). Переменные приложения задаются в блоке services.backend.envs с разбивкой по окружениям (_default/stage/preprod/production).

Переменная stage preprod production
DOCUMENTATION_HOST https://stage-api.sarex.io/documentations/api/v1 https://api.preprod.sarex.io/documentations/api/v1 https://api.sarex.io/documentations/api/v1
FLOW_HOST https://stage-api.sarex.io/flows/api/v1 https://api.preprod.sarex.io/flows/api/v1 https://api.sarex.io/flows/api/v1
DJANGO_HOST https://stage.sarex.io/api https://preprod.sarex.io/api https://lk.sarex.io/api
NOTE_HOST https://stage-api.sarex.io/notes/api/v1 https://api.preprod.sarex.io/notes/api/v1 https://api.sarex.io/notes/api/v1
REDIS_USE 0 0 0
TIMEOUT 120 120 120

Прочие параметры чарта (не переменные приложения): deployment.* (имя, порт 8000, replicaCount 1/1/3/3, ресурсы, probes.liveness/readinessотключены), image.name (cr.yandex/.../mapper), service.* (ClusterIP, порт 8000), imagePullSecrets (dockerhub), labels.monitoring=prometheus.

Переменные из инфра-репозитория (iac/apps/mapper)

GitOps-деплой через Kustomize + Flux, namespace mapper. Здесь же лежит настоящий документ.

Структура:

Путь Назначение
base/ Базовый Kustomize (namespace, serviceaccount mapper-vault, deployment, service)
yc-k8s-test/ Overlay поверх base (патч replicas: 1)
brusnika-stage/ Flux HelmRelease (universal-chart), хосты test.sarex.brusnika.tech, imagePullSecrets: dockerhub
brusnika-prod/ Flux HelmRelease (universal-chart), хосты cde.brusnika.ru, imagePullSecrets: regcred

Обычные env в base/deployment.yaml (production-хосты Sarex):

Переменная Значение
DOCUMENTATION_HOST https://api.sarex.io/documentations/api/v1
FLOW_HOST https://api.sarex.io/flows/api/v1
DJANGO_HOST https://lk.sarex.io/api
NOTE_HOST https://api.sarex.io/notes/api/v1
REDIS_USE 0
TIMEOUT 120

Секреты монтируются через HashiCorp Vault Agent (аннотации vault.hashicorp.com/*, роль mapper) как файлы в /vault/secrets/*, которые перед стартом экспортируются в окружение (set -a && . /vault/secrets/... && set +a):

Файл секрета Источник (Vault path) Экспортируемые переменные
mapper-django-auth secrets/data/vault/common/django_auth MAPPER_DJANGO_TOKEN
mapper-db secrets/data/postgresql/apps/mapper MAPPER_DB_USER, MAPPER_DB_PASSWORD, MAPPER_DB_HOST, MAPPER_DB_PORT, MAPPER_DB_NAME
mapper-rabbitmq secrets/data/rabbitmq/apps/mapper MAPPER_RABBITMQ_VHOST, MAPPER_RABBITMQ_USERNAME, MAPPER_RABBITMQ_PASSWORD, MAPPER_RABBITMQ_HOST, MAPPER_RABBITMQ_PORT
mapper-s3 secrets/data/minio/apps/mapper MAPPER_S3_ENDPOINT, MAPPER_S3_REGION, MAPPER_S3_BUCKET, MAPPER_S3_ACCESS_KEY_ID, MAPPER_S3_SECRET_ACCESS_KEY
mapper-kafka secrets/data/kafka/apps/mapper MAPPER_KAFKA_BOOTSTRAP_SERVERS, MAPPER_KAFKA_SECURITY_PROTOCOL, MAPPER_KAFKA_SASL_MECHANISM, MAPPER_KAFKA_USERNAME, MAPPER_KAFKA_PASSWORD

Переменные в CI (.gitlab-ci.yml)

Пайплайн подключает шаблоны generic/common-ci (common-security-scan.yaml, universal-pipeline.yaml@apps-business) и переключает окружение по ветке/тегу:

Условие STAND Namespace CHART_VERSION
ветка stage stage platform 0.0.1-stage
ветка master preprod mapper-preprod 0.0.1-preprod
тег (CI_COMMIT_TAG) production mapper-prod 0.0.1-prod
merge request — (сборка образа отключена)

Стадия test: job linter (flake8 src/app, max-line-length=120) и typechecker (mypy src/app с types-redis; disallow_untyped_defs=True).

Замечания и потенциальные проблемы

  • TIMEOUT не читается приложением. В Helm/Kustomize задаётся TIMEOUT=120, но клиенты читают DOCUMENTATION_TIMEOUT/FLOW_TIMEOUT/DJANGO_TIMEOUT/NOTE_TIMEOUT (каждый со своим префиксом). Без префикса переменная игнорируется — реальный таймаут остаётся 30. Чтобы поднять таймаут, задавайте <PREFIX>_TIMEOUT.
  • Секреты Vault не используются кодом. MAPPER_DB_*, MAPPER_RABBITMQ_*, MAPPER_S3_*, MAPPER_KAFKA_*, MAPPER_DJANGO_TOKEN монтируются и экспортируются в окружение (base/deployment.yaml), но текущая версия приложения ни PostgreSQL, ни RabbitMQ, ни S3, ни Kafka, ни MAPPER_DJANGO_TOKEN не читаетconfig.py таких настроек нет). Похоже, инфраструктура заготовлена наперёд либо унаследована из шаблона.
  • Кеш отключён во всех окружениях (REDIS_USE=0). При этом в get_response (utils.py) при не-200 ответе апстрима и выключенном кеше возвращается None, а роутер отдаёт 400 Bad Request. То есть при выключенном Redis запасного кеша нет.
  • Подпись JWT не проверяется (verify_signature=False) ни в режиме zitadel, ни в sarex. Доверие к токену — на сетевом слое (Istio/ingress). Публичный ключ не настраивается.
  • TLS-проверка апстримов отключена (httpx.AsyncClient(verify=False)) для всех четырёх клиентов.
  • Нет healthcheck-эндпоинта. Пробы liveness/readiness в чарте выключены — это согласовано.
  • Порты различаются: локально main.py слушает 8002, в контейнере gunicorn — 8000 (проброшен в k8s Service).
  • docker-compose.yaml поднимает только Redis (redis-stack-server); само приложение в compose не описано.

Минимальный набор для локального запуска

Поднять Redis (docker compose up redis) либо задать REDIS_USE=False, затем uvicorn main:app из src. Минимально стоит задать (у остальных есть рабочие дефолты для stage):

  • REDIS_USE (False, если Redis не поднят) и при необходимости REDIS_HOST/REDIS_PORT
  • при работе против нестандартных стендов — DOCUMENTATION_HOST, FLOW_HOST, DJANGO_HOST, NOTE_HOST
  • LOG_LEVEL (по желанию)

Готовые значения-примеры приведены в .env.example.