190 lines
17 KiB
Markdown
190 lines
17 KiB
Markdown
# Конфигурация проекта bim-backend-v2
|
||
|
||
Документ описывает все переменные окружения и способы конфигурирования сервиса.
|
||
|
||
## Способы конфигурирования
|
||
|
||
Сервис настраивается **только через переменные окружения**. Разбор выполняется в `config/config.go` (функция `NewConfig`) через библиотеку [`kelseyhightower/envconfig`](https://github.com/kelseyhightower/envconfig) (структура `Config`).
|
||
|
||
Особенности разбора:
|
||
|
||
- **Префикса нет** — `envconfig.Process("", &cfg)` вызывается с пустым префиксом, поэтому имена переменных задаются как есть (напр. `POSTGRES_ADDRESS`, `API_ADDRESS`). Имя переменной определяется тегом `envconfig:"..."` у каждого поля.
|
||
- Типы приводятся автоматически по типу поля Go (`string`, `int`, `bool`, `uint64`). Для `bool` подходят `1`/`0`/`true`/`false`.
|
||
- Ошибка разбора приводит к `panic` при старте (в `NewConfig` `envconfig.Process` завёрнут в `sync.Once`, ошибка не возвращается, а паникует).
|
||
- Отсутствующая переменная не является ошибкой — поле получает нулевое значение соответствующего типа (пустая строка, `0`, `false`). Обязательность полей не проверяется.
|
||
|
||
В отличие от python-сервисов, приложение **загружает `.env` автоматически**: в `cmd/httpserver/main.go` вызывается `godotenv.Load(".env")` (если файла нет — печатается предупреждение и используются переменные окружения процесса).
|
||
|
||
Отдельного конфиг-файла (yaml/toml) у приложения нет.
|
||
|
||
Источники переменных по способам запуска:
|
||
|
||
| Способ запуска | Откуда берутся переменные |
|
||
| --- | --- |
|
||
| Локально (бинарник) | Файл `.env` в рабочем каталоге (`godotenv.Load`) либо переменные окружения процесса |
|
||
| Локально (контейнеры) | `.docker/docker-compose.yml`: `env_file` → `.docker/.env` + `.docker/.docker.env` |
|
||
| Kubernetes (Helm, репозиторий приложения) | `.helm/values-<env>.yaml`: блоки `envs` (обычные значения) и `secrets` (из k8s-секретов); шаблон `.helm/templates/api.yaml` |
|
||
| Kubernetes (IaC, этот репозиторий) | `iac/apps/bim/base/backend-deployment.yaml`: блок `env` и секреты из Vault (аннотации `vault.hashicorp.com/*`, шаблон `bim-postgresql`) |
|
||
| CI/CD (GitLab) | `.gitlab-ci.yml`: подключение общих пайплайнов `generic/common-ci` и переменные `workflow.rules` по ветке/тегу |
|
||
|
||
Способы запуска процессов:
|
||
|
||
| Команда | Точка входа | Назначение |
|
||
| --- | --- | --- |
|
||
| `httpserver` (`make api` → `go install ./cmd/httpserver`) | `cmd/httpserver/main.go` | HTTP API. При старте применяет миграции (`appmigrations.RunOnStartup`), поднимает `net/http/pprof` на `:8081`, затем запускает API на `API_ADDRESS` |
|
||
| `migrations` (`go install ./cmd/migrations`) | `cmd/migrations/main.go` | Отдельный запуск миграций БД |
|
||
|
||
Порядок запуска в контейнере (`.docker/entrypoint.sh`): запускается `/go/bin/httpserver` (строка запуска миграций закомментирована — миграции выполняются самим приложением на старте). Финальный образ (`.docker/api.dockerfile`) — `scratch` со статически слинкованным бинарником `httpserver` и вшитым CA-сертификатом Postgres (`/root/yandex_pg.pem`).
|
||
|
||
## Переменные приложения
|
||
|
||
Ниже перечислены все переменные, читаемые кодом (`config/config.go`). Префикса нет. Дефолт — это нулевое значение типа Go, если переменная не задана.
|
||
|
||
### App
|
||
|
||
| Переменная | Тип | Значение по умолчанию | Назначение |
|
||
| --- | --- | --- | --- |
|
||
| `APP_NAME` | string | `""` | Имя приложения (в коде помечено TODO — в deploy-envs не задаётся) |
|
||
| `APP_VERSION` | string | `""` | Версия приложения (TODO — в deploy-envs не задаётся) |
|
||
| `LOG_LEVEL` | string | `""` | Уровень логирования, передаётся в `logging.NewLogger` (TODO — в deploy-envs не задаётся) |
|
||
| `API_ADDRESS` | string | `""` | Адрес прослушивания HTTP API в формате `host:port` (напр. `0.0.0.0:8080`) |
|
||
| `TEST_ENV` | string | `""` | Служебное поле для тестов |
|
||
|
||
### PostgreSQL
|
||
|
||
Сервис работает с **тремя** кластерами PostgreSQL (master, slave-1, slave-2). Выбор кластера для конкретного BIM выполняется по его id в `internal/app/http/httpserver.go` (сравнение с `LAST_MASTER_BIM*`/`LAST_SLAVE_1_BIM*`). Строка подключения собирается в `Config.GetPostgresConnectionURL` (`postgres://user:password@addr:port/db`, при `ENABLE_SSL=false` добавляется `?sslmode=disable`).
|
||
|
||
Master-кластер:
|
||
|
||
| Переменная | Тип | Назначение |
|
||
| --- | --- | --- |
|
||
| `POSTGRES_ADDRESS` | string | Хост PostgreSQL (master) |
|
||
| `POSTGRES_PORT` | string | Порт PostgreSQL (master) |
|
||
| `POSTGRES_USER` | string | Пользователь БД |
|
||
| `POSTGRES_PASSWORD` | string | Пароль пользователя БД |
|
||
| `POSTGRES_DB` | string | Имя базы данных |
|
||
| `POSTGRES_POOL_SIZE` | int | Размер пула соединений |
|
||
| `DB_CERT_PATH` | string | Путь к CA-сертификату PostgreSQL (используется при `ENABLE_SSL=1`) |
|
||
|
||
Slave-1 кластер — те же поля с суффиксом `_2`:
|
||
|
||
| Переменная | Тип | Назначение |
|
||
| --- | --- | --- |
|
||
| `POSTGRES_ADDRESS_2` | string | Хост PostgreSQL (slave-1) |
|
||
| `POSTGRES_PORT_2` | string | Порт |
|
||
| `POSTGRES_USER_2` | string | Пользователь |
|
||
| `POSTGRES_PASSWORD_2` | string | Пароль |
|
||
| `POSTGRES_DB_2` | string | База данных |
|
||
| `POSTGRES_POOL_SIZE_2` | int | Размер пула |
|
||
| `DB_CERT_PATH_2` | string | Путь к CA-сертификату |
|
||
|
||
Slave-2 кластер — те же поля с суффиксом `_3`:
|
||
|
||
| Переменная | Тип | Назначение |
|
||
| --- | --- | --- |
|
||
| `POSTGRES_ADDRESS_3` | string | Хост PostgreSQL (slave-2) |
|
||
| `POSTGRES_PORT_3` | string | Порт |
|
||
| `POSTGRES_USER_3` | string | Пользователь |
|
||
| `POSTGRES_PASSWORD_3` | string | Пароль |
|
||
| `POSTGRES_DB_3` | string | База данных |
|
||
| `POSTGRES_POOL_SIZE_3` | int | Размер пула |
|
||
| `DB_CERT_PATH_3` | string | Путь к CA-сертификату |
|
||
|
||
### Шардирование BIM по кластерам
|
||
|
||
| Переменная | Тип | Назначение |
|
||
| --- | --- | --- |
|
||
| `LAST_MASTER_BIM` | uint64 | Верхняя граница id BIM для master-кластера (API v1) |
|
||
| `LAST_MASTER_BIM_V3` | uint64 | Верхняя граница id BIM для master-кластера (API v2 / BIM v3) |
|
||
| `LAST_SLAVE_1_BIM` | uint64 | Верхняя граница id BIM для slave-1 (API v1) |
|
||
| `LAST_SLAVE_1_BIM_V3` | uint64 | Верхняя граница id BIM для slave-1 (API v2 / BIM v3) |
|
||
|
||
### Прочее
|
||
|
||
| Переменная | Тип | Значение по умолчанию | Назначение |
|
||
| --- | --- | --- | --- |
|
||
| `ENABLE_SSL` | bool | `false` | Подключение к PostgreSQL по TLS. При `false` в DSN добавляется `sslmode=disable` |
|
||
| `ENABLE_SQL_QUERY` | bool | `false` | Логировать SQL-запросы |
|
||
| `DJANGO_HOST` | string | `""` | Базовый URL Django-бэкенда для проверки прав администратора (см. `ENDPOINTS.md`) |
|
||
| `INTEGRATION_TESTS` | bool | `false` | Режим интеграционных тестов. При `true` `CheckUserIsAdmin` всегда возвращает `true` |
|
||
|
||
## Переменные, не читаемые приложением (инфраструктура/сборка/тесты)
|
||
|
||
Присутствуют в `.docker/.env`, `.docker/.docker.env`, `docker-compose.yml` или Helm, но `config/config.go` их не разбирает.
|
||
|
||
| Переменная | Где используется | Назначение |
|
||
| --- | --- | --- |
|
||
| `API_PORT` | `.docker/.env` | Порт API для локального compose |
|
||
| `POSTGRES_EXTERNAL_PORT`, `POSTGRES_EXTERNAL_PORT_2` | `docker-compose.yml` | Проброс портов контейнеров Postgres |
|
||
| `POSTGRES_ADDRESS2` | `.docker/.docker.env` | Опечатка/легаси (нет суффикса `_`), кодом не читается |
|
||
| `GRPC_ADDRESS`, `GRPC_PORT` | `.docker/.env` | Объявлены, но кодом не используются |
|
||
| `TEST_BIM`, `JWT_TEST` | `.docker/.env`, `.docker/.docker.env` | Данные для интеграционных тестов |
|
||
| `POSTGRES_*_4`, `DB_CERT_PATH_4` | `.helm/values-*.yaml`, IaC `backend-deployment.yaml` | Четвёртый кластер задаётся в деплое, но `config.go` доходит только до суффикса `_3` |
|
||
| `LAST_SLAVE_2_BIM(_V3)`, `LAST_SLAVE_3_BIM(_V3)`, `LAST_SLAVE_4_BIM(_V3)` | `.helm/values-*.yaml` | Заданы в values, но кодом не читаются (актуальны только `LAST_MASTER_*` и `LAST_SLAVE_1_*`) |
|
||
| `CI_COMMIT_SHORT_SHA` | `.gitlab-ci.yml` (build-arg) | Тег/версия сборки образа |
|
||
|
||
## Переменные из Helm-чарта приложения (`.helm/values-<env>.yaml`)
|
||
|
||
Обычные значения задаются в блоке `envs` для каждого окружения (`stage`/`preprod`/`production`) и содержат те же переменные `POSTGRES_*`, `API_ADDRESS`, `DJANGO_HOST`, `ENABLE_SSL`, `ENABLE_SQL_QUERY`, `LAST_*`, что описаны выше (различаются адресами БД, размерами пула, границами шардирования и доменом Django).
|
||
|
||
Значения из секретов (блок `secrets`, монтируются как env через `secretKeyRef`):
|
||
|
||
| Переменная | Секрет (`secret_name`) | Ключ (`secret_key`) |
|
||
| --- | --- | --- |
|
||
| `POSTGRES_USER` | `bim-v2-database-secret` | `username` |
|
||
| `POSTGRES_PASSWORD` | `bim-v2-database-secret` | `password` |
|
||
| `POSTGRES_USER_2` | `bim-v2-database-secret-2` | `username` |
|
||
| `POSTGRES_PASSWORD_2` | `bim-v2-database-secret-2` | `password` |
|
||
| `POSTGRES_USER_3` | `bim-v2-database-secret-3` | `username` |
|
||
| `POSTGRES_PASSWORD_3` | `bim-v2-database-secret-3` | `password` |
|
||
| `POSTGRES_USER_4` | `bim-v2-database-secret-3` | `username` |
|
||
| `POSTGRES_PASSWORD_4` | `bim-v2-database-secret-3` | `password` |
|
||
|
||
Ключевые не-env значения чарта: `api.name`, `api.image`, `api.version`, `api.port` (`8080`), `api.replicas`, `api.service_name`/`api.service_port` (`80`), `api.service_account`, `api.requests.memory`/`cpu`, `api.api_host`, `api.api_host_prefix` (`/bimv2/api/`), `api.api_path` (`/api/`), `api.internal_path` (`/internal/`), `api.permitted_ns` (namespace'ы, которым разрешён доступ к `/internal/*`), `imagePullSecrets`.
|
||
|
||
Сетевой слой (`.helm/templates/mesh-config.yaml`, Istio):
|
||
|
||
- `VirtualService` (при `api.virtual_service.enabled`) маршрутизирует `api_host_prefix` → `api_path`, задаёт CORS (`allowOrigins` — `*.sarex.io` и `localhost`, `allowHeaders` включают `Authorization`, `Content-Type`, `Identity`).
|
||
- `AuthorizationPolicy` `routes-v2`: доступ к `/api/*` разрешён только от istio-ingressgateway при наличии claim `token_type=access`; доступ к `/internal/*` — только из namespace'ов `api.permitted_ns`.
|
||
|
||
## Переменные из IaC-репозитория (`iac/apps/bim/`)
|
||
|
||
Деплой в этом репозитории (kustomize) устроен иначе, чем чарт приложения:
|
||
|
||
- `base/backend-deployment.yaml` — Deployment `backend` в namespace `bim`, образ `bim-api`, `containerPort: 8000`, `API_ADDRESS=0.0.0.0:8000`, health-проба `GET /ping`.
|
||
- Секреты PostgreSQL инъектируются **из Vault** (аннотации `vault.hashicorp.com/*`, роль `bim`, путь `secrets/data/postgresql/apps/bim`) в файл `/vault/secrets/bim-postgresql`, который экспортируется в окружение перед запуском (`set -a; . /vault/secrets/bim-postgresql; set +a; exec ./httpserver`). Шаблон Vault задаёт `POSTGRES_ADDRESS[_2../_4]`, `POSTGRES_PORT*`, `POSTGRES_DB*` (все указывают на `postgresql.bim.svc.cluster.local:5432`, БД `bim_db`) и `POSTGRES_USER*`/`POSTGRES_PASSWORD*` из Vault.
|
||
- Прочие env заданы прямо в манифесте: `LAST_MASTER_BIM`, `LAST_MASTER_BIM_V3`, `LAST_SLAVE_1_BIM`, `POSTGRES_POOL_SIZE`, `DB_CERT_PATH_2/3/4`, `DJANGO_HOST` (`http://backend.django.svc.cluster.local:8000`), `ENABLE_SQL_QUERY=0`, `ENABLE_SSL=0`.
|
||
- Оверлеи `brusnika-prod`, `brusnika-stage`, `yc-k8s-test` патчат base (реплики, образ, postgresql).
|
||
|
||
## Переменные в CI (`.gitlab-ci.yml`)
|
||
|
||
Пайплайн подключает общие шаблоны `generic/common-ci` (`universal-pipeline-<env>.yaml`, `common-security-scan.yaml`, `common-build.yaml`) и переключает окружение по ветке/тегу через `workflow.rules`:
|
||
|
||
| Условие | STAND | Namespace |
|
||
| --- | --- | --- |
|
||
| ветка `master` | `preprod` | `bim-api-preprod` |
|
||
| ветка `stage` | `stage` | `bim-api-stage` |
|
||
| тег (`CI_COMMIT_TAG`) | `prod` | `bim-api-prod` |
|
||
|
||
Общие переменные job'ов: `RELEASE_NAME=bim-backend-v2`, `CHART_NAME=bim-backend-v2`, `CHART_VERSION=0.0.1-<stand>`, `IMAGE_PATH=api.image`, `DOCKERFILE_PATH=.docker/api.dockerfile`, `HELM_SET_ARGS=--set api.image=${IMAGE_NAME}`, `BUILD_ARGS=--build-arg CI_COMMIT_SHORT_SHA=...`, флаги `ENABLE_BUILD_CHART`/`ENABLE_BUILD_IMAGE`/`ENABLE_STATE_UPDATE`/`ENABLE_DEPLOY=true`, `ENABLE_LINTER=false`. Стадии: `linter → test → unittest → prebuild-secscan → build → state-update → deploy`.
|
||
|
||
## Замечания и потенциальные проблемы
|
||
|
||
- Переменные разбираются **без префикса** (`envconfig.Process("", ...)`) — имена совпадают с тегами `envconfig` полей структуры `Config`.
|
||
- Обязательность полей не валидируется: отсутствующая переменная молча получает нулевое значение. Например пустой `API_ADDRESS` приведёт к попытке слушать на `":0"`.
|
||
- В коде объявлены только три кластера (`POSTGRES_*`, `_2`, `_3`), тогда как в деплое присутствует и четвёртый (`_4`). Переменные `_4` и дополнительные `LAST_SLAVE_2/3/4_*` в окружении задаются, но приложением не используются.
|
||
- Приложение загружает `.env` автоматически (`godotenv.Load(".env")`); при отсутствии файла ошибки нет — берутся переменные процесса.
|
||
- В `NewConfig` ошибка `envconfig.Process` не возвращается, а вызывает `panic` (обёрнута в `sync.Once`).
|
||
|
||
## Минимальный набор для локального запуска
|
||
|
||
Postgres поднимается через `docker-compose` (`.docker/docker-compose.yml`), приложение — через `make api` и запуск `httpserver`. Минимально необходимо задать (готовые примеры — в `.env.example`):
|
||
|
||
- `API_ADDRESS`
|
||
- Master-кластер: `POSTGRES_ADDRESS`, `POSTGRES_PORT`, `POSTGRES_USER`, `POSTGRES_PASSWORD`, `POSTGRES_DB`, `POSTGRES_POOL_SIZE`
|
||
- Slave-кластеры `_2` и `_3` (те же поля) — сервис создаёт соединения ко всем трём при старте
|
||
- `ENABLE_SSL=0`, `DB_CERT_PATH*` (при `ENABLE_SSL=1`)
|
||
- `DJANGO_HOST`
|
||
- `LAST_MASTER_BIM`, `LAST_MASTER_BIM_V3`, `LAST_SLAVE_1_BIM`, `LAST_SLAVE_1_BIM_V3`
|
||
- `INTEGRATION_TESTS=0`, `ENABLE_SQL_QUERY` по необходимости
|