iac/apps/django/aero/kustomization.yaml
emelinda c8eefab4b6 BIM API в контуре aero + вывод наружу
Релиз идёт из base без единого патча, и это проверено по исходникам
bim-backend-v2, а не принято на веру:

  * DJANGO_HOST в base уже правильный (backend-svc.django:80) — редкость,
    у processing и workspaces там лежал несуществующий backend:8000;
  * DB_CERT_PATH_2/3/4 указывают на сертификат управляемой СУБД Яндекса,
    которого в контуре нет, но зануления не требуют: файл читается только
    под флагом ENABLE_SSL, а при нуле к строке подключения дописывается
    ?sslmode=disable и путь не трогается;
  * пять комплектов POSTGRES_*_N в vault-шаблоне смотрят на один хост —
    в проде это реплики, в контуре СУБД одна.

Django ходил в никуда: BIMV2_INTERNAL_HOST в base ведёт в namespace
bim-api к сервису bim-backend-v2-service, а релиз разворачивается в
namespace bim и называет сервис backend-svc. Поправлено в обоих релизах
django — backend и celery.

Наружу выведен отдельным поддоменом bim.sarex.local.lonsdaleites.ru, а не
префиксом на домене платформы: своего фронтенда у сервиса нет, общего с
платформой origin тоже, а его пути конфликтовали бы с маршрутом /api/
django. Имя добавлено в dnsNames сертификата — без этого браузер получил
бы сертификат без него.

Обвязка: regcred и роль Vault для namespace bim, apps/bim в
gitea_sync_paths, секрет secrets/apps/bim/postgres (база bim_db и роль bim
уже заведены в contour.databases).

ИЗВЕСТНЫЙ РИСК, проверяется логами первого пода. В ветке develop у
bim-backend-v2 миграции катятся при старте httpserver, ошибка фатальна, а
адрес БД внутри RunOnStartup взят не из конфига, а из константы
rc1b-sse4o3n9vea392g4.mdb.yandexcloud.net:6432. Из контура он недостижим.
Если этот код попал в образ contour_f9f2a39-dirty, под уйдёт в
CrashLoopBackOff и лечиться это будет только пересборкой образа.
2026-08-09 12:45:09 +03:00

234 lines
14 KiB
YAML
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

