Compare commits

..

3 Commits

Author SHA1 Message Date
emelinda
c2a9bae0db Celery включён в контуре aero
Worker был отключён патчем удаления, пока не было очередей. RabbitMQ и MinIO
развёрнуты, схема БД накачена — включаем.

Расхождения с base у celery ровно те же, что у backend: нет Kafka, MinIO
живёт внутри кластера. Аннотации инжекции несуществующих секретов убраны —
иначе vault-agent-init не отрендерит шаблон и под навсегда останется в
Init:Error. SERVER_ZITADEL_ENABLED не трогаем: в base у celery он уже False.

Миграции и создание администратора в стартовый скрипт worker'а намеренно НЕ
добавлены: схемой владеет backend, а два процесса, катящих миграции при
одновременном старте, — гонка на ровном месте.

Тег образа выравнен с backend (production_f813140d вместо production_a96dead0).
Разные версии кода у web и worker ломаются не при старте, а на конкретной
задаче, когда worker десериализует аргументы по своей версии модели.
2026-08-08 18:12:32 +03:00
emelinda
bddcc955fe Создание администратора платформы перенесено в под backend
Учётка создавалась задачей 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.
2026-08-08 18:10:19 +03:00
emelinda
ee1881e516 feat(aero): django в k3s — backend, frontend, s3-proxy и выход наружу
Первый прикладной сервис переехал из compose в кластер. Развёрнуты backend,
frontend, redis, srx-admin и s3-proxy; celery выключен до следующего шага.
Платформа доступна по https://sarex.local.lonsdaleites.ru — корень отдаёт
frontend, /api/ и /admin/ уходят в backend, /media/ в s3-proxy.

Kafka и Zitadel в контуре не развёрнуты, и приложению они не нужны:
SERVER_KAFKA_ENABLED в base уже False, а zitadel_enabled в sarex-backend
читается ровно в одном месте — update_ams_user(), которая при выключенном
флаге сразу выходит. Но аннотации инжекции этих секретов пришлось убрать:
vault-agent-init не рендерит шаблон для несуществующего секрета и роняет под
в Init:Error ещё до старта приложения.

nginx-configmap заменён целиком. В base зашиты upstream'ы на namespace pm,
workspaces и processing; nginx резолвит их при старте и падал с
"host not found in upstream". pm убран как ненужный, для остальных двух
адрес вынесен в переменную с resolver — их резолвинг откладывается до
запроса, поэтому появление сервисов позже не потребует правки конфига.
Заменять пришлось именно патчем: объект с тем же kind+name уже приходит из
base, и добавление его в resources валит сборку слоя целиком.

Секреты django в Vault: postgres, rabbitmq, minio и общие RSA-ключи JWT.
RabbitMQ и MinIO подключены под административными кредами — отдельных
пользователей приложения чарты создавать не умеют, это осознанный долг.

Задача создания суперпользователя переписана с docker compose exec на
kubectl exec. ВНИМАНИЕ: она пока НЕ РАБОТАЕТ — команда падает, вывод скрыт
no_log. Механизм проверен отдельно и исправен, значит ошибка на стороне
Django. Чтобы увидеть причину, нужно временно снять no_log.
2026-08-08 16:49:16 +03:00
7 changed files with 452 additions and 60 deletions

View File

@ -44,7 +44,8 @@ platform = "ansible-playbook deploy.yml -e aero_platform_up=true"
gen-env = "ansible-playbook deploy.yml --tags env"
# docker login в приватный реестр по ключу json_key
login = "ansible-playbook deploy.yml --tags login"
# Сгенерировать логин+пароль и создать Django-суперпользователя в backend
# Показать логин+пароль администратора платформы. Саму учётку создаёт под
# backend при старте (apps/django/aero), эта задача только печатает креды.
superuser = "ansible-playbook deploy.yml -e sarex_create_superuser=true --tags superuser"
lint = "ansible-lint"

View File

