# CRUD Test Stand Три приложения (Lucee / Flask / Node.js) + общая PostgreSQL. Каждое делает CRUD в одной таблице `crud_items`. ## Структура — ДВЕ папки, ДВА независимых состояния ``` CRUD/ ├── pg/ ← база данных: кластер PostgreSQL + пользователь + база └── apps/ ← приложения-потребители: Lucee + Flask + Node.js ``` ### 🎯 Почему так, а не «всё в одной папке» | Зачем | Что это даёт | |---|---| | **`destroy` в `apps/` убивает ТОЛЬКО приложения** | база данных с данными не трогается — она в другом state (`pg/`) | | **БД — общий ресурс** | к одному кластеру можно подключить сколько угодно папок-потребителей: `apps/`, потом ещё `my-service/`, `reporting/` и т.д. | | **Разные жизненные циклы** | приложения пересоздаются сколько угодно раз, БД живёт всё это время | Если держать PG и приложения в одном state, `terraform destroy` снесёт **и кластер с данными**. Именно поэтому база вынесена отдельно. ### 🎯 Почему нельзя создать всё одним `apply` Пароль пользователя БД генерирует платформа и отдаёт его в выходе кластера `vault_secrets["users"]` — **только после** операции `create_user`. При создании всё одним `apply` приложения запрашивали пароль, когда его ещё не существовало, и поднимались с пустым `PGPASSWORD` (раньше это маскировалось функцией `try()`). Теперь порядок явный: **сначала БД отдельным прогоном, потом приложения** — пароль к этому моменту существует и лежит в файле `apps/creds.json`. Костыль `try()` убран. --- ## 📋 Порядок запуска (по шагам) ### Шаг 0. Скачать репозиторий ```bash git clone https://gitea.services.ngcloud.ru/terraform/tf_examples.git cd tf_examples/CRUD ``` ### Шаг 1. База данных — папка `pg/` ```bash cd pg cp terraform.tfvars.example terraform.tfvars # → отредактировать terraform.tfvars: api_token, realm, s3_name terraform init terraform apply ``` Создастся кластер PostgreSQL (несколько минут), затем пользователь БД и база. В конце `apply` выводятся outputs, включая пароль (помечен `sensitive`). | Переменная | Где брать | |---|---| | `api_token` | ЛК → Профиль → Токены → создать «Технический» | | `realm` | ЛК → Кластеры, например `k8s-4-sandbox-nubes-ru` | | `s3_name` | ЛК → S3 → имя экземпляра (для бэкапов PG) | ### Шаг 2. Передать креды БД в папку приложений 👈 ОДНА команда ```bash # всё ещё в папке pg/ terraform output -json > ../apps/creds.json ``` **Что делает:** выгружает хост БД, порт, имя пользователя, имя базы и пароль в файл `apps/creds.json`. Папка `apps/` читает этот файл (см. `apps/locals.tf`). **Почему файлом, а не «само»:** `pg/` и `apps/` — разные состояния Terraform (разные `terraform.tfstate`), между ними значения автоматически не передаются: у провайдера нет data-source, а общего backend в стенде нет. Поэтому креды передаются явной выгрузкой outputs — зато видно, что и откуда берётся. > ⚠️ В `creds.json` пароль **в открытом виде**. Файл добавлен в `apps/.gitignore`. > Не коммитить и не пересылать. Если пароль ротировали (пересоздали пользователя > в `pg/`) — повторите шаг 2, файл надо обновить. ### Шаг 3. Приложения — папка `apps/` ```bash cd ../apps cp terraform.tfvars.example terraform.tfvars # → отредактировать: api_token (тот же), realm (ТОТ ЖЕ, где создана БД) # → проверить домены в locals.tf: они должны быть УНИКАЛЬНЫМИ terraform init terraform apply ``` Создадутся три приложения, каждое подключится к общей БД. | Что менять | Файл | Зачем | |---|---|---| | `api_token`, `realm` | `terraform.tfvars` | доступ к API; realm обязан совпадать с БД | | `lucee_domain` / `flask_domain` / `nodejs_domain` | `locals.tf` | имена доменов — уникальны в облаке | | `*_git_path` | `locals.tf` | репозиторий с кодом приложения | --- ## 🔄 Повседневные операции | Что нужно | Команда | |---|---| | Передеплоить приложения | `cd apps && terraform apply` (БД не затрагивается) | | Снести приложения | `cd apps && terraform destroy` — **БД и данные остаются** | | Изменить БД | `cd pg && terraform apply` | | Снести БД | `cd pg && terraform destroy` — кластер уйдёт в `Suspend`, а не удалится; юзер и база останутся внутри (`keep_on_destroy = true`) | | Добавить ещё одного потребителя той же БД | скопировать `apps/` → своя папка → свой `creds.json` (шаг 2) → свои домены | ### Про `destroy` базы - Кластер PostgreSQL при `destroy` **не удаляется**, а переводится в `Suspend` (поведение сервиса по умолчанию). - Пользователь и база тоже **не удаляются**: у них `keep_on_destroy = true`. Иначе из живого кластера исчезли бы база и пользователь. Провайдер печатает предупреждение «Подресурс оставлен как есть, а не удалён». - Удалить по-настоящему: снять `keep_on_destroy` в `pg/postgres_user_db.tf` и `apply`. ## Состав | Файл | Ресурс / назначение | |---|---| | `pg/main.tf` | провайдер + переменные кластера, пользователя, базы | | `pg/postgres.tf` | `nubes_postgres.main_pg` — кластер | | `pg/postgres_user_db.tf` | `nubes_postgres_user` + `nubes_postgres_database` | | `pg/outputs.tf` | выводы для потребителей (хост, порт, юзер, база, пароль) | | `apps/main.tf` | провайдер + переменные приложений | | `apps/locals.tf` | креды из `creds.json` + домены, версии, размеры | | `apps/lucee.tf` | `nubes_lucee.applucee` | | `apps/flask.tf` | `nubes_flask.appflask` | | `apps/nodejs.tf` | `nubes_nodejs.appnodejs` | | `apps/creds.json` | **не в git** — выгрузка кредов из `pg/` (шаг 2) |