---
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
namespace: django
resources:
- ../base
patches:
# Свой nginx.conf вместо базового: в base зашиты upstream'ы на namespace,
# которых в контуре нет, и nginx из-за этого не стартовал. Подробности —
# в шапке nginx-configmap.yaml.
#
# ИМЕННО ПАТЧ, а не resources. Объект с тем же kind+name уже пришёл из
# ../base, и добавление его вторым ресурсом валит сборку целиком:
# may not add resource with an already registered id:
# ConfigMap.v1.[noGrp]/nginx-configmap.django
# Перекрывать базовый ресурс можно только патчем.
- path: nginx-configmap.yaml
target:
kind: ConfigMap
name: nginx-configmap
# --- celery: те же правки, что и у backend --------------------------------
# Worker крутит тот же образ и те же настройки, поэтому и расхождения с base
# у него ровно те же: нет Kafka, нет Zitadel, MinIO живёт внутри кластера.
# Обоснования — в блоке backend ниже, здесь не дублирую.
#
# SERVER_ZITADEL_ENABLED правки не требует: в base у celery он уже "False"
# (в отличие от backend).
- target: {kind: HelmRelease, name: celery}
patch: |
- op: remove
path: /spec/values/services/celery/podAnnotations/_default/vault.hashicorp.com~1agent-inject-secret-django-kafka
- op: remove
path: /spec/values/services/celery/podAnnotations/_default/vault.hashicorp.com~1agent-inject-template-django-kafka
- op: remove
path: /spec/values/services/celery/podAnnotations/_default/vault.hashicorp.com~1agent-inject-secret-django-common
- op: remove
path: /spec/values/services/celery/podAnnotations/_default/vault.hashicorp.com~1agent-inject-template-django-common
- op: replace
path: /spec/values/services/celery/podAnnotations/_default/vault.hashicorp.com~1agent-inject-template-django-s3
value: |-
{{- with secret "secrets/data/minio/apps/django" }}
AWS_S3_ENDPOINT_URL=http://minio.minio.svc.cluster.local:9000
S3_HOST=http://minio.minio.svc.cluster.local:9000
{{- $buckets := index .Data.data "buckets" }}
S3_BUCKET={{ if gt (len $buckets) 0 }}{{ index (index $buckets 0) "name" }}{{ else }}django{{ end }}
S3_LOGIN={{ index .Data.data "access_key" }}
S3_PASSWORD={{ index .Data.data "secret_key" }}
{{- end }}
# Стартовый скрипт без подключения django-kafka: этого секрета в контуре
# нет. migrate и создание администратора сюда НЕ добавляются — схемой
# владеет backend, а два процесса, катящих миграции наперегонки при
# одновременном старте, — это гонка на ровном месте.
- op: replace
path: /spec/values/services/celery/deployment/args/_default/0
value: |
set -a
[ -f /vault/secrets/django-postgresql ] && . /vault/secrets/django-postgresql
[ -f /vault/secrets/django-rabbitmq ] && . /vault/secrets/django-rabbitmq
[ -f /vault/secrets/django-s3 ] && . /vault/secrets/django-s3
[ -f /vault/secrets/django-jwt-private ] && export JWT_PRIVATE_KEY="$(cat /vault/secrets/django-jwt-private)"
[ -f /vault/secrets/django-jwt-public ] && export JWT_PUBLIC_KEY="$(cat /vault/secrets/django-jwt-public)"
set +a
exec celery -A config worker -B -l info -E -Q default -n default_worker.%h --concurrency=2
# Адреса на wb.sarex.io — чужой контур, из aero недостижим. В base их
# четыре, и все четыре у celery: у backend те же переменные уже указывают
# внутрь кластера. WORKFLOWS_USE=1, так что оставить как есть — значит
# получить зависания на недоступном домене вместо явной ошибки.
# BIM API. В base адрес ведёт в namespace bim-api к сервису
# bim-backend-v2-service — ни того, ни другого в контуре нет: релиз
# разворачивается в namespace bim и называет сервис backend-svc.
# См. apps/bim/aero.
- op: test
path: /spec/values/services/celery/envs/8/name
value: BIMV2_INTERNAL_HOST
- op: replace
path: /spec/values/services/celery/envs/8/value/_default
value: http://backend-svc.bim.svc.cluster.local:80
- op: test
path: /spec/values/services/celery/envs/15/name
value: SERVER_API_HOST
- op: replace
path: /spec/values/services/celery/envs/15/value/_default
value: https://sarex.local.lonsdaleites.ru
- op: test
path: /spec/values/services/celery/envs/16/name
value: SERVER_HOST
- op: replace
path: /spec/values/services/celery/envs/16/value/_default
value: https://sarex.local.lonsdaleites.ru
- op: test
path: /spec/values/services/celery/envs/17/name
value: WORKFLOWS_HOST
- op: replace
path: /spec/values/services/celery/envs/17/value/_default
value: http://backend-svc.processing.svc.cluster.local:80
- op: test
path: /spec/values/services/celery/envs/18/name
value: WORKFLOWS_BASE_HOST
- op: replace
path: /spec/values/services/celery/envs/18/value/_default
value: http://backend-svc.django.svc.cluster.local:80
# Тег образа выравнен с backend. В base у celery production_a96dead0, а у
# backend production_f813140d — worker и web крутили бы разный код, и
# рассинхрон вылезал бы не при старте, а на конкретной задаче: worker
# десериализует аргументы по своей версии модели.
- op: replace
path: /spec/values/services/celery/image/name/_default
value: cr.yandex/crp3ccidau046kdj8g9q/backend:production_f813140d
# --- backend: убираем зависимости, которых в контуре нет ------------------
- target: {kind: HelmRelease, name: backend}
patch: |
# Kafka и Zitadel в контуре не развёрнуты. Само приложение без них живёт:
# * SERVER_KAFKA_ENABLED в base уже "False", а в sarex-backend продюсер
# создаётся только под флагом (config/settings/deps/kafka.py);
# * zitadel_enabled читается ровно в одном месте — update_ams_user()
# в sarex/core/utils.py, которая при выключенном флаге сразу выходит.
# К аутентификации Zitadel отношения не имеет.
#
# Но аннотации инжекции убрать ОБЯЗАТЕЛЬНО: vault-agent-init не отрендерит
# шаблон для несуществующего секрета, упадёт, и под навсегда останется в
# Init:Error — приложение до своих флагов даже не дойдёт.
#
# В JSON Pointer слэш внутри ключа экранируется как ~1, поэтому
# vault.hashicorp.com/agent-... записан как vault.hashicorp.com~1agent-...
- op: remove
path: /spec/values/services/backend/podAnnotations/_default/vault.hashicorp.com~1agent-inject-secret-django-kafka
- op: remove
path: /spec/values/services/backend/podAnnotations/_default/vault.hashicorp.com~1agent-inject-template-django-kafka
- op: remove
path: /spec/values/services/backend/podAnnotations/_default/vault.hashicorp.com~1agent-inject-secret-django-common
- op: remove
path: /spec/values/services/backend/podAnnotations/_default/vault.hashicorp.com~1agent-inject-template-django-common
# S3 внутри кластера, а не через внешний домен. В base шаблон прибит к
# https://minio.contour.infra.sarex.tech — чужому домену, который отсюда
# не резолвится. Через values это не перекрыть: адрес зашит ВНУТРИ
# vault-шаблона аннотации, поэтому заменяем аннотацию целиком.
# Ходим напрямую в сервис MinIO, как это делал compose-стек.
- op: replace
path: /spec/values/services/backend/podAnnotations/_default/vault.hashicorp.com~1agent-inject-template-django-s3
value: |-
{{- with secret "secrets/data/minio/apps/django" }}
AWS_S3_ENDPOINT_URL=http://minio.minio.svc.cluster.local:9000
S3_HOST=http://minio.minio.svc.cluster.local:9000
{{- $buckets := index .Data.data "buckets" }}
S3_BUCKET={{ if gt (len $buckets) 0 }}{{ index (index $buckets 0) "name" }}{{ else }}django{{ end }}
S3_LOGIN={{ index .Data.data "access_key" }}
S3_PASSWORD={{ index .Data.data "secret_key" }}
{{- end }}
# Выключаем Zitadel. Операция test перед replace — страховка от смещения
# индекса: если в base порядок envs изменится, патч упадёт явно, вместо
# того чтобы молча перезаписать соседнюю переменную.
- op: test
path: /spec/values/services/backend/envs/2/name
value: SERVER_ZITADEL_ENABLED
- op: replace
path: /spec/values/services/backend/envs/2/value/_default
value: "False"
# BIM API — то же, что у celery выше: в base адрес ведёт в namespace
# bim-api к сервису bim-backend-v2-service, а в контуре это namespace bim
# и сервис backend-svc.
- op: test
path: /spec/values/services/backend/envs/10/name
value: BIMV2_INTERNAL_HOST
- op: replace
path: /spec/values/services/backend/envs/10/value/_default
value: http://backend-svc.bim.svc.cluster.local:80
# --- Администратор платформы --------------------------------------------
# Учётка приезжает из Vault под именами, которые ждёт штатная
# createsuperuser --noinput. Секрет кладёт ansible
# (vault_app_secrets, secrets/apps/django/superuser).
- op: add
path: /spec/values/services/backend/podAnnotations/_default/vault.hashicorp.com~1agent-inject-secret-django-superuser
value: secrets/data/apps/django/superuser
- op: add
path: /spec/values/services/backend/podAnnotations/_default/vault.hashicorp.com~1agent-inject-template-django-superuser
value: |-
{{- with secret "secrets/data/apps/django/superuser" -}}
DJANGO_SUPERUSER_USERNAME={{ index .Data.data "username" }}
DJANGO_SUPERUSER_EMAIL={{ index .Data.data "email" }}
DJANGO_SUPERUSER_PASSWORD={{ index .Data.data "password" }}
{{- end -}}
# Стартовый скрипт целиком, а не точечная правка: JSON6902 умеет заменять
# только элемент списка, а вся логика запуска — один shell-блок args[0].
# Отличия от base:
# * не подключается django-kafka — этого секрета в контуре нет;
# * добавлены migrate и создание администратора.
#
# Почему migrate здесь, хотя его же делает /opt/sarex/entrypoint.sh:
# там он запущен без set -e, и его падение НИКАК не мешает uwsgi
# стартовать. Именно так контур несколько часов отдавал 200 на /admin/
# с пустой схемой БД, пока backend не мог аутентифицироваться. Здесь
# команда выполняется под set -e из command базы (/bin/sh -ec), поэтому
# неудача валит под в CrashLoopBackOff — то есть становится видимой.
# Повторный прогон внутри entrypoint.sh при уже накатанной схеме
# отрабатывает вхолостую за секунды.
- op: replace
path: /spec/values/services/backend/deployment/args/_default/0
value: |
set -a
[ -f /vault/secrets/django-postgresql ] && . /vault/secrets/django-postgresql
[ -f /vault/secrets/django-rabbitmq ] && . /vault/secrets/django-rabbitmq
[ -f /vault/secrets/django-s3 ] && . /vault/secrets/django-s3
[ -f /vault/secrets/django-superuser ] && . /vault/secrets/django-superuser
[ -f /vault/secrets/django-jwt-private ] && export JWT_PRIVATE_KEY="$(cat /vault/secrets/django-jwt-private)"
[ -f /vault/secrets/django-jwt-public ] && export JWT_PUBLIC_KEY="$(cat /vault/secrets/django-jwt-public)"
set +a
python manage.py migrate --noinput
# Идемпотентно по построению: команда отрабатывает при КАЖДОМ старте
# пода, а на уже существующем логине штатно возвращает
# "That username is already taken" с ненулевым кодом. Ошибка ожидаема
# и гасится веткой else, иначе set -e ронял бы под на втором запуске.
if [ -n "${DJANGO_SUPERUSER_USERNAME:-}" ]; then
if python manage.py createsuperuser --noinput; then
echo "entrypoint: администратор создан"
else
echo "entrypoint: администратор уже существует, пропускаем"
fi
fi
exec /opt/sarex/entrypoint.sh