@ -83,6 +83,8 @@ registry_secrets:
- {name: regcred, namespace: postgresql}
- {name: regcred, namespace: rabbitmq}
- {name: regcred, namespace: minio}
# Образы backend и frontend лежат в приватном cr.yandex.
- {name: regcred, namespace: django}
# Поднимать стек (docker compose up -d). Требует запущенной службы docker и
# docker login в cr.yandex — по умолчанию выключено.
@ -125,6 +127,8 @@ gitea_sync_paths:
- infrastructure/postgresql
- infrastructure/rabbitmq
- infrastructure/minio
# Прикладной слой: django (backend + frontend).
- apps/django
# Рабочая копия репозитория gitea на хосте (клон, живёт между прогонами).
gitea_sync_workdir: "{{ deploy_dir }}/gitea-sync"
@ -231,6 +235,49 @@ vault_app_secrets:
processing: "{{ sarex_secrets.SAREX_PROCESSING_DB_PASSWORD }}"
bim: "{{ sarex_secrets.SAREX_BIM_DB_PASSWORD }}"
workspace: "{{ sarex_secrets.SAREX_WORKSPACE_DB_PASSWORD }}"
# --- Секреты приложения django ---------------------------------------------
# Пути и имена ключей заданы vault-шаблонами в apps/django/base/backend.yaml —
# менять их в отрыве от манифестов нельзя. Отсутствие любого из них означает
# не «пустая переменная», а падение vault-agent-init и под в Init:Error.
- path: secrets/apps/django/postgres
data:
host: postgresql.postgresql.svc.cluster.local
port: "5432"
database: "{{ sarex_postgres_django_db }}"
username: "{{ sarex_postgres_django_user }}"
password: "{{ sarex_secrets.SAREX_DJANGO_DB_PASSWORD }}"
# В k3s-инстансе RabbitMQ отдельного пользователя под django нет, поэтому
# используются админские креды и vhost по умолчанию. Это осознанный
# временный компромисс: завести отдельного пользователя нечем — чарт такой
# возможности не даёт, а руками это выпадет из GitOps.
- path: secrets/rabbitmq/apps/django
data:
username: "{{ sarex_rabbitmq_k8s_user }}"
password: "{{ sarex_secrets.SAREX_RABBITMQ_PASSWORD }}"
vhost: "/"
# Аналогично MinIO: пользователь приложения не заводится, берём root.
# buckets — список объектов: шаблон читает index (index $buckets 0) "name".
- path: secrets/minio/apps/django
data:
access_key: "{{ sarex_minio_k8s_user }}"
secret_key: "{{ sarex_secrets.SAREX_MINIO_ROOT_PASSWORD }}"
buckets:
- name: "{{ sarex_django_s3_bucket }}"
# Учётка администратора платформы. Её разбирает не приложение, а стартовый
# скрипт пода backend: он экспортирует ключи как DJANGO_SUPERUSER_* — ровно
# те имена, которые ждёт штатная manage.py createsuperuser --noinput.
# См. apps/django/aero/kustomization.yaml.
- path: secrets/apps/django/superuser
data:
username: "{{ superuser_name }}"
password: "{{ superuser_password }}"
email: "{{ superuser_name }}@{{ platform_domain }}"
# JWT-ключи RS512. Те же самые, что роль генерирует для compose-стека, —
# контур переезжает, и токены должны остаться совместимыми.
- path: secrets/vault/common/rsa_keys
data:
private_key: "{{ lookup('file', jwt_private_key_path) }}"
public_key: "{{ lookup('file', jwt_public_key_path) }}"
- path: secrets/rabbitmq/auth
data:
username: "{{ sarex_rabbitmq_k8s_user }}"
@ -246,6 +293,15 @@ vault_app_secrets:
sarex_rabbitmq_k8s_user: rabbit
sarex_minio_k8s_user: minioadmin
# Параметры подключения django к общей СУБД контура. Должны совпадать с
# соответствующей записью в contour.databases (infrastructure/postgresql/aero).
sarex_postgres_django_db: sarex_db
sarex_postgres_django_user: django
# Бакет медиафайлов django. ВНИМАНИЕ: чарт MinIO бакеты не создаёт — его нужно
# завести отдельно, иначе загрузка файлов будет падать.
sarex_django_s3_bucket: sarex-media-storage
# Сервисы приложения, поднимаемые при sarex_compose_up (depends_on тянет
# зависимости и порядок). k3s/gitea намеренно не поднимаем.
sarex_services:
@ -267,3 +323,7 @@ sarex_services:
- processing-frontend
- engine
- nginx
# Namespace прикладного слоя django в k3s. Используется задачей создания
# суперпользователя (tasks/main.yml) — backend переехал из compose в кластер.
django_namespace: django

