iac/apps/subscriptions/CONFIGURATION.md

20 KiB
Raw Blame History

Конфигурация sarex-subscriptions (api + cron-задачи)

Документ описывает все переменные окружения и способы конфигурирования сервиса sarex-subscriptions — Django-приложения рассылки уведомлений (email/Telegram) по подпискам.

Компоненты сервиса используют один и тот же модуль настроек config.settings.production и общий набор переменных окружения:

  • api — HTTP-сервис (uwsgi, config.wsgi:application), REST API подписок/получателей/шаблонов; отдаёт статику и медиа через S3;
  • cron run_notifications (subscription-periodic) — периодическая рассылка отложенных уведомлений;
  • cron run_immediately_notifications (subscription-immediately) — рассылка немедленных уведомлений.

Обе cron-задачи — это management-команды Django (server/apps/notification/management/commands), запускаемые как отдельные CronJob из того же образа.

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

Сервис настраивается через переменные окружения (os.getenv в config/settings/base.py и config/settings/production.py) плюс жёстко заданные в коде настройки. Отдельной библиотеки разбора конфига (как cleanenv в Go-сервисах) здесь нет — используется штатный механизм Django settings.

Часть настроек захардкожена в base.py и не выносится в окружение: SECRET_KEY, DEBUG, ALLOWED_HOSTS, CORS_*, REST Framework, локаль/таймзона (ru-ru, Europe/Moscow). В production.py DEBUG=False и заданы фиксированные ALLOWED_HOSTS/CORS_ALLOWED_ORIGINS.

Обязательные переменные не помечены тегами (как в Go), но при их отсутствии процесс падает при старте (подключение к БД) либо при обращении к соответствующему сервису (клиенты system-log/user-service, хранилище S3).

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

