157 lines
16 KiB
Markdown
157 lines
16 KiB
Markdown
# Конфигурация проекта dps-message-hub
|
||
# Версия: 0.1.0
|
||
|
||
Документ описывает все переменные окружения и способы конфигурирования сервиса.
|
||
|
||
`dps-message-hub` (`dps_message_hub`) — это Kafka-воркер на базе [FastStream](https://faststream.airt.ai/), потребляющий сообщения об изменении ассетов и обновляющий данные разметки (`markup_event`) в PostgreSQL домена «documentations». HTTP API у сервиса нет.
|
||
|
||
> Это отдельный сервис, не путать с приложением `message-hub` (домен `planning`): у них разные префиксы переменных, набор интеграций и назначение.
|
||
|
||
## Способы конфигурирования
|
||
|
||
Сервис настраивается **только через переменные окружения**. Разбор выполняется в `src/dps_message_hub/infra/config.py` через библиотеку [`pydantic-settings`](https://docs.pydantic.dev/latest/concepts/pydantic_settings/) (класс `AppSettings(BaseSettings)`).
|
||
|
||
Особенности разбора (`SettingsConfigDict`):
|
||
|
||
- `env_prefix="DPS_MESSAGE_HUB_"` — все переменные приложения начинаются с этого префикса;
|
||
- `env_nested_delimiter="__"` — вложенные секции задаются двойным подчёркиванием, напр. `DPS_MESSAGE_HUB_DOCUMENTATION_DB__HOST` → `documentation_db.host`;
|
||
- `env_ignore_empty=False` — пустая строка считается заданным значением (не игнорируется);
|
||
- `frozen=True` — объект настроек неизменяем после инициализации.
|
||
|
||
Настройки разбиты на три вложенные секции (модели `pydantic.BaseModel`), читаемые одним классом `AppSettings`:
|
||
|
||
- `app` (`App`) — префикс `DPS_MESSAGE_HUB_APP__`;
|
||
- `documentation_db` (`Database`) — префикс `DPS_MESSAGE_HUB_DOCUMENTATION_DB__`;
|
||
- `kafka` (`Kafka`) — префикс `DPS_MESSAGE_HUB_KAFKA__`.
|
||
|
||
Класс `Settings` (`src/dps_message_hub/infra/config.py`) — синглтон (`wiring.SingletonMeta`) поверх `AppSettings`, отдаёт настройки через свойство `.settings`.
|
||
|
||
Отдельного конфиг-файла (yaml/toml) у приложения нет. У классов настроек **не задан** `env_file`, поэтому файл `.env` автоматически не загружается — переменные нужно экспортировать в окружение процесса самостоятельно (`make config` лишь копирует `.example.env` → `.env` как шаблон), либо пробрасывать их в контейнер через `--env-file` (см. `Makefile`, цель `container-run`).
|
||
|
||
Источники переменных по способам запуска:
|
||
|
||
| Способ запуска | Откуда берутся переменные |
|
||
| --- | --- |
|
||
| Локально (бинарник) | Переменные окружения процесса. `make config` копирует `.example.env` → `.env`, но приложение **не загружает `.env` автоматически** — экспортируйте сами, напр. `set -a && . ./.env && set +a` |
|
||
| Локально (контейнер) | `Makefile`: цель `container-run` пробрасывает переменные через `--env-file .env` |
|
||
| Kubernetes (Helm) | `.helm/values.yaml`: блок `universal-chart.services.api.envs` (обычные значения) и `secretEnvs` (значения из k8s-секретов); базовый чарт — `universal-chart` (`oci://.../charts`, версия `0.1.7`) |
|
||
| CI/CD (GitLab) | `.gitlab-ci.yml`: переменные пайплайна (`workflow.rules`) и `HELM_SET_ARGS` |
|
||
|
||
Запуск процесса (`scripts/entrypoint.sh`): единый FastStream-процесс с брокером Kafka:
|
||
|
||
```
|
||
faststream run \
|
||
--factory \
|
||
--workers ${DPS_MESSAGE_HUB_NUM_WORKERS} \
|
||
'dps_message_hub.infra.app:get_app'
|
||
```
|
||
|
||
Фабрика `dps_message_hub.infra.app:get_app` (`src/dps_message_hub/infra/app.py`) собирает `FastStream`-приложение: создаёт `KafkaBroker`, подключает роутер-потребитель топика `assets` и открывает пул соединений PostgreSQL в lifespan. Отдельных точек входа для воркеров/крон-задач нет — `pyproject.toml` не содержит `[project.scripts]`.
|
||
|
||
## Переменные приложения
|
||
|
||
Дефолт `—` означает, что значение обязательно (иначе ошибка старта настроек).
|
||
|
||
### Приложение (`DPS_MESSAGE_HUB_APP__*`) — класс `App`
|
||
|
||
| Переменная | Тип | Значение по умолчанию | Назначение |
|
||
| --- | --- | --- | --- |
|
||
| `DPS_MESSAGE_HUB_APP__LOG_LEVEL` | enum (`LogLevel`) | `INFO` | Уровень логирования. Допустимо: `CRITICAL`/`FATAL`/`ERROR`/`WARNING`/`WARN`/`INFO`/`DEBUG`/`NOTSET` |
|
||
| `DPS_MESSAGE_HUB_APP__IS_DEV` | bool | `False` | Признак dev-режима. Поле объявлено в настройках, но в текущем коде не используется |
|
||
| `DPS_MESSAGE_HUB_APP__BROKER_TYPE` | enum (`BrokerType`) | `—` (обязательно) | Тип брокера сообщений. Поддерживается только значение `kafka` |
|
||
|
||
### База данных PostgreSQL (`DPS_MESSAGE_HUB_DOCUMENTATION_DB__*`) — класс `Database`
|
||
|
||
| Переменная | Тип | Значение по умолчанию | Назначение |
|
||
| --- | --- | --- | --- |
|
||
| `DPS_MESSAGE_HUB_DOCUMENTATION_DB__HOST` | string | `—` | Хост PostgreSQL |
|
||
| `DPS_MESSAGE_HUB_DOCUMENTATION_DB__PORT` | int | `—` | Порт PostgreSQL |
|
||
| `DPS_MESSAGE_HUB_DOCUMENTATION_DB__USER` | string | `—` | Пользователь БД |
|
||
| `DPS_MESSAGE_HUB_DOCUMENTATION_DB__PASSWORD` | string | `—` | Пароль пользователя БД |
|
||
| `DPS_MESSAGE_HUB_DOCUMENTATION_DB__NAME` | string | `—` | Имя базы данных |
|
||
| `DPS_MESSAGE_HUB_DOCUMENTATION_DB__ENABLE_SSL` | bool | `—` | Включить TLS-подключение к БД |
|
||
| `DPS_MESSAGE_HUB_DOCUMENTATION_DB__SSL_MODE` | enum (`verify-full`/`verify-ca`/`""`) | `—` | Режим проверки TLS (параметр `sslmode` DSN). При `ENABLE_SSL=true` не должно быть пустым |
|
||
| `DPS_MESSAGE_HUB_DOCUMENTATION_DB__SSL_ROOT_CERT_PATH` | string | `—` | Путь к CA-сертификату (параметр `sslrootcert` DSN). При `ENABLE_SSL=true` не должно быть пустым |
|
||
|
||
Валидатор `check_ssl_options_configured_when_ssl_enabled`: если `ENABLE_SSL=true`, то `SSL_MODE` и `SSL_ROOT_CERT_PATH` обязаны быть непустыми, иначе ошибка старта. Итоговый DSN собирается в вычисляемом поле `documentation_db.uri` (`postgresql://user:password@host:port/name`, при SSL добавляются `?sslmode=...&sslrootcert=...`).
|
||
|
||
### Kafka (`DPS_MESSAGE_HUB_KAFKA__*`) — класс `Kafka`
|
||
|
||
| Переменная | Тип | Значение по умолчанию | Назначение |
|
||
| --- | --- | --- | --- |
|
||
| `DPS_MESSAGE_HUB_KAFKA__HOST` | string | `—` | Хост брокера Kafka |
|
||
| `DPS_MESSAGE_HUB_KAFKA__PORT` | int | `—` | Порт брокера Kafka |
|
||
| `DPS_MESSAGE_HUB_KAFKA__USERNAME` | string | `—` | Логин SASL (`SCRAM-SHA-512`) |
|
||
| `DPS_MESSAGE_HUB_KAFKA__PASSWORD` | string | `—` | Пароль SASL (`SCRAM-SHA-512`) |
|
||
| `DPS_MESSAGE_HUB_KAFKA__SSL_CAFILE` | string \| null | `None` | Путь к CA-сертификату для SSL-контекста. Если задан — используется `SASL_SSL`, иначе SASL без TLS |
|
||
| `DPS_MESSAGE_HUB_KAFKA__TOPICS` | dict (JSON) → `KafkaTopics` | `—` | Соответствие логического топика реальному имени в Kafka. Обязателен ключ `assets`, напр. `{"assets": "assets_broadcast"}` |
|
||
|
||
Формирование параметров подключения (`src/dps_message_hub/infra/app.py`): адрес брокера — вычисляемое поле `kafka.uri` (`host:port`). Безопасность через `faststream.security.SASLScram512`:
|
||
|
||
- если заданы `USERNAME` и `PASSWORD` и `SSL_CAFILE` пуст — `SASLScram512(..., use_ssl=False)`;
|
||
- если задан `SSL_CAFILE` — `SASLScram512(..., ssl_context=create_ssl_context(cafile=...))`.
|
||
|
||
## Переменные инфраструктуры, сборки и запуска
|
||
|
||
Не читаются классами настроек приложения, но участвуют в запуске/сборке/деплое.
|
||
|
||
| Переменная | Где используется | Назначение |
|
||
| --- | --- | --- |
|
||
| `DPS_MESSAGE_HUB_NUM_WORKERS` | `scripts/entrypoint.sh`, Helm `envs` | Число воркеров FastStream (`faststream run --workers`). В Helm `_default: '2'` |
|
||
| `DPS_MESSAGE_HUB_PYTHON_IMAGE_NAME` | `Dockerfile` (ARG) | Базовый образ Python (по умолчанию `python`) |
|
||
| `DPS_MESSAGE_HUB_PYTHON_IMAGE_TAG` | `Dockerfile` (ARG) | Тег базового образа (по умолчанию `3.13-slim`) |
|
||
| `PIP_INDEX_URL`, `PIP_TRUSTED_HOST` | `Dockerfile` (ARG) | Индекс/доверенный хост pip при сборке |
|
||
|
||
Сборка (`Dockerfile`): многостадийная — стадия `builder` собирает wheel'ы из `requirements/requirements.txt`, стадия `runner` ставит их, копирует `src/` и устанавливает пакет (`pip install . --no-deps`); процесс запускается непривилегированным пользователем `dps_message_hub`. Целевая версия Python — `3.13` (`.python-version`, `requires-python >=3.13`).
|
||
|
||
## Переменные из Helm-чарта (`.helm/values.yaml`)
|
||
|
||
Сервис деплоится через зависимость `universal-chart` (`.helm/Chart.yaml`, версия `0.1.7`). Настраивается один сервис — `services.api` (тип нагрузки — deployment, `replicaCount._default: 1`). HTTP-`service` и `ingress` отключены, health-пробы (`liveness`/`readiness`, путь `/ping`) — `enabled: false` (у сервиса нет HTTP-эндпоинтов).
|
||
|
||
Обычные значения (блок `services.api.envs`) различаются по окружениям (`_default`/`stage`/`preprod`/`production`) адресами БД и Kafka, именем БД и именами топиков. В Helm дополнительно заданы (отсутствуют в `.example.env`): `DPS_MESSAGE_HUB_KAFKA__HOST`, `DPS_MESSAGE_HUB_KAFKA__PORT` (`9091`), `DPS_MESSAGE_HUB_KAFKA__SSL_CAFILE` (`/opt/config/ca.crt`), `DPS_MESSAGE_HUB_DOCUMENTATION_DB__ENABLE_SSL=true`, `SSL_MODE=verify-full`, `SSL_ROOT_CERT_PATH=/opt/config/ca.crt`.
|
||
|
||
Значения из секретов (блок `secretEnvs`, монтируются как env через `secretKeyRef`):
|
||
|
||
| Переменная | Секрет (`_default` / `stage`) | Ключ (`secretKey`) |
|
||
| --- | --- | --- |
|
||
| `DPS_MESSAGE_HUB_DOCUMENTATION_DB__USER` | `ya-pg-secret` / `documentations-postgresql-secret` | `username` |
|
||
| `DPS_MESSAGE_HUB_DOCUMENTATION_DB__PASSWORD` | `ya-pg-secret` / `documentations-postgresql-secret` | `password` |
|
||
| `DPS_MESSAGE_HUB_KAFKA__USERNAME` | `kafka-secret` | `username` |
|
||
| `DPS_MESSAGE_HUB_KAFKA__PASSWORD` | `kafka-secret` | `password` |
|
||
|
||
Помимо env, чарт монтирует CA-сертификат Яндекса из `ConfigMap` `ya-ca-cert` (ключ `ca.crt`) как файл `/opt/config/ca.crt` (том `cm-ya-ca-cert`, `readOnly`) — на него указывают `DPS_MESSAGE_HUB_KAFKA__SSL_CAFILE` и `DPS_MESSAGE_HUB_DOCUMENTATION_DB__SSL_ROOT_CERT_PATH`.
|
||
|
||
## Переменные в CI (`.gitlab-ci.yml`)
|
||
|
||
Пайплайн подключает общие шаблоны из `generic/common-ci` (`common-security-scan.yaml`, `universal-pipeline.yaml`, ref `apps-business`) и переключает окружение по ветке/тегу через `workflow.rules`:
|
||
|
||
| Условие | STAND | NAMESPACE | CHART_VERSION | K8S_HUSTLER_BRANCH |
|
||
| --- | --- | --- | --- | --- |
|
||
| MR (`merge_request_event`) | — | — | — | сборка образа отключена (`ENABLE_BUILD_IMAGE=false`) |
|
||
| ветка `stage` | `stage` | `documentations` | `0.0.1-stage` | `stage` |
|
||
| ветка `master` | `preprod` | `documentations-preprod` | `0.0.1-preprod` | `preprod` |
|
||
| тег (`CI_COMMIT_TAG`) | `prod` | `documentations-prod` | `0.0.1-prod` | `master` |
|
||
|
||
Ключевые переменные пайплайна: `SERVICE_NAME=dps-message-hub`, `DOCKERFILE_PATH=Dockerfile`, `CI_TRIGGER_SOURCE=app`, `RELEASE_NAME=dps-message-hub`, `CHART_NAME=dps-message-hub`, флаги `ENABLE_BUILD_CHART`/`ENABLE_BUILD_IMAGE`/`ENABLE_DEPLOY`, `HELM_SET_ARGS` (`--set universal-chart...`). Отдельные job'ы `format` (`ruff format --diff`) и `lint` (`ruff check`) на образе `python:3.13-slim`.
|
||
|
||
## Замечания и потенциальные проблемы
|
||
|
||
- Приложение **не читает `.env`** автоматически (в `SettingsConfigDict` нет `env_file`). `make config` только создаёт `.env` из шаблона — переменные нужно экспортировать самому либо передавать контейнеру через `--env-file`.
|
||
- Большинство полей БД и Kafka **обязательны** (без дефолтов): при пустом окружении настройки не пройдут валидацию и сервис не стартует. Единственные необязательные — `APP__LOG_LEVEL`, `APP__IS_DEV`, `KAFKA__SSL_CAFILE`.
|
||
- `DPS_MESSAGE_HUB_APP__BROKER_TYPE` обязателен; поддерживается только `kafka` (иных веток в `match` нет). При другом значении брокер не будет создан.
|
||
- При `ENABLE_SSL=true` обязательно задавать `SSL_MODE` и `SSL_ROOT_CERT_PATH`, иначе валидатор настроек прервёт старт.
|
||
- `DPS_MESSAGE_HUB_KAFKA__TOPICS` обязан содержать ключ `assets` (модель `KafkaTopics`); прочие ключи игнорируются, отсутствие `assets` — ошибка старта.
|
||
- Поле `APP__IS_DEV` присутствует в настройках, но в текущем коде нигде не задействовано.
|
||
- Обработка Kafka-сообщений идёт с `auto_commit=False` и middleware `Retry` (`src/dps_message_hub/interface/middleware.py`), которая повторяет обработку **бесконечно** с экспоненциальной задержкой (до `1<<10 = 1024` сек) — «отравленное» сообщение может заблокировать партицию.
|
||
|
||
## Минимальный набор для локального запуска
|
||
|
||
Kafka и Zookeeper поднимаются через `Makefile` (цели `run-deps`/`run-zookeeper`/`run-kafka`); PostgreSQL — внешний. Минимально нужно задать:
|
||
|
||
- `DPS_MESSAGE_HUB_APP__BROKER_TYPE=kafka`;
|
||
- `DPS_MESSAGE_HUB_DOCUMENTATION_DB__HOST`, `__PORT`, `__USER`, `__PASSWORD`, `__NAME`, `__ENABLE_SSL` (для локали `false`), `__SSL_MODE`, `__SSL_ROOT_CERT_PATH` (при `ENABLE_SSL=false` можно пустыми);
|
||
- `DPS_MESSAGE_HUB_KAFKA__HOST`, `__PORT`, `__USERNAME`, `__PASSWORD`, `__TOPICS` (JSON с ключом `assets`);
|
||
- `DPS_MESSAGE_HUB_NUM_WORKERS` (для `entrypoint.sh`).
|
||
|
||
Локальный запуск: `make run` (через `entrypoint.sh`) либо `make run-dev` (`faststream run --factory --reload dps_message_hub.infra.app:get_app`). Готовые значения-примеры приведены в `dps-message-hub.env.example` рядом с этим документом.
|