View File

@ -92,6 +92,28 @@
no_log: true
tags: [env]
# Учётка администратора платформы. Живёт отдельно от общего цикла выше по двум
# причинам: у логина свой алфавит (Django не примет username со спецсимволами)
# и оба значения не должны попасть в .env — compose-стек их не использует,
# потребитель один, и это Vault.
#
# Генерируется всегда, а не по флагу sarex_create_superuser: значения уезжают
# в secrets/apps/django/superuser (см. vault_app_secrets), откуда их забирает
# стартовый скрипт пода backend. Без них vault-agent не отрендерит шаблон и
# под встанет в Init:Error. lookup('password') идемпотентен — на повторных
# прогонах возвращает уже сохранённое значение, а не генерирует новое.
- name: Сгенерировать/загрузить логин администратора платформы
ansible.builtin.set_fact:
superuser_name: "{{ lookup('password', secrets_store ~ '/' ~ inventory_hostname ~ '/SAREX_SUPERUSER_NAME chars=ascii_lowercase,digits length=10') }}"
no_log: true
tags: [env, superuser]
- name: Сгенерировать/загрузить пароль администратора платформы
ansible.builtin.set_fact:
superuser_password: "{{ lookup('password', secrets_store ~ '/' ~ inventory_hostname ~ '/SAREX_SUPERUSER_PASSWORD chars=ascii_letters,digits length=' ~ secret_length) }}"
no_log: true
tags: [env, superuser]
# RSA-ключи JWT (RS512): генерируем на control-node, persist в secrets_store.
- name: Сгенерировать приватный RSA-ключ JWT
ansible.builtin.command: "openssl genrsa -out {{ jwt_private_key_path }} 4096"
@ -181,54 +203,16 @@
changed_when: true
tags: [up]
# --- Django-суперпользователь: генерация логина+пароля, создание -----------
# Только по явной команде (uv run poe superuser передаёт sarex_create_superuser=true),
# чтобы обычный deploy не дёргал backend. Логин/пароль персистятся в secrets_store
# (переиспользуются при повторе — идемпотентно).
- name: Сгенерировать логин суперпользователя
ansible.builtin.set_fact:
superuser_name: "{{ lookup('password', secrets_store ~ '/' ~ inventory_hostname ~ '/SAREX_SUPERUSER_NAME chars=ascii_lowercase,digits length=10') }}"
when: sarex_create_superuser | default(false) | bool
no_log: true
tags: [superuser]
- name: Сгенерировать пароль суперпользователя
ansible.builtin.set_fact:
superuser_password: "{{ lookup('password', secrets_store ~ '/' ~ inventory_hostname ~ '/SAREX_SUPERUSER_PASSWORD chars=ascii_letters,digits length=' ~ secret_length) }}"
when: sarex_create_superuser | default(false) | bool
no_log: true
tags: [superuser]
- name: Создать/обновить Django-суперпользователя в backend
when: sarex_create_superuser | default(false) | bool
ansible.builtin.command:
argv:
- "{{ compose_cmd.split()[0] }}"
- exec
- -e
- "SU_NAME={{ superuser_name }}"
- -e
- "SU_PASS={{ superuser_password }}"
- -e
- "SU_EMAIL={{ superuser_name }}@{{ platform_domain }}"
- sarex-backend
- python
- manage.py
- shell
- -c
- >-
import os;
from django.contrib.auth import get_user_model;
U = get_user_model();
u, c = U.objects.get_or_create(username=os.environ['SU_NAME'], defaults={'email': os.environ['SU_EMAIL']});
u.is_staff = True; u.is_superuser = True; u.set_password(os.environ['SU_PASS']); u.save();
print('created' if c else 'updated')
register: superuser_result
changed_when: true
no_log: true
tags: [superuser]
- name: Показать учётные данные суперпользователя
# --- Django-суперпользователь ----------------------------------------------
# Самого создания здесь больше нет — его делает под backend при каждом старте
# (apps/django/aero/kustomization.yaml, args стартового скрипта). Так учётка
# появляется вместе с приложением, а не отдельной командой оператора, и
# переживает пересоздание пода без ручного вмешательства.
#
# Ansible остаётся владельцем самих значений: генерирует их выше (тег env) и
# кладёт в Vault. Здесь — только показать логин по явному запросу
# (uv run poe superuser передаёт sarex_create_superuser=true).
- name: Показать учётные данные администратора платформы
ansible.builtin.debug:
msg: "Django superuser → login: {{ superuser_name }} password: {{ superuser_password }}"
when: sarex_create_superuser | default(false) | bool

