16 KiB
Конфигурация проекта 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) |
| Контейнер | Dockerfile → entrypoint.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.