iac/aero/roles/sarex_stack/defaults/main.yml
emelinda c750a08f8d Measurements пишет в общий бакет контура + перекат нагрузок в ansible
--- Бакет ---

В base у measurements имя бакета ЗАШИТО в самом vault-шаблоне:
  "buckets":["measurements"]
Из-за этого он складывал файлы в собственный бакет, тогда как
медиахранилище контура одно — sarex-media-storage: туда пишет django и
оттуда же раздаёт s3-proxy на маршруте /media/. Ссылка на файл
измерения, отданная через django, вела бы в пустоту.

Шаблон переопределён в apps/measurements/aero так, чтобы имя бакета
читалось из секрета — как уже читается endpoint. Значение задаётся в
одном месте, vault_app_secrets, и совпадает с sarex_django_s3_bucket.
Заменить пришлось аннотацию целиком: значение внутри шаблона, через
values его не перекрыть.

Из Job'а бакетов убран measurements. Это не удаление: Job умеет только
mc mb --ignore-existing, ранее созданный бакет остаётся на месте — мы
лишь перестаём его заводить.

Порядок применения безопасен: в platform.yml vault-k8s.yml идёт до
gitea-sync.yml, то есть секрет получит ключ bucket раньше, чем Flux
пересоздаст под. Иначе vault-agent-init не отрендерил бы шаблон с
несуществующим ключом и под ушёл бы в Init:Error.

--- Перекат нагрузок ---

Единственное действие, которое до сих пор делалось руками мимо роли, —
rollout restart после правки ConfigMap. Оно понадобилось дважды (порядок
set/rewrite в nginx и auth_type) и оба раза выглядело как «правка не
поехала»: Flux рапортует Applied revision, а поведение старое.

Причина в том, что у релизов universal-chart в шаблоне пода нет
контрольной суммы конфига: ConfigMap меняется, спецификация пода — нет,
Kubernetes не видит причин пересоздавать под. Декларативно это здесь не
лечится — имя ConfigMap передаётся чарту строкой, монтирует он его сам, а
configMapGenerator переименовал бы ресурс, не поправив ссылку внутри
чарта, который рендерится уже в кластере.

Добавлены tasks/reload.yml, список sarex_reload_workloads в defaults и
задача `uv run poe reload`. По умолчанию выключено: перезапуск подов
посреди обычного деплоя происходить сам не должен.
2026-08-09 11:58:06 +03:00

442 lines
25 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.

---
# Каталог развёртывания на хосте (домашка root)
deploy_dir: /root/sarex
# --- Модули ядра для istio-init --------------------------------------------
# Ноды k3s — это docker-контейнеры, своего ядра у них нет: любые операции с
# iptables внутри подов идут через ядро ХОСТА. istio-init настраивает перехват
# трафика и создаёт legacy-таблицу nat, поэтому на хосте должен быть загружен
# iptable_nat.
#
# На RED OS 8 (ядро 6.6, iptables в режиме nf_tables) он по умолчанию НЕ
# загружен: присутствуют только nft-варианты (nft_chain_nat, xt_nat). Без этих
# модулей istio-init падает с
# xtables parameter problem: iptables-restore: unable to initialize table 'nat'
# и КАЖДЫЙ под в namespace с istio-injection=enabled навсегда остаётся в
# Init:Error — именно так встал kubernetes-dashboard.
aero_kernel_modules:
- iptable_nat
- iptable_mangle
- iptable_raw
- xt_REDIRECT
- xt_owner
# Источник файлов — корень репозитория iac (playbook лежит в aero/)
deploy_src_root: "{{ playbook_dir }}/.."
# Домены верхнего nginx (попадают в .env и в SAN сертификата)
platform_domain: sarex.local.lonsdaleites.ru
admin_domain: admin.sarex.local.lonsdaleites.ru
minio_domain: minio.sarex.local.lonsdaleites.ru
# Самоподписанный TLS-сертификат
cert_days: 365
cert_dir: "{{ deploy_dir }}/nginx/certs"
# Переменные-пароли: им генерируются стойкие значения взамен слабых дефолтов.
sarex_secret_vars:
- K3S_TOKEN
- GITEA_ADMIN_PASSWORD
- SAREX_POSTGRES_PASSWORD
- SAREX_DJANGO_DB_PASSWORD
- SAREX_PROCESSING_DB_PASSWORD
- SAREX_BIM_DB_PASSWORD
- SAREX_WORKSPACE_DB_PASSWORD
- SAREX_RABBITMQ_PASSWORD
- SAREX_MINIO_ROOT_PASSWORD
- SAREX_MINIO_APP_PASSWORD
secret_length: 24
# Хранилище секретов на control-node (persist + переиспользование). Gitignored.
secrets_store: "{{ playbook_dir }}/.secrets"
# RSA-ключи JWT backend (RS512). Генерируются на control-node, persist в secrets_store.
jwt_private_key_path: "{{ secrets_store }}/{{ inventory_hostname }}/jwt_private.pem"
jwt_public_key_path: "{{ secrets_store }}/{{ inventory_hostname }}/jwt_public.pem"
# --- Приватный реестр образов и чартов (cr.yandex, аутентификация json_key) ---
registry_host: cr.yandex
registry_username: json_key
# Путь к ключу сервисного аккаунта НА CONTROL-NODE (ansible запускается из WSL,
# поэтому диск C: виден как /mnt/c). Это машинно-зависимое значение —
# переопределяйте, не редактируя роль:
# uv run poe platform -- -e registry_key_src=/path/to/authorized_key.json
registry_key_src: "/mnt/c/Users/user/Downloads/Telegram Desktop/authorized_key.json"
# Куда ключ кладётся на целевом хосте на время провижининга (удаляется после).
registry_key_dest: "{{ deploy_dir }}/registry-key.json"
# Секреты в кластере. Namespace создаётся вместе с секретом — он нужен раньше,
# чем чарт создаст его сам. Список пополняется по мере переезда компонентов.
# * yc-cr-auth (flux-system) — Flux тянет им OCI-ЧАРТЫ. Обязателен.
# * regcred — imagePullSecrets подов в namespace потребителя.
registry_secrets:
- {name: yc-cr-auth, namespace: flux-system}
- {name: regcred, namespace: cert-manager}
- {name: regcred, namespace: istio-system}
- {name: regcred, namespace: kubernetes-dashboard}
# Образы vault-k8s и vault-agent для injector'а тоже лежат в cr.yandex.
- {name: regcred, namespace: vault}
- {name: regcred, namespace: local-path-provisioner}
- {name: regcred, namespace: postgresql}
- {name: regcred, namespace: rabbitmq}
- {name: regcred, namespace: minio}
# Образы backend и frontend лежат в приватном cr.yandex.
- {name: regcred, namespace: django}
- {name: regcred, namespace: measurements}
- {name: regcred, namespace: processing}
- {name: regcred, namespace: workspaces}
# Поднимать стек (docker compose up -d). Требует запущенной службы docker и
# docker login в cr.yandex — по умолчанию выключено.
sarex_compose_up: false
compose_cmd: "docker compose"
# --- Перекат нагрузок после правки конфигурации -----------------------------
# Выключено по умолчанию: перезапуск подов посреди обычного деплоя — не то, что
# должно происходить само. Включается задачей `uv run poe reload`.
#
# Список — это нагрузки, конфигурация которых лежит в ConfigMap и потому НЕ
# подхватывается без пересоздания пода: спецификация пода не меняется, значит
# Kubernetes не видит причин его перекатывать. Всё остальное перекатывается
# само — правка envs или podAnnotations меняет шаблон пода.
#
# Добавляешь ConfigMap, который читает приложение, — добавь нагрузку сюда,
# иначе правка будет уезжать в кластер и не применяться, а выглядеть это будет
# как «изменение не сработало».
sarex_reload: false
sarex_reload_timeout: 240s
sarex_reload_workloads:
# nginx главного фронтенда: apps/django/aero/nginx-configmap.yaml. Здесь
# маршруты на микрофронтенды workspaces и workflows и подмена
# /static/config.json, которой выключается Zitadel.
- namespace: django
deployment: frontend
why: nginx-configmap
# --- GitOps-подложка контура (k3s + gitea + vault + flux) -------------------
# Параметры gitea и Flux. Попадают в .env (их читает docker-compose) — менять
# нужно здесь, а не в .env.example, иначе перезапишется при следующем деплое.
gitea_admin_user: sarex
gitea_admin_email: "{{ gitea_admin_user }}@{{ platform_domain }}"
gitea_org: infra
gitea_repo: iac
# Ветка и путь, за которыми следит Flux внутри репозитория в gitea.
aero_git_branch: master
aero_flux_path: clusters/aero
# --- Синхронизация репозитория в gitea --------------------------------------
# Содержимое едет с control-node на хост архивом и коммитится в gitea оттуда:
# наружу gitea не публикуется дальше самого хоста, а git на control-node может
# быть не настроен на работу с контуром.
#
# Список — это ЗАМЫКАНИЕ ссылок clusters/aero, а не весь репозиторий:
# clusters/aero/controllers ссылается на ../../../infrastructure/<компонент>/aero,
# каждый такой overlay тянет только ../base. Каталоги ansible-роли, snapshots и
# docs кластеру не нужны и не синхронизируются.
#
# ВАЖНО: добавил компонент в clusters/aero/controllers — добавь его сюда, иначе
# kustomize в кластере упадёт на отсутствующем каталоге.
gitea_sync_paths:
- clusters/aero
- infrastructure/cert-manager
- infrastructure/dashboard
- infrastructure/istio-base
- infrastructure/istio-config
- infrastructure/istio-gateway
- infrastructure/istio-pilot
- infrastructure/vault-injector
- infrastructure/local-path-provisioner
- infrastructure/postgresql
- infrastructure/rabbitmq
- infrastructure/minio
# Прикладной слой.
- apps/django
- apps/measurements
- apps/processing
- apps/workspaces
# Рабочая копия репозитория gitea на хосте (клон, живёт между прогонами).
gitea_sync_workdir: "{{ deploy_dir }}/gitea-sync"
gitea_sync_archive_remote: "{{ deploy_dir }}/gitea-sync.tar.gz"
# Адрес gitea с точки зрения ХОСТА: контейнер публикует 3000 наружу.
gitea_http_addr: "http://127.0.0.1:3000"
# URL с кредами для push'а. sarex_secrets заполняется в tasks/main.yml, поэтому
# выражение раскрывается лениво — к моменту использования пароль уже есть.
gitea_sync_url: >-
http://{{ gitea_admin_user }}:{{ sarex_secrets.GITEA_ADMIN_PASSWORD | urlencode }}@127.0.0.1:3000/{{ gitea_org }}/{{ gitea_repo }}.git
# Временный архив на control-node (удаляется после синхронизации).
gitea_sync_archive_local: /tmp/aero-gitea-sync.tar.gz
# Автор коммитов синхронизации.
gitea_commit_name: aero-sync
gitea_commit_email: "aero-sync@{{ platform_domain }}"
# Поднимается отдельно от прикладного стека: образы публичные, docker login в
# cr.yandex не нужен. По умолчанию выключено, включает `uv run poe platform`.
aero_platform_up: false
# Волна 1: долгоживущие сервисы, которым нужно успеть стать healthy.
# k3s-server при первом старте создаёт ./k3s/kubeconfig.yaml — его ждёт волна 2.
aero_platform_services_wave1:
- k3s-server
- k3s-worker-1
- k3s-worker-2
- gitea
# Волна 2: провижининг. gitea-init и flux-k8s-init одноразовые (restart: "no"),
# vault-init живёт постоянно и распечатывает Vault после ребута хоста.
#
# flux-bootstrap ВЫНЕСЕН в волну 3 намеренно: между волнами Vault получает
# auth/kubernetes, а репозиторий контура синхронизируется в gitea. Запусти
# bootstrap раньше — Flux привязался бы к репозиторию, в котором ещё нет ни
# clusters/aero, ни infrastructure, и первая же реконсиляция упала бы.
aero_platform_services_wave2:
- gitea-init
- flux-k8s-init
- vault
- vault-init
# Волна 3: bootstrap FluxCD. Запускается после наполнения gitea.
aero_platform_services_wave3:
- flux-bootstrap
# Ноды k3s. Каждой нужен свой каталог PVC-хранилища на хосте: провижинер
# local-path node-local, и PV навсегда привязывается к ноде через nodeAffinity.
aero_k3s_nodes:
- server
- worker-1
- worker-2
# Сколько ждать появления kubeconfig от k3s-server, секунд.
aero_kubeconfig_timeout: 180
# --- Интеграция Vault ↔ k3s (метод auth/kubernetes) -------------------------
# Статический IP контейнера vault в compose-сети. ДОЛЖЕН совпадать с
# ipv4_address сервиса vault в docker-compose.yaml: отсюда значение
# подставляется в Endpoints DNS-моста (k3s/manifests/vault/bridge-vault.yaml).
vault_bridge_ip: 172.28.0.13
# Адрес apiserver, по которому Vault валидирует токены подов. Vault живёт в
# compose, поэтому имя резолвится compose-DNS, а не CoreDNS кластера.
vault_k8s_host: "https://k3s-server:6443"
# Политика чтения kv-v2 на пути secrets/ — том же, что включает vault-init.
vault_apps_policy: apps-read
# TTL выдаваемого агенту токена по умолчанию.
vault_role_ttl: 24h
# Роли auth/kubernetes. Имя роли — то, что приложение указывает в аннотации
# vault.hashicorp.com/role, namespace — где живут его поды. Пополняется по мере
# переезда приложений из compose в k3s.
vault_k8s_roles:
- {name: postgresql, namespaces: [postgresql]}
- {name: rabbitmq, namespaces: [rabbitmq]}
- {name: minio, namespaces: [minio]}
- {name: django, namespaces: [django]}
- {name: processing, namespaces: [processing]}
- {name: measurements, namespaces: [measurements]}
- {name: workspaces, namespaces: [workspaces]}
# Боевые креды инфраструктурных сервисов. Пути и имена ключей заданы чартами
# (см. vaultRoot у minio и auth.vault у rabbitmq в infrastructure/*/aero) —
# менять их в отрыве от values нельзя.
#
# Пароли не хранятся в репозитории: берутся из sarex_secrets, который
# генерирует и персистит в aero/.secrets задача "Сгенерировать/загрузить пароли".
vault_app_secrets:
# Пароль администратора PostgreSQL. Имя ключа задано чартом
# (contour.vault.secretKey) — менять в отрыве от values нельзя.
- path: secrets/postgresql/admin
data:
postgres-password: "{{ sarex_secrets.SAREX_POSTGRES_PASSWORD }}"
# Пароли владельцев баз. Ключи сопоставляются с passwordKey каждой базы в
# contour.databases (см. infrastructure/postgresql/aero). Добавляешь базу —
# добавь сюда ключ, иначе бутстрап не сможет задать роли пароль.
- path: secrets/postgresql/users
data:
django: "{{ sarex_secrets.SAREX_DJANGO_DB_PASSWORD }}"
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 }}"
# --- Секреты приложения processing ------------------------------------------
# Пути и имена ключей заданы vault-шаблонами в apps/processing/base —
# менять их в отрыве от манифестов нельзя.
#
# Владелец workflow_db в контуре — роль processing (см. contour.databases в
# infrastructure/postgresql/aero). В yc-k8s-test та же база принадлежит роли
# workflow; здесь имя другое, и пароль берётся из соответствующего ключа
# secrets/postgresql/users.
- path: secrets/apps/processing/postgres
data:
host: postgresql.postgresql.svc.cluster.local
port: "5432"
database: "{{ sarex_postgres_processing_db }}"
username: "{{ sarex_postgres_processing_user }}"
password: "{{ sarex_secrets.SAREX_PROCESSING_DB_PASSWORD }}"
# Как и у django — админские креды вместо отдельного пользователя.
# Хост и порт брокера чарт не читает отсюда: они захардкожены внутри
# vault-шаблона в apps/processing/base/engine-low.yaml.
- path: secrets/rabbitmq/apps/processing
data:
username: "{{ sarex_rabbitmq_k8s_user }}"
password: "{{ sarex_secrets.SAREX_RABBITMQ_PASSWORD }}"
# S3 для engine и api. Формат — не пары ключ-значение, а ЦЕЛЫЙ JSON-файл:
# переменная S3_SERVICE_ACCOUNT содержит путь к нему, а не сами креды.
# Набор ключей повторяет engine/yc-s3-service-account.json из compose-стека,
# который точно так же смотрел в MinIO.
- path: secrets/minio/apps/processing
data:
host: http://minio.minio.svc.cluster.local:9000
bucket: "{{ sarex_django_s3_bucket }}"
access_key_id: "{{ sarex_minio_k8s_user }}"
secret_access_key: "{{ sarex_secrets.SAREX_MINIO_ROOT_PASSWORD }}"
region: us-east-1
# --- Секреты приложения measurements ---------------------------------------
# Структура задана vault-шаблоном в apps/measurements/base/backend.yaml: он
# собирает из этих ключей одну переменную S3_JSON_SETTINGS и читает endpoint
# ВЛОЖЕННЫМ ключом .Data.data.client.endpoint — отсюда лишний уровень client.
#
# Бакет — ОБЩЕЕ медиахранилище контура, то же, куда пишет django и откуда
# раздаёт s3-proxy на маршруте /media/. В base у measurements имя бакета
# зашито в самом vault-шаблоне ("buckets":["measurements"]), из-за чего он
# складывал файлы отдельно и ссылка, отданная через django, вела бы в пустоту.
# Шаблон переопределён в apps/measurements/aero, чтобы имя читалось отсюда.
#
# Отдельного пользователя MinIO нет — тот же временный компромисс, что и у
# django: берём root-креды.
- path: secrets/minio/apps/measurements
data:
client:
endpoint: http://minio.minio.svc.cluster.local:9000
bucket: "{{ sarex_django_s3_bucket }}"
access_key: "{{ sarex_minio_k8s_user }}"
secret_key: "{{ sarex_secrets.SAREX_MINIO_ROOT_PASSWORD }}"
# Учётка администратора платформы. Её разбирает не приложение, а стартовый
# скрипт пода 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 }}"
# --- Секреты приложения workspaces ------------------------------------------
# Пути и имена ключей заданы vault-шаблонами в apps/workspaces/base/backend.yaml.
- path: secrets/apps/workspaces/postgres
data:
host: postgresql.postgresql.svc.cluster.local
port: "5432"
database: "{{ sarex_postgres_workspaces_db }}"
username: "{{ sarex_postgres_workspaces_user }}"
password: "{{ sarex_secrets.SAREX_WORKSPACE_DB_PASSWORD }}"
# ЭТО НЕ ТОКЕН ZITADEL, вопреки имени пути и соседнему ключу.
# workspaces читает отсюда ключ key и подставляет его в DJANGO_BASIC_AUTH —
# то есть содержимое обязано быть base64("логин:пароль") администратора
# django, той самой учётки из secrets/apps/django/superuser выше. Из этого же
# пути django в base читает django_zitadel_access_token, но в контуре Zitadel
# нет и та аннотация удалена в apps/django/aero — здесь удалять её НЕ нужно.
- path: secrets/vault/common/django_auth
data:
key: "{{ (superuser_name ~ ':' ~ superuser_password) | b64encode }}"
# 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 }}"
password: "{{ sarex_secrets.SAREX_RABBITMQ_PASSWORD }}"
- path: secrets/minio/admin
data:
rootUser: "{{ sarex_minio_k8s_user }}"
rootPassword: "{{ sarex_secrets.SAREX_MINIO_ROOT_PASSWORD }}"
# Логины сервисов в k3s. Совпадают со значениями в .env.example для
# одноимённых сервисов compose; пароли к ним — в sarex_secrets (те же
# переменные: контур переезжает в k3s, плодить вторую пару кредов незачем).
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
# То же для processing. Должно совпадать с записью workflow_db в
# contour.databases (infrastructure/postgresql/aero).
sarex_postgres_processing_db: workflow_db
sarex_postgres_processing_user: processing
# То же для workspaces. Должно совпадать с записью workspace_db в
# contour.databases (infrastructure/postgresql/aero). Имена в единственном
# числе — так они заведены в бутстрапе СУБД, namespace же во множественном.
sarex_postgres_workspaces_db: workspace_db
sarex_postgres_workspaces_user: workspace
# Бакет медиафайлов django. ВНИМАНИЕ: чарт MinIO бакеты не создаёт — его нужно
# завести отдельно, иначе загрузка файлов будет падать.
sarex_django_s3_bucket: sarex-media-storage
# Сервисы приложения, поднимаемые при sarex_compose_up (depends_on тянет
# зависимости и порядок). k3s/gitea намеренно не поднимаем.
sarex_services:
# k3s + провижининг DNS-моста (нужны engine'у до старта)
- k3s-server
- processing-k8s-init
- postgres
- postgres-init
- redis
- rabbitmq
- minio
- minio-init
- measurements
- s3-proxy
- backend
- celery
- frontend
- processing-api
- processing-frontend
- engine
- nginx
# Namespace прикладного слоя django в k3s. Используется задачей создания
# суперпользователя (tasks/main.yml) — backend переехал из compose в кластер.
django_namespace: django