Add JWT key generation for backend, update provisioning and deployment tasks, enhance docker-compose with protected Redis, and refine Ansible workflow with new install sequence and application stack management

This commit is contained in:
emelinda 2026-07-23 19:09:22 +03:00
parent 0f9969ddad
commit e4953042c0
6 changed files with 97 additions and 25 deletions

View File

@ -15,6 +15,10 @@ SAREX_DJANGO_DB_USER=django
SAREX_DJANGO_DB_PASSWORD=django-secret SAREX_DJANGO_DB_PASSWORD=django-secret
# Расширения PostgreSQL для базы django (создаёт postgres-init суперпользователем) # Расширения PostgreSQL для базы django (создаёт postgres-init суперпользователем)
SAREX_DJANGO_DB_EXTENSIONS=ltree SAREX_DJANGO_DB_EXTENSIONS=ltree
# JWT-ключи backend (RS512): PEM в одну строку с литеральными \n (backend разворачивает).
# Генерируются автоматически в .secrets ролью sarex_stack; здесь — плейсхолдеры.
SAREX_JWT_PRIVATE_KEY=changeme
SAREX_JWT_PUBLIC_KEY=changeme
SAREX_PROCESSING_DB_USER=processing SAREX_PROCESSING_DB_USER=processing
SAREX_PROCESSING_DB_PASSWORD=processing-secret SAREX_PROCESSING_DB_PASSWORD=processing-secret
SAREX_BIM_DB_USER=bim SAREX_BIM_DB_USER=bim

View File

@ -41,16 +41,37 @@ chmod 600 ~/.ssh/id_ed25519
## Запуск ## Запуск
Из WSL, в каталоге проекта (`/mnt/c/.../iac/aero`): Из WSL, в каталоге проекта (`/mnt/c/.../iac/aero`).
### Установка с чистого листа — одной командой
```bash ```bash
uv sync # окружение (uv сам поставит нужный Python) uv run poe install
uv run poe ping # проверка SSH + sudo
uv run poe check # dry-run (--check --diff), ничего не меняет
uv run poe play # боевой прогон роли
``` ```
Одной командой всё сразу: `uv run poe up` (`sync` → `galaxy``play`). Последовательность: `sync``galaxy`**`provision`** (базовые пакеты + docker
и запуск службы) → **`stack`** (деплой конфигурации + `docker compose up -d`
сервисов приложения) → **`superuser`** (генерация и создание Django-админа).
Перед запуском впишите хосты в [inventory.ini](inventory.ini) и убедитесь, что
на control-node есть SSH-ключ и `~/.ssh/local/authorized_key.json` для входа в
реестр.
### Пошагово / отдельные команды
| Команда | Действие |
| --- | --- |
| `uv run poe ping` | проверка SSH + sudo |
| `uv run poe provision` | базовые пакеты + docker/containerd + запуск службы |
| `uv run poe deploy` | деплой конфигурации: файлы, `.env`, сертификаты, `docker login` |
| `uv run poe stack` | деплой + поднять стек (`docker compose up -d`) |
| `uv run poe gen-env` | только сгенерировать пароли и `.env` |
| `uv run poe login` | только `docker login` в реестр по ключу |
| `uv run poe superuser` | сгенерировать логин+пароль и создать Django-суперпользователя |
| `uv run poe check` / `deploy-check` | dry-run (`--check --diff`) |
Поднимаемые сервисы задаются в `sarex_services` (роль `sarex_stack`) —
`postgres(+init)`, `redis`, `rabbitmq`, `minio(+init)`, `measurements`,
`backend`, `celery`, `frontend`, `nginx` (k3s/gitea/processing не поднимаются).
> `ANSIBLE_CONFIG` и `UV_LINK_MODE` заданы в `[tool.poe.env]` — на диске `C:` > `ANSIBLE_CONFIG` и `UV_LINK_MODE` заданы в `[tool.poe.env]` — на диске `C:`
> (через `/mnt/c`) права `0777`, и ansible игнорирует `ansible.cfg` в > (через `/mnt/c`) права `0777`, и ansible игнорирует `ansible.cfg` в
@ -58,10 +79,12 @@ uv run poe play # боевой прогон роли
### Развёртывание sarex-стека (роль `sarex_stack`) ### Развёртывание sarex-стека (роль `sarex_stack`)
Копирует `docker-compose.yaml`, `nginx/` и `postgres/initdb` в `/root/sarex`, Копирует `docker-compose.yaml`, `nginx/templates` и `backend/uwsgi.ini` в
рендерит `.env` из `.env.example` со **сгенерированными паролями** и доменами `/root/sarex`, рендерит `.env` из `.env.example` со **сгенерированными паролями**
`*.sarex.local.lonsdaleites.ru`, генерирует самоподписанный TLS-сертификат на и доменами `*.sarex.local.lonsdaleites.ru`, генерирует самоподписанный
все три домена (`nginx/certs/selfsigned.{crt,key}`). TLS-сертификат на все три домена (`nginx/certs/selfsigned.{crt,key}`), логинится
в реестр и (при `sarex_compose_up=true`) поднимает сервисы приложения.
Роли/базы и MinIO-бакеты создаются одноразовыми `postgres-init`/`minio-init`.
```bash ```bash
uv run ansible-playbook deploy.yml --check --diff # dry-run uv run ansible-playbook deploy.yml --check --diff # dry-run