Способ запуска Откуда берутся переменные
Локально (docker-compose) docker-compose.yml поднимает контейнер db (postgis/postgis) и сервисы api/migrations. Значения БД (DATABASE_USER/PASSWORD/NAME, ALLOWED_HOST_EMAIL) подставляются из окружения/файла .env рядом с compose. api собирается из compose/server/Dockerfile и стартует через entrypoint.sh
Kubernetes — собственный Helm-чарт репозитория .helm/values.yaml: зависимость от universal-chart. Блоки services.api.envs (обычные значения, per-env _default/stage/preprod/production) и services.api.secretEnvs (из k8s-секретов). Плюс cronjobs.periodic / cronjobs.immediately — шаблоны CronJob в .helm/templates/, наследующие envs/secretEnvs api
Kubernetes — этот infra-репозиторий (iac/apps/subscriptions) base/ — kustomize-манифесты с инъекцией секретов через HashiCorp Vault (annotations vault.hashicorp.com/*), обычные переменные заданы инлайн в env:. Оверлеи: yc-k8s-test (base + postgresql), brusnika-stage / brusnika-prod (Flux HelmRelease на universal-chart, блоки envs/secretEnvs)
CI/CD (GitLab) .gitlab-ci.yml подключает шаблоны generic/common-ci (universal-pipeline.yaml, ref apps-business); в workflow.rules задаются переменные пайплайна (SERVICE_NAME, STAND, NAMESPACE, RELEASE_NAME, CHART_NAME, CHART_VERSION, K8S_HUSTLER_BRANCH, HELM_SET_ARGS, IMAGE_NAME и т.п.)

Миграции БД (api). Выполняются в entrypoint контейнера api перед стартом uwsgi: python manage.py migrate --settings=config.settings.production (compose/server/entrypoint.sh). Cron-задачи миграций не выполняют. Локально в docker-compose.yml отдельный сервис migrations вызывает makemigrations (генерация миграций, не применение).


api (sarex-subscriptions)

Переменные читаются в config/settings/base.py (S3, OTEL) и config/settings/production.py (БД и внешние сервисы; production.py импортирует всё из base.py).

База данных (PostgreSQL / PostGIS)

Движок — core.db.backends.postgis (GeoDjango, требуется PostGIS). Все пять переменных обязательны — без них подключение к БД не поднимется.

Переменная Тип Обяз. По умолчанию Назначение
DATABASE_HOST string да Хост PostgreSQL
DATABASE_PORT string да Порт PostgreSQL
DATABASE_NAME string да Имя базы данных
DATABASE_USER string да Пользователь БД
DATABASE_PASSWORD string да Пароль пользователя БД

Хранилище S3 (static + media)

STATICFILES_STORAGE и DEFAULT_FILE_STORAGE = storages.backends.s3boto3.S3Boto3Storage, AWS_DEFAULT_ACL="public-read". Значения по умолчанию отсутствуют (os.getenv без default → None), поэтому для корректной отдачи статики/медиа креды фактически обязательны.

Переменная Тип Обяз. Назначение
YC_S3_ACCESS_KEY_ID string да Access key (AWS_ACCESS_KEY_ID)
YC_S3_SECRET_ACCESS_KEY string да Secret key (AWS_SECRET_ACCESS_KEY)
YC_S3_BUCKET_NAME string да Имя бакета (AWS_STORAGE_BUCKET_NAME)
YC_S3_ENDPOINT_URL string да Endpoint S3 (AWS_S3_ENDPOINT_URL)

Трейсинг (OpenTelemetry)

Блок OTEL в base.py (и обёртка WSGI в wsgi.py) включается по os.getenv('USE_OTEL', False). Важно: проверяется истинность строки, а не её значение — любая непустая строка (в т.ч. "False", "0") включает трейсинг. Задействует пакет django_otel_tools.

Переменная Тип По умолчанию Назначение
USE_OTEL bool-строка не задана (выкл.) Включает OTEL-трейсинг, otel-логгер и OtelMiddleware
SERVICE_NAME string subscriptions Имя сервиса в трейсах
TRACER_ENDPOINT string localhost:4375 Адрес OTLP-коллектора
USE_INSECURE bool-строка не задана (выкл.) Небезопасное (без TLS) подключение к коллектору; та же логика истинности строки, что и USE_OTEL

Захардкожено (не через окружение): SECRET_KEY, DEBUG (False в production), ALLOWED_HOSTS, CORS_ALLOWED_ORIGINS, CORS_ALLOW_ALL_ORIGINS=True.


cron-задачи (run_notifications, run_immediately_notifications)

Обе команды используют тот же модуль настроек и, помимо БД, обращаются к внешним сервисам для сбора данных и рассылки. Переменные читаются в production.py и потребляются в server/apps/notification/management/commands/*.py.

Внешние сервисы и рассылка

Переменная Тип Обяз. По умолчанию Назначение
SYSTEM_LOG_HOST string да Базовый URL сервиса system-log (логирование событий)
USER_SERVICE_HOST string да Базовый URL сервиса пользователей (Django/ЛК)
USER_SERVICE_LOGIN string нет "" Логин для авторизации в user-service
USER_SERVICE_PASSWORD string нет "" Пароль для авторизации в user-service
ALLOWED_HOST_EMAIL string нет https://lk.sarex.io Базовый URL для формирования ссылок в письмах (context_processing/document.py)

Email — Mailgun

Переменная Тип Обяз. По умолчанию Назначение
IS_MAILGUN_USE bool-строка нет True Использовать Mailgun для отправки писем
MAILGUN_API_KEY string нет "" API-ключ Mailgun
MAILGUN_BASE_URL string нет https://api.mailgun.net/v3/mg.sarex.io Базовый URL Mailgun API
MAILGUN_EMAIL_FROM string нет hello@sarex.io Адрес отправителя

Email — SMTP

SMTP-ветка активируется, только если заданы оба SMTP_EMAIL_HOST и SMTP_EMAIL_PORT (по умолчанию None — SMTP отключён).

Переменная Тип Обяз. По умолчанию Назначение
SMTP_EMAIL_HOST string нет None Хост SMTP-сервера
SMTP_EMAIL_PORT string нет None Порт SMTP-сервера
SMTP_EMAIL_FROM string нет hello@sarex.io Адрес отправителя

Telegram

Переменная Тип Обяз. По умолчанию Назначение
IS_USE_TELEGRAM bool-строка нет True Использовать Telegram-рассылку
TELEGRAM_BOT_TOKEN string нет "" Токен Telegram-бота (settings.TELEGRAM_BOT_TOKEN)

Значения IS_* читаются как строки Django-настроек: непустая строка истинна. Чтобы выключить канал, инфраструктурные манифесты задают "false"/"0" — но с точки зрения Python это тоже непустые строки, поэтому фактическое поведение зависит от того, как значение интерпретируется в коде команды (см. замечания).


Инфраструктурные и вспомогательные переменные

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

Переменная Где используется Назначение
API_ADDRESS base/*.yaml, .helm/values.yaml, brusnika-оверлеи Задаётся в манифестах, но кодом не читается — порт uwsgi фиксирован в uwsgi.ini (http = 0.0.0.0:8000)
DJANGO_SETTINGS_MODULE entrypoint.sh, wsgi.py, manage.py Модуль настроек Django (config.settings.production в проде)
NEXUS_USERNAME, NEXUS_PASSWORD compose/server/Dockerfile (build-arg), .gitlab-ci.yml Доступ к приватному PyPI (nexus.infra.sarex.io) при сборке образа
SERVICE_NAME, DOCKERFILE_PATH, BUILD_ARGS, CI_TRIGGER_SOURCE .gitlab-ci.yml (variables) Параметры сборки/пайплайна
STAND, NAMESPACE, RELEASE_NAME, CHART_NAME, CHART_VERSION, K8S_HUSTLER_BRANCH, HELM_SET_ARGS, IMAGE_NAME, ENABLE_BUILD_IMAGE .gitlab-ci.yml (workflow.rules) Параметры пайплайна generic/common-ci (деплой по окружениям stage/preprod/production)

Обратите внимание: SERVICE_NAME встречается в двух ролях — как переменная приложения (имя сервиса в OTEL-трейсах) и как переменная CI (имя сервиса для сборки/чарта). Значения задаются в разных местах и не связаны между собой.


Деплой из этого репозитория (iac/apps/subscriptions)

Здесь используется kustomize (не собственный Helm-чарт сервиса). Секреты БД и S3 инъектируются агентом Vault и подгружаются в окружение процесса до старта:

set -a
[ -f /vault/secrets/subscriptions-postgresql ] && . /vault/secrets/subscriptions-postgresql
[ -f /vault/secrets/subscriptions-minio ] && . /vault/secrets/subscriptions-minio
set +a
exec /server/entrypoint.sh

base/

  • backend-deployment.yaml — обычные переменные инлайн в env: (API_ADDRESS, SYSTEM_LOG_HOST, USER_SERVICE_HOST, IS_USE_TELEGRAM=false, IS_MAILGUN_USE=0, SMTP_EMAIL_FROM/HOST/PORT). Vault-шаблоны формируют DATABASE_HOST/PORT/NAME/USER/PASSWORD (из secrets/data/postgresql/apps/subscriptions, БД subscriptions_db) и YC_S3_ACCESS_KEY_ID/SECRET_ACCESS_KEY/BUCKET_NAME/ENDPOINT_URL (из secrets/data/minio/apps/subscriptions).
  • backend-service.yaml, namespace.yaml (istio-injection: enabled), serviceaccount.yaml (subscriptions-vault).
  • kustomization.yaml собирает namespace, serviceaccount, deployment и service.

Оверлеи

  • yc-k8s-test../base + postgresql.yaml (HelmRelease postgresql-contour: создаёт БД subscriptions_db, пользователя subscriptions, расширения ltree/pg_stat_statements/postgis/timescaledb, восстановление из дампа).
  • brusnika-stage / brusnika-prod — Flux HelmRelease на universal-chart (v0.1.7). Переменные — в envs (DATABASE_HOST/PORT/NAME, API_ADDRESS, SYSTEM_LOG_HOST, USER_SERVICE_HOST, IS_USE_TELEGRAM=false, IS_MAILGUN_USE=0, SMTP_*), секреты — в secretEnvs (postgres-secret: username/password; yc-s3-secret: key_id/access_key/storage_bucket_name/endpoint_url). Отличаются только DATABASE_HOST (postgres-service в prod vs 192.168.2.45 в stage).

В этих kustomize/brusnika-манифестах не задаются USE_OTEL, TELEGRAM_BOT_TOKEN, MAILGUN_API_KEY, USER_SERVICE_LOGIN/PASSWORD, ALLOWED_HOST_EMAIL — используются дефолты из кода. Telegram и Mailgun выключены (IS_USE_TELEGRAM=false, IS_MAILGUN_USE=0), рассылка идёт по SMTP.


Отличия от Helm-чарта репозитория (.helm/values.yaml)

Собственный чарт сервиса (.helm/) — это другой путь деплоя, с более широким набором переменных, чем kustomize/brusnika здесь:

  • дополнительно задаёт ALLOWED_HOST_EMAIL, USE_OTEL=True, SERVICE_NAME, TRACER_ENDPOINT, USE_INSECURE=True (per-env), а также секреты MAILGUN_API_KEY (mailgun-cred) и TELEGRAM_BOT_TOKEN (telegram-bot-secret);
  • IS_USE_TELEGRAM=true, монтирует uwsgi.ini через ConfigMap;
  • определяет CronJob subscription-periodic (0,30 * * * *, run_notifications) и subscription-immediately (* * * * *, run_immediately_notifications), наследующие envs/secretEnvs api.

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

  • manage.py указывает на несуществующий модуль настроек. По умолчанию manage.py задаёт DJANGO_SETTINGS_MODULE=config.settings.local, но модуля local.py в репозитории нет (есть только base.py и production.py). Поэтому management-команды нужно запускать с явным --settings=config.settings.production (как это и делают entrypoint и cron-задачи). Локальный сервис migrations в docker-compose.yml вызывает makemigrations без --settings — команда упадёт из-за отсутствия local.
  • USE_OTEL/USE_INSECURE работают по истинности строки. os.getenv('USE_OTEL', False) возвращает строку; любое непустое значение (включая "False", "0") включает трейсинг. Чтобы выключить — переменную нужно не задавать вовсе, а не ставить False.
  • Каналы рассылки IS_MAILGUN_USE / IS_USE_TELEGRAM — тоже строки. Значения по умолчанию — True (Python-объект), но из окружения приходит строка; поведение зависит от того, как значение проверяется в коде команды. При настройке важно учитывать это (в манифестах используют "false"/"0").
  • S3 обязателен для статики/медиа. Хранилища заданы как S3 без файлового фолбэка; при отсутствии YC_S3_* operations со статикой/медиа будут падать, хотя сам процесс поднимется.
  • SECRET_KEY захардкожен в base.py и не выносится в окружение — для продакшена это стоит вынести в секрет.
  • Расхождения имён БД между окружениями: infra base и postgresql-чарт используют subscriptions_db, brusnika-оверлеи — subscriptions. При подключении важно использовать значение конкретного окружения.
  • Два деплой-пути расходятся по набору переменных (kustomize/brusnika vs .helm): OTEL, Telegram-токен, Mailgun-ключ и ALLOWED_HOST_EMAIL присутствуют только в .helm. Это стоит учитывать при переносе окружения.

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

api (uwsgi, миграции при старте):

  • DATABASE_HOST, DATABASE_PORT, DATABASE_NAME, DATABASE_USER, DATABASE_PASSWORD
  • YC_S3_ACCESS_KEY_ID, YC_S3_SECRET_ACCESS_KEY, YC_S3_BUCKET_NAME, YC_S3_ENDPOINT_URL
  • при необходимости — USE_OTEL (+ SERVICE_NAME, TRACER_ENDPOINT, USE_INSECURE)

cron run_notifications / run_immediately_notifications:

  • блок БД (как у api)
  • SYSTEM_LOG_HOST, USER_SERVICE_HOST (+ USER_SERVICE_LOGIN, USER_SERVICE_PASSWORD при авторизации)
  • канал рассылки: IS_MAILGUN_USE + MAILGUN_API_KEY или SMTP_EMAIL_HOST + SMTP_EMAIL_PORT; при Telegram — IS_USE_TELEGRAM + TELEGRAM_BOT_TOKEN
  • при необходимости — ALLOWED_HOST_EMAIL (ссылки в письмах)

См. пример значений в .env.example рядом с этим файлом.