View File

@ -0,0 +1,183 @@
---
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
# Тег образа выравнен с 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"
# --- Администратор платформы --------------------------------------------
# Учётка приезжает из 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

View File

@ -0,0 +1,140 @@
# Копия nginx-configmap из base с правками под контур aero.
#
# Патчем это сделать нельзя: конфиг лежит одной строкой в data.nginx.conf,
# и kustomize умеет только заменить значение целиком.
#
# ЧТО ИЗМЕНЕНО ОТНОСИТЕЛЬНО base — три вещи:
#
# 1. Убран location /api/pm/. Сервис pm в контуре не нужен и не планируется.
#
# 2. Для workspaces и processing адрес вынесен в переменную и добавлен
# resolver. Причина: nginx резолвит все upstream'ы ПРИ СТАРТЕ и падает
# целиком, если хоть один не найден:
# nginx: [emerg] host not found in upstream "backend-svc.pm..."
# Из-за этого весь frontend уходил в CrashLoopBackOff, хотя не работал
# лишь один маршрут. С переменной nginx откладывает резолвинг до запроса:
# сейчас эти пути отдают 502, а когда сервисы приедут — заработают сами,
# без правки конфига и перезапуска.
#
# 3. backend-svc оставлен прямой ссылкой: он в этом же namespace и существует
# всегда, откладывать его резолвинг незачем — наоборот, ошибка в имени
# обнаружится сразу при старте.
#
# Адрес resolver'а — CoreDNS кластера. В k3s он называется kube-dns
# (имя сохранено для совместимости), namespace kube-system.
apiVersion: v1
kind: ConfigMap
metadata:
name: nginx-configmap
namespace: django
data:
nginx.conf: |
worker_processes auto;
pid /var/run/nginx.pid;
events {
use epoll;
worker_connections 1024;
}
http {
# Basic Settings
large_client_header_buffers 8 128k;
sendfile on;
tcp_nopush on;
tcp_nodelay on;
keepalive_timeout 300;
types_hash_max_size 2048;
client_max_body_size 5000M;
client_header_buffer_size 5M;
include /etc/nginx/mime.types;
default_type application/octet-stream;
# Logging Settings
access_log /var/log/nginx/access.log;
error_log /var/log/nginx/error.log;
# GZIP Settings
gzip on;
gzip_vary on;
gzip_proxied any;
gzip_comp_level 6;
gzip_buffers 16 8k;
gzip_http_version 1.1;
gzip_types text/plain text/css application/json application/javascript text/xml application/xml application/xml+rss text/javascript;
log_format main '$remote_addr - $remote_user [$time_local] "$request" '
'$status $body_bytes_sent "$http_referer" '
'"$http_user_agent" "$http_x_forwarded_for"';
# Резолвер нужен для upstream'ов, заданных переменной (см. ниже).
resolver kube-dns.kube-system.svc.cluster.local valid=30s ipv6=off;
server {
listen 80;
listen [::]:80;
root /opt/react_client/;
add_header 'Access-Control-Allow-Origin' '*' always;
add_header 'Access-Control-Allow-Methods' '*' always;
add_header 'Access-Control-Allow-Headers' '*' always;
location = /static/index.bundle.js {
add_header Cache-Control 'no-store no-cache, must-revalidate, proxy-revalidate, max-age=0';
if_modified_since off;
expires off;
}
# location /api/pm/ из base удалён: сервис pm контуру не нужен.
location ~^/(api|admin)/ {
proxy_http_version 1.1;
proxy_set_header Connection "";
proxy_set_header Host $host;
proxy_pass http://backend-svc.django.svc.cluster.local:80;
}
# workspaces появится позже — резолвим лениво, через переменную.
location ~^/workspaces-v2/(.+).js {
proxy_http_version 1.1;
proxy_set_header Connection "";
rewrite /workspaces-v2/(.+) /$1 break;
set $up_workspaces frontend-svc.workspaces.svc.cluster.local;
proxy_pass http://$up_workspaces:80;
}
location ~^/workspaces-v2/(.+)\.wasm$ {
proxy_http_version 1.1;
proxy_set_header Connection "";
rewrite ^/workspaces-v2/(.+) /$1 break;
set $up_workspaces_wasm frontend-svc.workspaces.svc.cluster.local;
proxy_pass http://$up_workspaces_wasm:80;
}
location @index {
add_header Cache-Control 'no-cache, must-revalidate, proxy-revalidate, max-age=0';
if_modified_since off;
expires off;
try_files /static/index.html =404;
}
# processing появится позже — тоже лениво.
location ~^/workflows/(.+).js {
proxy_http_version 1.1;
proxy_set_header Connection "";
rewrite /workflows/(.+) /$1 break;
set $up_processing frontend-svc.processing.svc.cluster.local;
proxy_pass http://$up_processing:80;
}
location /service-worker.js {
try_files /static/$uri @index;
}
location / {
try_files $uri @index;
}
}
}

View File

@ -1,15 +1,9 @@
# Слой 3: прикладные сервисы контура.
#
# Сейчас пуст. Тестовое приложение (apps/hello) удалено за ненадобностью: оно
# существовало, чтобы доказать работоспособность подложки — доставку Flux'ом,
# внедрение sidecar istio и получение секрета из Vault. Теперь это же
# подтверждают боевые сервисы: rabbitmq и minio поднимаются с кредами,
# приезжающими из Vault через vault-agent-injector.
#
# Слой намеренно оставлен пустым, а не удалён: следующий этап — перенос
# прикладных сервисов из compose в k3s, и они приедут именно сюда. Пустой
# resources корректен для kustomize; Flux при этом удалит из кластера то, что
# раньше применял этот слой (prune: true).
# Первым переезжает django — backend и frontend. Остальные его части
# (celery, srx-admin, s3-proxy) выключены в apps/django/aero и подключатся
# следующим шагом.
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
resources: []
resources:
- ../../../apps/django/aero

View File

@ -93,6 +93,36 @@ spec:
prefix: /
service: dashboard-kong-proxy.kubernetes-dashboard.svc.cluster.local
port: 80
# Платформа sarex: frontend в корне, backend на /api/ и /admin/.
# Порядок маршрутов значим — более специфичные префиксы идут
# первыми, иначе их перехватит правило для /.
platform:
namespace: gateway
hosts:
- sarex.local.lonsdaleites.ru
gateways:
- gateway/contour-gateway
routes:
# Медиафайлы отдаёт s3-proxy: он добавляет CORS и поддерживает
# Range-запросы, которых у S3 API «как есть» нет. Backend
# формирует ссылки вида /media/<key>, поэтому маршрут должен
# идти ПЕРЕД правилом для /.
- path:
prefix: /media/
service: s3-proxy-svc.django.svc.cluster.local
port: 80
- path:
prefix: /api/
service: backend-svc.django.svc.cluster.local
port: 80
- path:
prefix: /admin/
service: backend-svc.django.svc.cluster.local
port: 80
- path:
prefix: /
service: frontend-svc.django.svc.cluster.local
port: 80
# Management UI RabbitMQ.
rabbitmq:
namespace: gateway