Учётка создавалась задачей ansible через kubectl exec — по явной команде оператора и в обход GitOps. Теперь это делает сам под при каждом старте: стартовый скрипт подхватывает креды из Vault и вызывает штатную createsuperuser --noinput. Идемпотентность обеспечена ветвлением, а не подавлением ошибки: на уже существующем логине Django возвращает "That username is already taken" с ненулевым кодом, что под set -e из command базы уронило бы под на втором запуске. Ошибка ожидаема и гасится веткой else. Заодно в стартовый скрипт добавлен migrate. В entrypoint.sh образа он уже есть, но запущен без set -e — его падение не мешает uwsgi стартовать. Именно так контур несколько часов отдавал 200 на /admin/ с пустой схемой: backend не мог аутентифицироваться в СУБД, миграции падали, приложение работало. Здесь команда идёт под set -e, поэтому отказ виден сразу. Ansible остаётся владельцем значений: генерирует логин и пароль (теперь безусловно, а не по флагу — их ждёт vault-agent) и кладёт в secrets/apps/django/superuser. Задача poe superuser печатает креды. Убраны временные диагностические задачи с ignore_errors, добавленные при разборе отказа createsuperuser.
144 lines
9.1 KiB
YAML
144 lines
9.1 KiB
YAML
---
|
||
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 выключен: очереди подключим вместе с остальными фоновыми задачами.
|
||
# s3-proxy ВКЛЮЧЁН — он отдаёт медиафайлы браузеру с CORS и Range, а бакет
|
||
# уже создан, так что проверять его есть на чём.
|
||
- target: {kind: HelmRelease, name: celery}
|
||
patch: |
|
||
$patch: delete
|
||
apiVersion: helm.toolkit.fluxcd.io/v2
|
||
kind: HelmRelease
|
||
metadata:
|
||
name: celery
|
||
namespace: django
|
||
|
||
# --- 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"
|
||
|
||
# --- Администратор платформы --------------------------------------------
|
||
# Учётка приезжает из 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
|