View File

@ -28,22 +28,26 @@ UV_LINK_MODE = "copy"
sync = "uv sync" sync = "uv sync"
galaxy = "ansible-galaxy collection install -r requirements.yml" galaxy = "ansible-galaxy collection install -r requirements.yml"
ping = "ansible docker_hosts -m ping" ping = "ansible docker_hosts -m ping"
play = "ansible-playbook site.yml" # Провижининг хоста: базовые пакеты + docker/containerd + запуск службы
provision = "ansible-playbook site.yml"
check = "ansible-playbook site.yml --check --diff" check = "ansible-playbook site.yml --check --diff"
# Деплой конфигурации: файлы, .env с паролями, сертификаты, docker login
deploy = "ansible-playbook deploy.yml" deploy = "ansible-playbook deploy.yml"
deploy-check = "ansible-playbook deploy.yml --check --diff" deploy-check = "ansible-playbook deploy.yml --check --diff"
# Деплой + поднять стек приложения (docker compose up -d)
stack = "ansible-playbook deploy.yml -e sarex_compose_up=true"
# Только генерация паролей и рендер .env на хосте (без копирования/сертификатов) # Только генерация паролей и рендер .env на хосте (без копирования/сертификатов)
gen-env = "ansible-playbook deploy.yml --tags env" gen-env = "ansible-playbook deploy.yml --tags env"
# docker login в приватный реестр по ключу json_key # docker login в приватный реестр по ключу json_key
login = "ansible-playbook deploy.yml --tags login" login = "ansible-playbook deploy.yml --tags login"
# сгенерировать логин+пароль и создать Django-суперпользователя в backend # Сгенерировать логин+пароль и создать Django-суперпользователя в backend
superuser = "ansible-playbook deploy.yml --tags superuser" superuser = "ansible-playbook deploy.yml -e sarex_create_superuser=true --tags superuser"
lint = "ansible-lint" lint = "ansible-lint"
# Полная подготовка окружения: uv sync + установка коллекций # Подготовка окружения: uv sync + установка коллекций
[tool.poe.tasks.setup] [tool.poe.tasks.setup]
sequence = ["sync", "galaxy"] sequence = ["sync", "galaxy"]
# Всё сразу: подготовка окружения и прогон плейбука # Установка с чистого листа: окружение → docker → деплой+стек → суперпользователь
[tool.poe.tasks.up] [tool.poe.tasks.install]
sequence = ["sync", "galaxy", "play"] sequence = ["sync", "galaxy", "provision", "stack", "superuser"]

View File

@ -31,6 +31,10 @@ secret_length: 24
# Хранилище секретов на control-node (persist + переиспользование). Gitignored. # Хранилище секретов на control-node (persist + переиспользование). Gitignored.
secrets_store: "{{ playbook_dir }}/.secrets" 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"
# Приватный реестр образов и ключ для входа (json_key) # Приватный реестр образов и ключ для входа (json_key)
registry_host: cr.yandex registry_host: cr.yandex
registry_username: json_key registry_username: json_key
@ -41,3 +45,18 @@ registry_key_src: /mnt/c/Users/user/.ssh/local/authorized_key.json
# docker login в cr.yandex — по умолчанию выключено. # docker login в cr.yandex — по умолчанию выключено.
sarex_compose_up: false sarex_compose_up: false
compose_cmd: "docker compose" compose_cmd: "docker compose"
# Сервисы приложения, поднимаемые при sarex_compose_up (depends_on тянет
# зависимости и порядок). k3s/gitea/processing намеренно не поднимаем.
sarex_services:
- postgres
- postgres-init
- redis
- rabbitmq
- minio
- minio-init
- measurements
- backend
- celery
- frontend
- nginx

View File

