From 51b912079739b3706e40ae5895806bd61c5b634e Mon Sep 17 00:00:00 2001 From: emelinda Date: Sat, 8 Aug 2026 20:41:23 +0300 Subject: [PATCH] =?UTF-8?q?Measurements=20=D0=B2=20=D0=BA=D0=BE=D0=BD?= =?UTF-8?q?=D1=82=D1=83=D1=80=D0=B5=20aero=20+=20=D0=B4=D0=B5=D0=BA=D0=BB?= =?UTF-8?q?=D0=B0=D1=80=D0=B0=D1=82=D0=B8=D0=B2=D0=BD=D0=BE=D0=B5=20=D0=B7?= =?UTF-8?q?=D0=B0=D0=B2=D0=B5=D0=B4=D0=B5=D0=BD=D0=B8=D0=B5=20=D0=B1=D0=B0?= =?UTF-8?q?=D0=BA=D0=B5=D1=82=D0=BE=D0=B2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Django обращается к сервису измерений при MEASUREMENTS_USE_MEASUREMENTS=1 (значение по умолчанию в base), так что без него часть API отвечала ошибкой. Сервис переименован в measurements-service, как в brusnika-*. В base он называется measurements-svc, а django ходит на measurements-service — переименовать сервис дешевле, чем патчить переменную в двух релизах django. Бакеты теперь заводит Job в infrastructure/minio/aero, а не команда руками. Чарт MinIO этого не умеет: форк не принимает ни buckets, ни provisioning. Так бакет django был создан императивно и не пережил бы пересоздание контура — развернув всё с нуля, получили бы работающий MinIO и приложения, падающие на отсутствующем бакете. Measurements сломался бы на этом сразу: имя бакета зашито в его vault-шаблоне. Идемпотентность даёт mc mb --ignore-existing. Сайдкар istio у Job отключён: контейнер задачи выходит, envoy продолжает работать, и под навсегда остаётся Running. --- aero/roles/sarex_stack/defaults/main.yml | 21 +++++- apps/measurements/aero/kustomization.yaml | 26 +++++++ clusters/aero/apps/kustomization.yaml | 7 +- infrastructure/minio/aero/buckets-job.yaml | 72 ++++++++++++++++++++ infrastructure/minio/aero/kustomization.yaml | 2 + 5 files changed, 123 insertions(+), 5 deletions(-) create mode 100644 apps/measurements/aero/kustomization.yaml create mode 100644 infrastructure/minio/aero/buckets-job.yaml diff --git a/aero/roles/sarex_stack/defaults/main.yml b/aero/roles/sarex_stack/defaults/main.yml index 2b3010e..bb35f4d 100644 --- a/aero/roles/sarex_stack/defaults/main.yml +++ b/aero/roles/sarex_stack/defaults/main.yml @@ -85,6 +85,7 @@ registry_secrets: - {name: regcred, namespace: minio} # Образы backend и frontend лежат в приватном cr.yandex. - {name: regcred, namespace: django} + - {name: regcred, namespace: measurements} # Поднимать стек (docker compose up -d). Требует запущенной службы docker и # docker login в cr.yandex — по умолчанию выключено. @@ -127,8 +128,9 @@ gitea_sync_paths: - infrastructure/postgresql - infrastructure/rabbitmq - infrastructure/minio - # Прикладной слой: django (backend + frontend). + # Прикладной слой. - apps/django + - apps/measurements # Рабочая копия репозитория gitea на хосте (клон, живёт между прогонами). gitea_sync_workdir: "{{ deploy_dir }}/gitea-sync" @@ -213,6 +215,7 @@ vault_k8s_roles: - {name: minio, namespaces: [minio]} - {name: django, namespaces: [django]} - {name: processing, namespaces: [processing]} + - {name: measurements, namespaces: [measurements]} # Боевые креды инфраструктурных сервисов. Пути и имена ключей заданы чартами # (см. vaultRoot у minio и auth.vault у rabbitmq в infrastructure/*/aero) — @@ -263,6 +266,22 @@ vault_app_secrets: secret_key: "{{ sarex_secrets.SAREX_MINIO_ROOT_PASSWORD }}" buckets: - name: "{{ sarex_django_s3_bucket }}" + # --- Секреты приложения measurements --------------------------------------- + # Структура задана vault-шаблоном в apps/measurements/base/backend.yaml: он + # собирает из этих ключей одну переменную S3_JSON_SETTINGS и читает endpoint + # ВЛОЖЕННЫМ ключом .Data.data.client.endpoint — отсюда лишний уровень client. + # + # Бакет measurements зашит в том же шаблоне и заводится Job'ом + # infrastructure/minio/aero/buckets-job.yaml. + # + # Отдельного пользователя MinIO нет — тот же временный компромисс, что и у + # django: берём root-креды. + - path: secrets/minio/apps/measurements + data: + client: + endpoint: http://minio.minio.svc.cluster.local:9000 + access_key: "{{ sarex_minio_k8s_user }}" + secret_key: "{{ sarex_secrets.SAREX_MINIO_ROOT_PASSWORD }}" # Учётка администратора платформы. Её разбирает не приложение, а стартовый # скрипт пода backend: он экспортирует ключи как DJANGO_SUPERUSER_* — ровно # те имена, которые ждёт штатная manage.py createsuperuser --noinput. diff --git a/apps/measurements/aero/kustomization.yaml b/apps/measurements/aero/kustomization.yaml new file mode 100644 index 0000000..a666f7e --- /dev/null +++ b/apps/measurements/aero/kustomization.yaml @@ -0,0 +1,26 @@ +--- +apiVersion: kustomize.config.k8s.io/v1beta1 +kind: Kustomization +namespace: measurements +resources: + - ../base + +patches: + - target: {kind: HelmRelease, name: measurements} + patch: | + # Имя сервиса приводим к measurements-service, как в brusnika-*. + # + # В base сервис называется measurements-svc, а django ходит на + # MEASUREMENTS_HOST=http://measurements-service.measurements.svc.cluster.local:8000/api + # (apps/django/base/backend.yaml). Переименовать сервис дешевле, чем + # патчить переменную в двух релизах django (backend и celery), и заодно + # совпадает с тем, как это сделано в остальных кластерах. + - op: replace + path: /spec/values/services/backend/service/name/_default + value: measurements-service + + # S3 внутри кластера. В base endpoint приезжает из самого секрета + # (.Data.data.client.endpoint), поэтому шаблон править не нужно — адрес + # задаёт ansible в vault_app_secrets. Бакет measurements заводит + # infrastructure/minio/aero/buckets-job.yaml: имя зашито в шаблоне + # аннотации, и без готового бакета приложение не поднимется. diff --git a/clusters/aero/apps/kustomization.yaml b/clusters/aero/apps/kustomization.yaml index 7b24061..9d180be 100644 --- a/clusters/aero/apps/kustomization.yaml +++ b/clusters/aero/apps/kustomization.yaml @@ -1,9 +1,8 @@ # Слой 3: прикладные сервисы контура. -# -# Первым переезжает django — backend и frontend. Остальные его части -# (celery, srx-admin, s3-proxy) выключены в apps/django/aero и подключатся -# следующим шагом. apiVersion: kustomize.config.k8s.io/v1beta1 kind: Kustomization resources: - ../../../apps/django/aero + # Сервис измерений. Django обращается к нему при MEASUREMENTS_USE_MEASUREMENTS=1 + # (значение по умолчанию в base), поэтому без него часть API отвечает ошибкой. + - ../../../apps/measurements/aero diff --git a/infrastructure/minio/aero/buckets-job.yaml b/infrastructure/minio/aero/buckets-job.yaml new file mode 100644 index 0000000..1950af8 --- /dev/null +++ b/infrastructure/minio/aero/buckets-job.yaml @@ -0,0 +1,72 @@ +--- +# Заведение бакетов. Чарт MinIO этого не умеет — форк не принимает ни buckets, +# ни provisioning, — а приложения ждут бакеты готовыми: django падает на +# загрузке файлов, measurements — на старте, потому что имя бакета зашито в +# его vault-шаблоне. +# +# До этого Job'а бакеты заводились руками через mc. Такой бакет не переживает +# пересоздание контура и не виден в репозитории: развернув всё с нуля, ты +# получишь работающий MinIO и приложения, падающие на отсутствующем бакете. +# +# Идемпотентность даёт mc mb --ignore-existing: Job гоняется при каждом +# применении релиза (ttl удаляет завершившийся), повторный запуск на готовых +# бакетах отрабатывает вхолостую и завершается нулём. +apiVersion: batch/v1 +kind: Job +metadata: + name: minio-buckets + namespace: minio + annotations: + # Пересоздавать Job при каждой синхронизации: спецификация Job неизменяема, + # и без пересоздания kustomize не смог бы применить правку списка бакетов. + kustomize.toolkit.fluxcd.io/force: "true" +spec: + # Job живёт час после завершения и удаляется. Не 0: если он упадёт, логи + # должны остаться доступными для разбора. + ttlSecondsAfterFinished: 3600 + backoffLimit: 6 + template: + metadata: + annotations: + # Sidecar istio не даёт Job завершиться: контейнер задачи выходит, а + # envoy продолжает работать, и под навсегда остаётся Running. + sidecar.istio.io/inject: "false" + vault.hashicorp.com/agent-inject: "true" + vault.hashicorp.com/agent-init-first: "true" + vault.hashicorp.com/agent-pre-populate-only: "true" + vault.hashicorp.com/auth-path: auth/kubernetes + vault.hashicorp.com/role: minio + vault.hashicorp.com/agent-inject-secret-admin: secrets/data/minio/admin + vault.hashicorp.com/agent-inject-template-admin: |- + {{- with secret "secrets/data/minio/admin" -}} + MINIO_ROOT_USER={{ index .Data.data "rootUser" }} + MINIO_ROOT_PASSWORD={{ index .Data.data "rootPassword" }} + {{- end -}} + spec: + restartPolicy: OnFailure + serviceAccountName: minio-sa + imagePullSecrets: + - name: regcred + containers: + - name: mc + # Тот же образ, которым бакеты заводил compose-стек (SAREX_MC_IMAGE + # в docker-compose.yaml). Тег фиксированный: latest в приватном + # реестре не публикуется. + image: cr.yandex/crp3ccidau046kdj8g9q/mc:250724 + command: ["/bin/sh", "-ec"] + args: + - | + . /vault/secrets/admin + + # Ждём готовности API: Job стартует вместе с релизом и обгоняет + # готовность самого MinIO. + until mc alias set contour http://minio.minio.svc.cluster.local:9000 \ + "$MINIO_ROOT_USER" "$MINIO_ROOT_PASSWORD" >/dev/null 2>&1; do + echo "жду MinIO..." + sleep 5 + done + + for b in sarex-media-storage measurements; do + mc mb --ignore-existing "contour/$b" + echo "бакет $b готов" + done diff --git a/infrastructure/minio/aero/kustomization.yaml b/infrastructure/minio/aero/kustomization.yaml index a747147..285f576 100644 --- a/infrastructure/minio/aero/kustomization.yaml +++ b/infrastructure/minio/aero/kustomization.yaml @@ -3,6 +3,8 @@ apiVersion: kustomize.config.k8s.io/v1beta1 kind: Kustomization resources: - ../base + # Заведение бакетов: чарт этого не умеет, см. шапку файла. + - buckets-job.yaml patches: - path: minio.yaml target: