Начало работы с Nubes Terraform¶
Это руководство поможет вам установить инструменты, настроить доступ и развернуть вашу первую инфраструктуру в облаке Nubes, используя Terraform.
ИИ-помощники на этом сайте¶
В правом нижнем углу сайта документации есть две кнопки:
- «LLM» — ассистент документации. Задайте вопрос своими словами — получите ответ со ссылками на страницы документации.
- «TF» — проверка файла. Вставьте содержимое своего
terraform.tfvars— ИИ вернёт исправленный файл и список изменений (что и почему поменял). Секреты (api_token, пароли) в поле не вставляйте — держите их в отдельном файлеsecrets.auto.tfvars.
ИИ-агент в вашей IDE
В репозитории примеров (Nail/tf_examples) есть файл AGENTS.md — инструкция для
ИИ-агентов (GitHub Copilot, Claude, Cursor и др.). Агент прочитает её сам и поможет
писать и проверять конфигурацию, опираясь на реальные схемы ресурсов, а не на догадки.
Что ИИ не делает
Ассистенты только помогают: не деплоят (terraform plan/apply запускаете вы),
не читают ваши секреты и не заменяют проверку конфигурации по документации.
0. Установка Terraform¶
Для работы с инфраструктурой как кодом (IaC) потребуется утилита Terraform (версии 1.5 или выше).
- Скачивание:
- Официальный сайт: hashicorp.com/terraform/install
- Для пользователей из РФ (без VPN): Yandex Cloud Mirror
- Установка:
- Распакуйте скачанный архив.
- Поместите исполняемый файл (
terraformилиterraform.exe) в папку, доступную в переменной окруженияPATH.
- Проверка:
- Откройте терминал (PowerShell, CMD, Bash или Terminal в macOS).
- Введите команду
terraform -version. Вы должны увидеть номер версии.
1. Подготовка конфигурации¶
Создайте рабочую директорию для вашего проекта и создайте основной файл конфигурации main.tf.
terraform {
required_providers {
nubes = {
source = "tf-registry.containerk8s.services.ngcloud.ru/nubes-dev/nubes"
version = "2.1.0"
}
}
}
provider "nubes" {
api_endpoint = "https://lk-api-gateway-dev.ngcloud.ru/api/v1/svc"
api_token = var.api_token
}
variable "api_token" {
type = string
sensitive = true
}
Стенд nubes-dev (обязательные адреса)
Эта документация относится к стенду nubes-dev.
- Личный кабинет: https://deck-dev.ngcloud.ru/dashboard/
- API endpoint: https://lk-api-gateway-dev.ngcloud.ru/api/v1/svc
Безопасность
Никогда не храните токен прямо в файле main.tf, если планируете загружать код в систему контроля версий (git). Используйте variables.tf или файл terraform.tfvars.
2. Получение API токена¶
Токен (Access Token) необходим провайдеру для авторизации ваших действий в облаке.
Если нет ТОКЕНА доступа или хотите создать новый - В Личном Кабинете - на странице Профиля пользователя https://deck-dev.ngcloud.ru/authorization/profile во вкладке Токены - нажать "Выпустить тех-токен" Значение токена показывается только при его создании, надо его сохранить
Создайте файл terraform.tfvars и сохраните токен там:
3. Описание ресурсов¶
Добавьте ресурсы, которые вы хотите создать, в файл main.tf или resources.tf.
Пример создания S3 бакета:
resource "nubes_s3bucket" "my_files" {
resource_name = "bucket_0"
bucket_name = "my-unique-bucket-name"
s3_user_uid = "235e0546-..." # UID Корневой услуги S3
}
Или Postgres кластера:
resource "nubes_postgres" "db" {
resource_name = "pg-tst0"
startup_configuration = {
resource_realm = "k8s-4-sandbox-nubes-ru" # платформа развёртывания
}
cluster_configuration = {
cpu = 500 # milicores (1000 = 1 ядро)
memory = 512 # MB
replicas = 1
disk = 10 # GB
}
access_configuration = {
master_ip_space = "no-needed"
master_access_list = jsonencode([])
slave_ip_space = "no-needed"
slave_access_list = jsonencode([])
}
postgres_configuration = {
version = "17"
ssl_required = true
pooler_master = false
pooler_slave = false
}
backup_configuration = {
s3_uid = "my-s3" # ИМЯ экземпляра S3 (не UUID)
retain = 7
schedule = "0 0 * * *"
}
autoscale_configuration = {
enabled = false
schedule = 0
percent = 10
quota = 100
}
}
Подробные примеры конфигураций смотрите в разделе Resources документации.
4. Запуск¶
Теперь вы готовы применить конфигурацию:
- Инициализация:
terraform init(загружает плагин провайдера). - План:
terraform plan(показывает, что будет сделано). - Применение:
terraform apply(создает инфраструктуру).
5. Почему провайдер использует Suspend/Adopt¶
Для этой логики используются два флага.
suspend_on_destroyadopt_existing_on_create
suspend_on_destroy¶
true(по умолчанию)- при
terraform destroyинстанс уходит вSuspend; - при удалении ресурса из манифеста — тоже
Suspend. false- Terraform удаляет ресурс только из state;
- в облаке инстанс не меняется.
adopt_existing_on_create¶
false(по умолчанию)- если инстанс уже существует, Terraform вернёт ошибку.
true- Terraform может взять существующий инстанс под управление.
Как это связано с terraform import¶
Это похоже на terraform import,
но срабатывает в обычном apply.
- найден
running→adopt; - найден
suspended+ обязательные параметры совпадают →resume + adopt.
Важно¶
Один инстанс должен быть только в одном state.
Если подключить один инстанс к двум манифестам, получится конфликт управления:
- первый
applyменяет ресурс; - второй
applyоткатывает или перезаписывает изменения.
Следующий шаг: изучите Справочник по командам Terraform для уверенной работы.
Примеры¶
Lucee & PostgreSQL¶
Lucee и NodeJS — это два отдельных UI для CRUD‑операций, оба работают с одной и той же таблицей в Postgres.
resource "nubes_postgres" "db2" {
# Основной Postgres-кластер для демо.
resource_name = "pg-crud"
startup_configuration = {
resource_realm = "k8s-4-sandbox-nubes-ru"
}
cluster_configuration = {
cpu = 500
memory = 512
replicas = 1
disk = 10
}
access_configuration = {
master_ip_space = "no-needed"
master_access_list = jsonencode([])
slave_ip_space = "no-needed"
slave_access_list = jsonencode([])
}
postgres_configuration = {
version = "17"
ssl_required = true
pooler_master = false
pooler_slave = false
}
postgres_conf = {
# Доп. настройки postgresql.conf (map-fixed).
log_connections = "off"
log_disconnections = "off"
}
backup_configuration = {
s3_uid = "my-s3" # ИМЯ экземпляра S3 (не UUID)
retain = 7
schedule = "0 0 * * *"
}
autoscale_configuration = {
enabled = false
schedule = 0
percent = 10
quota = 100
}
}
# Пользователь и база для приложений.
resource "nubes_postgres_user" "demo" {
postgres_id = nubes_postgres.db2.id
username = "crud_user"
role = "ddl_user"
}
resource "nubes_postgres_database" "demo_db" {
postgres_id = nubes_postgres.db2.id
db_name = "postgres"
db_owner = nubes_postgres_user.demo.username
}
resource "nubes_lucee" "app1" {
# Lucee UI, который читает/пишет в Postgres.
resource_name = "lucy1"
startup_configuration = {
resource_realm = "k8s-4-sandbox-nubes-ru"
}
cluster_configuration = {
cpu = 300
memory = 512
replicas = 1
}
access_configuration = {
domain = "web03" # Пример домена приложения
}
app_configuration = {
version = "5.4"
git_path = "https://<ваш-репозиторий>/<приложение>.git"
}
json_env = jsonencode({
# JDBC datasource для Lucee. Пароль БД платформа кладёт в vault_secrets["users"].
testds_class = "org.postgresql.Driver"
testds_bundleName = "org.postgresql.jdbc"
testds_bundleVersion = "42.6.0"
testds_connectionString = "jdbc:postgresql://${nubes_postgres.db2.state_out_flat["internalMaster"]}:5432/${nubes_postgres_database.demo_db.db_name}"
testds_username = nubes_postgres_user.demo.username
testds_password = nonsensitive(jsondecode(nubes_postgres.db2.vault_secrets["users"])[nubes_postgres_user.demo.username].password)
testds_connectionLimit = "5"
testds_liveTimeout = "15"
testds_validate = "false"
})
depends_on = [nubes_postgres.db2]
}
resource "nubes_nodejs" "app3" {
# NodeJS демо, работающий с тем же Postgres.
resource_name = "node_0"
startup_configuration = {
resource_realm = "k8s-4-sandbox-nubes-ru"
}
cluster_configuration = {
cpu = 300
memory = 256
replicas = 1
}
access_configuration = {
domain = "node"
}
app_configuration = {
version = "23"
git_path = "https://<ваш-репозиторий>/<приложение>.git"
health_path = "/healthz"
}
json_env = jsonencode({
PGHOST = nubes_postgres.db2.state_out_flat["internalMaster"]
PGPORT = "5432"
PGUSER = nubes_postgres_user.demo.username
PGPASSWORD = nonsensitive(jsondecode(nubes_postgres.db2.vault_secrets["users"])[nubes_postgres_user.demo.username].password)
PGDATABASE = nubes_postgres_database.demo_db.db_name
PGSSLMODE = "require"
})
depends_on = [nubes_postgres.db2]
}
Пример: RabbitMQ + Lucee UI + NodeJS воркер¶
Ниже полный пример resources.tf для RabbitMQ + Lucee UI + NodeJS воркера.
Комментарий: UI Lucee отправляет CRUD‑запросы в RabbitMQ, а применение изменений в Postgres выполняет отдельный воркер на NodeJS. Сервис Postgres должен быть запущен заранее (пример развертывания — выше в этом же руководстве, см. раздел про Lucee + Postgres).
# RabbitMQ кластер для демо.
resource "nubes_rabbitmq" "rb1" {
resource_name = "rabbit_0"
startup_configuration = {
resource_realm = "k8s-4-sandbox-nubes-ru"
}
cluster_configuration = {
cpu = 500
memory = 512
disk = 5
replicas = 1
}
access_configuration = {
master_ip_space = "no-needed"
master_access_list = jsonencode([])
}
mq_configuration = {
max_message_size = 4
}
}
# Lucee UI, который пишет в Rabbit и читает из Postgres.
resource "nubes_lucee" "rabbit_ui" {
resource_name = "rb-lucee-ui"
startup_configuration = {
resource_realm = "k8s-4-sandbox-nubes-ru"
}
cluster_configuration = {
cpu = 300
memory = 512
replicas = 1
}
access_configuration = {
domain = "rb-ui"
}
app_configuration = {
version = "5.4"
git_path = "https://<ваш-репозиторий>/<приложение>.git"
}
# Переменные окружения для datasource, Rabbit и UI.
json_env = jsonencode({
testds_bundleName = "org.postgresql.jdbc"
testds_bundleVersion = "42.6.0"
testds_class = "org.postgresql.Driver"
testds_connectionLimit = "5"
testds_connectionString = "jdbc:postgresql://${var.PGHOST}:5432/postgres"
testds_liveTimeout = "15"
testds_username = var.PGUSER
testds_password = var.PGPASSWORD
testds_validate = "false"
PG_TABLE = "rabbit_messages"
UI_LOG_TABLE = "rabbit_ui_log"
RABBIT_HOST = nubes_rabbitmq.rb1.state_out.internalConnect
RABBIT_PORT = "5672"
RABBIT_USER = nubes_rabbitmq.rb1.vault_secrets["adminUser"]
RABBIT_PASSWORD = nubes_rabbitmq.rb1.vault_secrets["adminPass"]
RABBIT_VHOST = "/"
RABBIT_QUEUES = "crud_queue"
RABBIT_DURABLE = "true"
RABBIT_ADMIN_URL = "https://${jsondecode(nubes_rabbitmq.rb1.state_out.externalConnect).admin}/#/"
NODEWORKER_URL = "https://${nubes_nodejs.rabbit_nodeworker.state_out.connectionUrl}/"
})
depends_on = [nubes_rabbitmq.rb1]
}
# NodeJS воркер, который переносит CRUD из очереди в Postgres.
resource "nubes_nodejs" "rabbit_nodeworker" {
resource_name = "nodeworker"
startup_configuration = {
resource_realm = "k8s-4-sandbox-nubes-ru"
}
cluster_configuration = {
cpu = 100
memory = 256
replicas = 1
}
access_configuration = {
domain = "nodeworker"
}
app_configuration = {
version = "23"
git_path = "https://<ваш-репозиторий>/<приложение>.git"
health_path = "/healthz"
}
# Параметры подключения к Rabbit и Postgres.
json_env = jsonencode({
RABBIT_HOST = nubes_rabbitmq.rb1.state_out.internalConnect
RABBIT_PORT = "5672"
RABBIT_USER = nubes_rabbitmq.rb1.vault_secrets["adminUser"]
RABBIT_PASSWORD = nubes_rabbitmq.rb1.vault_secrets["adminPass"]
RABBIT_VHOST = "/"
RABBIT_QUEUES = "crud_queue"
RABBIT_DURABLE = "true"
RABBIT_PREFETCH = "1"
REQUEUE_ON_ERROR = "true"
PGHOST = var.PGHOST
PGPORT = "5432"
PGUSER = var.PGUSER
PGPASSWORD = var.PGPASSWORD
PGDATABASE = "postgres"
PGSSLMODE = "require"
PG_TABLE = "rabbit_messages"
})
depends_on = [nubes_rabbitmq.rb1]
}
# Ручной запуск операций из Terraform.
#
# 1) redeploy: у ресурсов с операцией `redeploy` есть поле `git_revision`.
# Отдельного ресурса `nubes_nodejs_redeploy` больше нет — меняйте значение
# поля в самом `nubes_nodejs`, чтобы запустить пересборку из git:
#
# resource "nubes_nodejs" "rabbit_nodeworker" {
# # ... остальные параметры ...
# git_revision = "manual-001" # ← смена значения = redeploy
# }
#
# 2) reconcile и любые другие операции — ресурсом `nubes_service_operation`
# (доступен во всех опубликованных версиях). Меняйте `run_id`, чтобы
# запустить операцию повторно:
#
# resource "nubes_service_operation" "pg_reconcile" {
# instance_uid = nubes_postgres.db2.id
# operation = "reconcile"
# run_id = "manual-001" # ← смена значения = повторный запуск
# }
#
# Операция выполняется, только если она есть в `availableOperations`
# инстанса; иначе — ошибка «операция … недоступна для экземпляра …».
// Postgres уже запущен, значения берутся из UI Личного кабинета.
variable "PGUSER" {
type = string
default = "postgres"
}
variable "PGPASSWORD" {
type = string
default = "" # пароль укажите вручную, в примере не публикуем
}
variable "PGHOST" {
type = string
default = "postgresqlk8s-master.<id>.svc.k8s-3.ext.nubes.ru" # оставьте префикс и суффикс, меняется только <id>
}