@ -43,7 +43,24 @@
no_log: true no_log: true
tags: [env] tags: [env]
- name: Собрать словарь переопределений для .env (пароли + домены) # RSA-ключи JWT (RS512): генерируем на control-node, persist в secrets_store.
- name: Сгенерировать приватный RSA-ключ JWT
ansible.builtin.command: "openssl genrsa -out {{ jwt_private_key_path }} 4096"
args:
creates: "{{ jwt_private_key_path }}"
delegate_to: localhost
become: false
tags: [env]
- name: Извлечь публичный RSA-ключ JWT
ansible.builtin.command: "openssl rsa -in {{ jwt_private_key_path }} -pubout -out {{ jwt_public_key_path }}"
args:
creates: "{{ jwt_public_key_path }}"
delegate_to: localhost
become: false
tags: [env]
- name: Собрать словарь переопределений для .env (пароли + домены + JWT)
ansible.builtin.set_fact: ansible.builtin.set_fact:
env_overrides: >- env_overrides: >-
{{ sarex_secrets | combine({ {{ sarex_secrets | combine({
@ -51,6 +68,8 @@
'ADMIN_DOMAIN': admin_domain, 'ADMIN_DOMAIN': admin_domain,
'MINIO_DOMAIN': minio_domain, 'MINIO_DOMAIN': minio_domain,
'SAREX_MINIO_CONSOLE_URL': 'https://' ~ minio_domain, 'SAREX_MINIO_CONSOLE_URL': 'https://' ~ minio_domain,
'SAREX_JWT_PRIVATE_KEY': "'" ~ (lookup('file', jwt_private_key_path) | replace('\n', '\\n')) ~ "'",
'SAREX_JWT_PUBLIC_KEY': "'" ~ (lookup('file', jwt_public_key_path) | replace('\n', '\\n')) ~ "'",
}) }} }) }}
no_log: true no_log: true
tags: [env] tags: [env]
@ -118,13 +137,14 @@
state: absent state: absent
tags: [login] tags: [login]
# --- Опционально: поднять стек ------------------------------------------- # --- Поднять сервисы приложения ------------------------------------------
- name: Поднять стек (docker compose up -d) - name: Поднять сервисы приложения (docker compose up -d)
ansible.builtin.command: ansible.builtin.command:
cmd: "{{ compose_cmd }} up -d" cmd: "{{ compose_cmd }} up -d {{ sarex_services | join(' ') }}"
chdir: "{{ deploy_dir }}" chdir: "{{ deploy_dir }}"
when: sarex_compose_up | bool when: sarex_compose_up | bool
changed_when: true changed_when: true
tags: [up]
# --- Django-суперпользователь: генерация логина+пароля, создание ----------- # --- Django-суперпользователь: генерация логина+пароля, создание -----------
# Только по явной команде (uv run poe superuser передаёт sarex_create_superuser=true), # Только по явной команде (uv run poe superuser передаёт sarex_create_superuser=true),

View File

@ -181,7 +181,9 @@ services:
image: ${SAREX_REDIS_IMAGE:-cr.yandex/crp3ccidau046kdj8g9q/redis:7.0.10} image: ${SAREX_REDIS_IMAGE:-cr.yandex/crp3ccidau046kdj8g9q/redis:7.0.10}
container_name: redis container_name: redis
restart: unless-stopped restart: unless-stopped
command: ["redis-server", "--save", "60", "1", "--loglevel", "warning"] # protected-mode no: redis только во внутренней docker-сети (порт наружу не
# публикуется), иначе бэкенд/celery из других контейнеров отбиваются "DENIED"
command: ["redis-server", "--save", "60", "1", "--loglevel", "warning", "--protected-mode", "no", "--stop-writes-on-bgsave-error", "no"]
volumes: volumes:
- sarex-redis-data:/data - sarex-redis-data:/data
healthcheck: healthcheck:
@ -273,8 +275,8 @@ services:
environment: &sarex-backend-env environment: &sarex-backend-env
ALLOWED_HOSTS: "*" ALLOWED_HOSTS: "*"
DJANGO_SETTINGS_MODULE: config.settings.production DJANGO_SETTINGS_MODULE: config.settings.production
JWT_PRIVATE_KEY: "JWT" JWT_PRIVATE_KEY: ${SAREX_JWT_PRIVATE_KEY:-JWT}
JWT_PUBLIC_KEY: "JPK" JWT_PUBLIC_KEY: ${SAREX_JWT_PUBLIC_KEY:-JPK}
DJANGO_POSTGRES_HOST: postgres DJANGO_POSTGRES_HOST: postgres
DJANGO_POSTGRES_DATABASE: ${SAREX_POSTGRES_DB:-sarex_db} DJANGO_POSTGRES_DATABASE: ${SAREX_POSTGRES_DB:-sarex_db}
DJANGO_POSTGRES_USER: ${SAREX_DJANGO_DB_USER:-django} DJANGO_POSTGRES_USER: ${SAREX_DJANGO_DB_USER:-django}
@ -362,7 +364,7 @@ services:
POSTGRES_SSL_USE: "0" POSTGRES_SSL_USE: "0"
ENABLE_SQL_QUERY: "0" ENABLE_SQL_QUERY: "0"
DJANGO_HOST: http://backend:8000 # django-backend в этом же compose DJANGO_HOST: http://backend:8000 # django-backend в этом же compose
PUBLIC_KEY: "JPK" # JWT public key (заглушка, как у backend) PUBLIC_KEY: ${SAREX_JWT_PUBLIC_KEY:-JPK} # JWT public key (общий с backend)
ports: ports:
- "8081:8080" - "8081:8080"
depends_on: depends_on: