# Конфигурация проекта eav-python Документ описывает все переменные окружения и способы конфигурирования сервиса. ## Способы конфигурирования Сервис — это Django-приложение (**Django 4.1 + Django REST Framework**), запускаемое как WSGI (`config.wsgi`) через **uWSGI** (порт `8000`, см. `compose/eav-backend/uwsgi.ini`). Настройки читаются из переменных окружения в `src/config/settings/base.py` и `src/config/settings/production.py`. Разбор выполняется частично через библиотеку [`django-environ`](https://django-environ.readthedocs.io/) (объект `env = environ.Env()`), частично напрямую через `os.getenv`. Особенности разбора: - **префикса/делимитера у секций нет** — каждая настройка задаётся плоской переменной окружения (напр. `DJANGO_POSTGRES_HOST`, `KAFKA_HOST`, `YC_S3_BUCKET_NAME`); - **`.env` не загружается автоматически** — в коде нет вызова `environ.Env.read_env()` / `load_dotenv`, хотя `python-dotenv` присутствует в зависимостях. Переменные нужно экспортировать в окружение самому (напр. `set -a && . ./.env && set +a`) либо задавать через `--env`/манифесты k8s. Файл `.env` при этом в `.gitignore`; - **выбор набора настроек** задаётся `DJANGO_SETTINGS_MODULE`: `config.settings.production` (боевой набор с БД, CORS, JWT), `config.settings.test` (только `base`), либо `config.settings.local` (по умолчанию в `manage.py`, в репозитории отсутствует, `.gitignore`); - **часть переменных читается только в `production.py`** — БД (`DJANGO_POSTGRES_*`) и JWT (`JWT_PRIVATE_KEY`/`JWT_PUBLIC_KEY`); в `base.py`/`test.py` их нет. Отдельного конфиг-файла (yaml/toml) у приложения нет. Источники переменных по способам запуска: | Способ запуска | Откуда берутся переменные | | --- | --- | | Локально (manage.py / uWSGI) | Переменные окружения процесса (`.env` нужно экспортировать вручную) | | Локально (docker-compose) | `docker-compose.yml`: блок `environment` сервиса `backend` + образ `postgres` (timescaledb-postgis) | | Kubernetes (Helm, репозиторий приложения) | `.helm/values-.yaml`: блоки `backend.deployment.envs` (обычные значения) и `backend.deployment.secrets` (из k8s-секретов через `secretKeyRef`); шаблон `templates/server.yaml`, роутинг — `templates/mesh-config.yaml` (Istio VirtualService) | | Kubernetes (infra, Flux/Kustomize) | `infra/iac/apps/eav/base/helmrelease.yaml` (universal-chart): секреты инъектируются Vault-агентом (`vault.hashicorp.com/agent-inject-*`) и экспортируются в окружение в `args`; настройки `production.py` монтируются из `django-configmap` | | CI/CD (GitLab) | `.gitlab-ci.yml`: общие шаблоны `generic/common-ci`, переменные пайплайна в `workflow.rules` | Способы запуска процессов: | Процесс | Точка входа | Назначение | | --- | --- | --- | | HTTP API | `compose/eav-backend/entrypoint.sh` → `uwsgi --ini uwsgi.ini` (`config.wsgi`, порт 8000) | REST API | | Миграции | `entrypoint.sh` → `python3 manage.py migrate` (выполняется перед стартом uWSGI) | Миграции БД | | Kafka-продюсер | `config/kafka.py` (инициализируется при импорте, если `KAFKA_ENABLED`) | Публикация событий EAV в топики | Порядок запуска в контейнере (`entrypoint.sh`): сначала `manage.py migrate`, затем `uwsgi` (оба под `opentelemetry-instrument`). ## Переменные приложения Дефолт `—` означает, что значения по умолчанию в коде нет. ### Django / приложение | Переменная | Тип | Значение по умолчанию | Назначение | | --- | --- | --- | --- | | `DJANGO_SETTINGS_MODULE` | string | `config.settings.local` (в `manage.py`); в контейнере — `config.settings.production` | Какой набор настроек Django загружать | | `DJANGO_DEBUG` | bool | `False` | Режим отладки Django (в `production.py` жёстко `False`) | | `DJANGO_SECRET_KEY` | string | (захардкоженный дефолт) | Секретный ключ Django. В проде обязателен свой | | `SERVICE_NAME` | string | `eav` | Имя сервиса (в `base.py`); в OTEL-секции дефолт `eav.eav-backend` | | `VERSION` | string | `1.0.0` | Версия приложения | ### Database — PostgreSQL (только `config.settings.production`) | Переменная | Тип | Значение по умолчанию | Назначение | | --- | --- | --- | --- | | `DJANGO_POSTGRES_HOST` | string | — | Хост PostgreSQL | | `DJANGO_POSTGRES_PORT` | int | `6432` | Порт PostgreSQL (в infra-манифесте — `5432`) | | `DJANGO_POSTGRES_DATABASE` | string | — | Имя базы данных | | `DJANGO_POSTGRES_USER` | string | — | Пользователь БД | | `DJANGO_POSTGRES_PASSWORD` | string | — | Пароль пользователя БД | > Engine — `django.db.backends.postgresql`. В `docker-compose.yml` поднимается `timescale/timescaledb-postgis` (проекту нужны расширения PostGIS/ltree). ### Auth / JWT (только `config.settings.production`) Используются два механизма аутентификации (`REST_FRAMEWORK.DEFAULT_AUTHENTICATION_CLASSES`): `ZitadelJWTAuthentication` (заголовок `Identity`) и `rest_framework_simplejwt` (RS512). | Переменная | Тип | Значение по умолчанию | Назначение | | --- | --- | --- | --- | | `SIMPLE_JWT_ISSUER` | string | `django` | Значение claim `iss` (проверяется при верификации токена) | | `JWT_PRIVATE_KEY` | string (PEM) | — | Приватный RSA-ключ (подпись). Экранированные `\n` заменяются на переводы строк. Обязателен | | `JWT_PUBLIC_KEY` | string (PEM) | — | Публичный RSA-ключ (проверка). Экранированные `\n` заменяются на переводы строк. Обязателен | > `SIMPLE_JWT`: алгоритм `RS512`, `ACCESS_TOKEN_LIFETIME` 5 мин, `REFRESH_TOKEN_LIFETIME` 1 день, тип заголовка `Bearer`, claim пользователя — `user_id`. ### S3 — Yandex Object Storage (`base.py`, boto3/django-storages) | Переменная | Тип | Значение по умолчанию | Назначение | | --- | --- | --- | --- | | `YC_S3_ACCESS_KEY_ID` | string | `None` | Access key | | `YC_S3_SECRET_ACCESS_KEY` | string | `None` | Secret key | | `YC_S3_BUCKET_NAME` | string | `None` | Бакет по умолчанию | | `YC_S3_ENDPOINT_URL` | string | `None` | Эндпоинт S3 | > `DEFAULT_FILE_STORAGE`/`STATICFILES_STORAGE` — `storages.backends.s3boto3.S3Boto3Storage`, `AWS_DEFAULT_ACL=public-read`. ### Kafka (`base.py`, `config/kafka.py`) | Переменная | Тип | Значение по умолчанию | Назначение | | --- | --- | --- | --- | | `KAFKA_ENABLED` | bool | `True` | Включить реального продюсера (иначе `MockProducer` — события не отправляются) | | `KAFKA_HOST` | string | `""` | Адрес брокера (`bootstrap_servers`) | | `KAFKA_USERNAME` | string | `platform` | Пользователь SASL | | `KAFKA_PASSWORD` | string | `""` | Пароль SASL | | `KAFKA_SSL_CAFILE` | string | `/usr/local/share/ca-certificates/Yandex/YandexInternalRootCA.crt` | CA-сертификат для TLS | | `SASL_MECHANISM` | string | `SCRAM-SHA-512` | Механизм SASL | | `SECURITY_PROTOCOL` | string | `SASL_SSL` | Протокол безопасности Kafka | | `ASSETS_TOPIC` | string | `assets_broadcast_test` | Топик рассылки по ассетам | Топики событий EAV: | Переменная | Значение по умолчанию | | --- | --- | | `KAFKA_TOPIC_ATTRIBUTE_CREATED` | `eav.attribute.created.v1` | | `KAFKA_TOPIC_ATTRIBUTE_UPDATED` | `eav.attribute.updated.v1` | | `KAFKA_TOPIC_ATTRIBUTE_DELETED` | `eav.attribute.deleted.v1` | | `KAFKA_TOPIC_VALUE_OPTION_CREATED` | `eav.value_option.created.v1` | | `KAFKA_TOPIC_VALUE_OPTION_DELETED` | `eav.value_option.deleted.v1` | ### OpenTelemetry (`base.py`) Блок трейсинга активируется, только если задана переменная `USE_OTEL` (проверяется через `os.getenv('USE_OTEL', False)` — истинно при любом непустом значении). Используется `django-otel-tools`; при включении в начало `MIDDLEWARE` добавляется `OtelMiddleware`. | Переменная | Тип | Значение по умолчанию | Назначение | | --- | --- | --- | --- | | `USE_OTEL` | bool/string | `False` | Включить трейсинг и OTEL-логгер | | `SERVICE_NAME` | string | `eav.eav-backend` | Имя сервиса в трейсах | | `TRACER_ENDPOINT` | string | `localhost:4375` | Адрес OTLP-коллектора | | `USE_INSECURE` | bool/string | `False` | Небезопасное (без TLS) подключение к коллектору | | `ENVIRONMENT` | string | `prod` | Атрибут ресурса `environment` | | `MODULE` | string | `eav` | Атрибут ресурса `module` | | `TEAM` | string | `platform_team` | Атрибут ресурса `team` | | `COMPONENT` | string | `backend` | Атрибут ресурса `component` | ## Переменные из Helm-чарта (`.helm/values-.yaml`) Обычные значения задаются в блоке `backend.deployment.envs` для каждого окружения (`stage`/`preprod`/`production`): `DJANGO_SETTINGS_MODULE`, `USE_OTEL`, `SERVICE_NAME`, `TRACER_ENDPOINT`, `USE_INSECURE`, `ENVIRONMENT`, `KAFKA_HOST`, `ASSETS_TOPIC` (различаются адресами коллектора/брокера и именами топиков). Значения из секретов (блок `secrets`, монтируются как env через `secretKeyRef`): | Переменная | Секрет (`secret_name`) | Ключ (`secret_key`) | | --- | --- | --- | | `DJANGO_POSTGRES_HOST` | `yc-pg-secret` | `host` | | `DJANGO_POSTGRES_DATABASE` | `yc-pg-secret` | `database` | | `DJANGO_POSTGRES_PORT` | `yc-pg-secret` | `port` (только preprod) | | `DJANGO_POSTGRES_USER` | `yc-pg-secret` | `user` | | `DJANGO_POSTGRES_PASSWORD` | `yc-pg-secret` | `password` | | `DJANGO_CLICKHOUSE_HOST` | `yc-ch-secret` | `host` | | `DJANGO_CLICKHOUSE_DATABASE` | `yc-ch-secret` | `database` | | `DJANGO_CLICKHOUSE_USER` | `yc-ch-secret` | `user` | | `DJANGO_CLICKHOUSE_PASSWORD` | `yc-ch-secret` | `password` | | `YC_S3_ACCESS_KEY_ID` | `yc-s3-secret` | `key_id` | | `YC_S3_SECRET_ACCESS_KEY` | `yc-s3-secret` | `access_key` | | `YC_S3_BUCKET_NAME` | `yc-s3-secret` | `storage_bucket_name` | | `YC_S3_ENDPOINT_URL` | `yc-s3-secret` | `endpoint_url` | | `JWT_PRIVATE_KEY` | `jwt-secret` | `private_key` | | `JWT_PUBLIC_KEY` | `jwt-secret` | `public_key` | | `KAFKA_USERNAME` | `kafka-secret` / `yc-kafka-secret` | `username` | | `KAFKA_PASSWORD` | `kafka-secret` / `yc-kafka-secret` | `password` | | `KAFKA_HOST` | `yc-kafka-secret` | `host` (prod/stage) | Помимо env, чарт монтирует CA-сертификаты: PostgreSQL (`yc-pg-certificate` → `~/.postgresql/root.crt`) и Yandex Internal Root CA (`yc-ch-certificate` → `/usr/local/share/ca-certificates/Yandex/YandexInternalRootCA.crt`, тот же путь, что в `KAFKA_SSL_CAFILE`), а также конфиг clickhouse-client. ## Переменные в infra-манифесте (Flux/Kustomize, `infra/iac/apps/eav`) В отличие от Helm-чарта приложения, боевой деплой Sarex использует Vault-инъекцию (`base/helmrelease.yaml`, чарт `universal-chart`). Секреты рендерятся Vault-агентом в файлы `/vault/secrets/*` и экспортируются в окружение в `args` контейнера перед запуском `entrypoint.sh`: | Переменная(ые) | Источник (Vault path) | | --- | --- | | `DJANGO_POSTGRES_HOST/PORT/DATABASE/USER/PASSWORD` | `secrets/data/postgresql/apps/eav` | | `YC_S3_ENDPOINT_URL/BUCKET_NAME/ACCESS_KEY_ID/SECRET_ACCESS_KEY` | `secrets/data/minio/apps/eav` | | `JWT_PRIVATE_KEY` / `JWT_PUBLIC_KEY` | `secrets/data/vault/common/rsa_keys` | Прямо в `env` деплоймента задаются `KAFKA_ENABLED=False`, `ASSETS_TOPIC=sarex`, `DJANGO_SETTINGS_MODULE=config.settings.production`. Файл `production.py` монтируется из `django-configmap` (переопределяет `production.py` из образа; в нём `DEBUG=True`, `ALLOWED_HOSTS=['*']`, свои CORS/CSRF-домены и имена cookie `eav-sessionid`/`eav-csrftoken`). ## Замечания и потенциальные проблемы - Приложение **не загружает `.env` автоматически** (нет `read_env`/`load_dotenv`). `python-dotenv` установлен, но не используется в настройках — переменные нужно экспортировать в окружение самому. - Переменные БД (`DJANGO_POSTGRES_*`) и JWT (`JWT_PRIVATE_KEY`/`JWT_PUBLIC_KEY`) читаются **только** в `config.settings.production`. При `test`/`base` их отсутствие не мешает старту, но БД по умолчанию не сконфигурирована. - `JWT_PRIVATE_KEY`/`JWT_PUBLIC_KEY` в `production.py` читаются через `env.str(...)` **без дефолта** — их отсутствие приводит к ошибке старта. В infra-варианте (`django-configmap`) используется `get_env_variable` с тем же требованием. - `DJANGO_CLICKHOUSE_*` присутствуют в Helm-секретах, но **кодом приложения не читаются** (в текущих настройках ClickHouse не используется) — это подготовка/наследие инфраструктуры. - `KAFKA_ENABLED`: при ложном значении используется `MockProducer` — события EAV в Kafka не публикуются (так сделано в infra-деплое: `KAFKA_ENABLED=False`). Значение разбирается `django-environ` как bool. - Флаги OTEL (`USE_OTEL`, `USE_INSECURE`) читаются через `os.getenv(..., False)` и трактуются как истинные при **любой непустой строке**, включая `"False"`. Чтобы отключить — переменную нужно не задавать вовсе. - `SERVICE_NAME` определяется дважды: как имя приложения (`base.py`, дефолт `eav`) и как имя сервиса в OTEL (дефолт `eav.eav-backend`) — фактически одна и та же переменная окружения. - В `docker-compose.yml` захардкожен пароль БД (`zealot096`) — только для локального окружения. ## Минимальный набор для локального запуска (`config.settings.production`) - `DJANGO_SETTINGS_MODULE=config.settings.production` - `DJANGO_POSTGRES_HOST`, `DJANGO_POSTGRES_PORT`, `DJANGO_POSTGRES_DATABASE`, `DJANGO_POSTGRES_USER`, `DJANGO_POSTGRES_PASSWORD` - `JWT_PRIVATE_KEY`, `JWT_PUBLIC_KEY` (обязательны; можно тестовую RSA-пару) - `KAFKA_ENABLED=False` (чтобы не поднимать брокер) либо `KAFKA_HOST`/`KAFKA_USERNAME`/`KAFKA_PASSWORD` - при работе с файлами: `YC_S3_ACCESS_KEY_ID`, `YC_S3_SECRET_ACCESS_KEY`, `YC_S3_BUCKET_NAME`, `YC_S3_ENDPOINT_URL` - `USE_OTEL` — не задавать (иначе включится трейсинг) Готовые значения-примеры для всех переменных приведены в `.env.example`.