Files
tf_provider/docs/30_registry/guides/provider-behavior.md
T

21 KiB
Raw Blame History

Как работает провайдер Nubes: поведение и отличия от канонического Terraform

Страница для тех, кто уже работал с Terraform и хочет понять, чего ожидать от провайдера Nubes, и для DevOps, которым важно знать, что реально произойдёт в облаке при plan, apply и destroy. Здесь описан наблюдаемый контракт (что уже проверено на живых стендах) и ограничения платформы.

1. Суть в одном абзаце

Провайдер Nubes — это не плагин к гипервизору, а обёртка над API личного кабинета: каждый Terraform-ресурс соответствует инстансу услуги в облаке, а create / update / delete транслируются в асинхронные операции платформы (create, modify, suspend, resume, delete). Отсюда и все отличия от привычного Terraform:

  • операции долгие — провайдер ждёт их завершения (поллинг);
  • меняется уже созданный объект, а не создаётся заново;
  • ошибки приходят внутри тела ответа, а не HTTP-кодом;
  • ресурс ищется по имени, а не только по id;
  • удаление может быть «мягким» — объект остаётся в облаке.

2. Какие ресурсы за что отвечают

Услуга облака Ресурс Terraform Что делает
Организация в Cloud Director (19) nubes_vc_org_ip_allocation модификатор: меняет квоту внешних IP в организации (саму организацию создают в ЛК, Terraform её не трогает)
Виртуальный датацентр (21) nubes_vc_vdc создаёт vDC с ресурсами (CPU/RAM/Storage)
Сетевой шлюз периметра (22) nubes_vc_nsxt создаёт Edge (routed-сеть, ALB/AVI)
Сетевой шлюз периметра (22) nubes_vc_nsxt_snat модификатор: включает/переключает SNAT (ipSpaceName) на существующем Edge
Kubernetes кластер Штурвал (150) nubes_k8s_sthutrval_cluster создаёт кластер (control plane + группы воркеров)

Общее правило. Ресурс, у которого есть свой «объект в облаке», создаёт этот объект. А то, что платформа меняет операцией modify (квота IP, включение SNAT, настройки шлюза), собрано в отдельные ресурсы- модификаторы: они не создают ничего нового, а правят уже существующий объект.

3. Отличия от «учебного» Terraform

Ожидание по канону Как в Nubes Причина
create = один вызов API Создание инстанса — последовательность: POST /instances → POST /instanceOperations → параметры (instanceOperationCfsParams) → run → поллинг до dtFinish Так устроен API платформы
id — произвольная строка id = UUID инстанса в облаке; провайдер читает его из ответа платформы Состояние живёт в облаке, не в Terraform
update = замена при несовместимых параметрах update = операция modify над тем же инстансом; часть параметров — create-only (менять нельзя → ошибка на этапе plan) Платформа не пересоздаёт объекты «из коробки»
есть data sources для поиска существующего Поиск/ссылки — через ref-параметры: можно указать UUID или имя инстанса Экономит data sources, но требует дисциплины в значениях
import — явная команда Плюс к import есть авто-усыновление adopt_existing_on_create = true: ресурс сам находит инстанс по имени В проде объекты живут неделями, и пересоздавать их нельзя
plan показывает, что ресурс уже существует Нет: проверка «инстанс с таким именем уже есть / adopt» выполняется в Create, то есть на apply Иначе ломается destroy и работа с tainted-ресурсами
delete удаляет delete может быть delete / suspend / state_only — см. §5 У платформы не всё удаляется, а часть объектов удалять нельзя, пока жив потребитель
ошибка приходит HTTP-кодом Ошибка операции — в теле: isSuccessful=false + errorLog, при успешном HTTP-коде API платформы отвечает 200/201 почти всегда
операции быстрые Операции асинхронные и долгие (Штурвал — десятки минут) → параметр operation_timeout; одновременные операции на одном инстансе не поддерживаются Наследство платформы (внутри — CFS-оркестратор)
план обязан совпадать с config Тоже, но: ref-параметры в плане остаются как написаны (name не подменяется на uuid), а уже перед вызовом API провайдер сам разберётся, имя это или UUID Иначе Terraform упадёт с «Provider produced invalid plan»
параметры сравниваются как строки JSON-параметры сравниваются канонично: порядок ключей и пробелы не важны, скаляры приводятся к строке, UUID — без учёта регистра API возвращает JSON в своём виде, иначе будет «вечный diff»
идентичность — по id Дополнительно: инстанс ищется по resource_name (displayName) → переименование = новый ресурс Имя задаётся при создании и не меняется

Что из этого следует на практике

  • terraform plan не может проверить, что «такой объект уже есть»: он это покажет как will be created, а разбираться будет apply.
  • terraform apply на существующей инфраструктуре — это нормальный сценарий (adopt), если включён adopt_existing_on_create; без флага вы получите явную ошибку-конфликт, а не дубль ресурса.
  • Долгие операции требуют терпения: смотрите operation_timeout, не запускайте вторую операцию по тому же объекту.

4. Создание, чтение, изменение, удаление

Создание. Провайдер формирует параметры операции, отправляет её и ждёт завершения. Параметры, зависящие от других ресурсов (например vdc_uid, nsxt_uid), передаются как ref-значения: UUID или имя.

Чтение (refresh). Значения state_params / state_out приходят из облака: так в state попадают адреса (kubernetesApiAddress, ingressAddress), имена, текущие параметры.

Изменение. Если параметры поменялись, вызывается modify (для модификаторов — обратный modify при удалении). Параметры, помеченные как create-only, изменить нельзя — провайдер скажет об этом до обращения к API.

Удаление. См. следующий раздел — это самое неочевидное место.

5. Удаление: delete, suspend и «оставить как есть»

У платформы три разных исхода, и провайдер умеет все три. Управляется флагами:

Флаг Где применим Поведение при destroy Предупреждение в выводе
suspend_on_destroy = true кластер Штурвала, vDC (по умолчанию true) объект приостанавливается, не удаляется «Ресурс заморожен, а не удалён»
keep_on_destroy = true Edge, SNAT, квота IP (по умолчанию false) объект не трогается в облаке, только убирается из state «Оставлен как есть» (для SNAT — «SNAT не выключался», для квоты IP — «Аллокация IP не снималась»)
оба false любой обычное удаление —

Если выставлены оба флага, победит keep_on_destroy (объект просто не тронут). По умолчанию провайдер удаляет по-настоящему (keep_on_destroy = false), а «заморозку» или «оставить как есть» включают явно в конфигурации стенда.

Почему Edge остаётся running

Edge физически не умеет suspend: в списке доступных операций шлюза есть только delete, modify, reconcile. Усыпить его платформа не даёт, поэтому единственный корректный способ «не удалять шлюз» — не трогать его: keep_on_destroy = true. Побочный эффект — шлюз продолжает работать и тарифицироваться, и через него продолжают публиковаться внешние адреса (API кластера и ingress). Именно поэтому удаление Edge при живом кластере Штурвала (или других зависимых объектах: vApp, VM) считается недопустимым.

Почему SNAT остаётся включённым, а квота IP не обнуляется

  • SNAT-модификатор с keep_on_destroy = true не отправляет обратный modify с ipSpaceName = "no-needed", то есть SNAT для виртуальных машин остаётся в прежнем состоянии.
  • Квота внешних IP: уменьшать count ниже фактически занятых адресов платформа не разрешает (ошибка вида «Кол-во занятых Ip … Невозможно выставить параметр count ниже этого параметра»). Адреса держит кластер Штурвала (API + ingress), и suspend кластера их не освобождает. Поэтому при «заморозке» квота не изменяется вовсе, а реальное освобождение адресов возможно только после удаления кластера — отдельным шагом.

Почему кластер и vDC уходят в suspend

Для них suspend поддерживается платформой, и это самый близкий к «выключить» вариант: ВМ останавливаются, объекты не удаляются, адреса и конфигурация сохраняются. Обратный ход делает apply:

Что При destroy (заморозка) При следующем apply
Кластер Штурвала suspend adopt по имени + resume
vDC suspend adopt по имени + resume
Edge не трогается (running) adopt (инстанс уже работает)
SNAT не трогается (включён) повторный modify теми же значениями (фактически no-op)
Квота IP не трогается modify с тем же count (no-op)

Итого цикл «destroy → apply» на стенде выглядит как «усыпить → разбудить», а не «снести → поднять заново». Полностью удалить такой стенд можно только явным отказом от заморозки (keep_on_destroy = false / suspend_on_destroy = false) и в правильном порядке (см. §6).

6. Конкретный пайплайн стенда: vDC → Edge → внешние IP → SNAT → Штурвал

Порядок создания и зависимости:

  1. Организация — вручную в ЛК (Terraform её не создаёт).
  2. nubes_vc_vdc — виртуальный датацентр.
  3. nubes_vc_nsxt — Edge. Обязательно включать балансировщик (параметр need_enable_avi = true) и задать не меньше 3 виртуальных сервисов (virtual_services_count >= 3) — это нужно кластеру Штурвала.
  4. nubes_vc_org_ip_allocation — внешние адреса в организации (минимум 3 по инструкции услуги Штурвала).
  5. nubes_vc_nsxt_snat — SNAT на Edge (по depends_on после аллокации адресов).
  6. nubes_k8s_sthutrval_cluster — кластер Штурвала (по depends_on после SNAT: нодам нужен выход в интернет).

При удалении Terraform идёт в обратном порядке. Ограничения платформы, которые встречаются на этом пути:

  • 1 кластер Штурвала = 1 vDC (действующее ограничение услуги).
  • vDC удаляется только через 14 дней после suspend; при живых Edge/vApp/VM/кластере — через поддержку.
  • Edge не удаляется при живых зависимых объектах (по инструкции услуги).
  • Квота IP не опускается ниже занятых адресов (см. §5).

Как проверить, что конфигурация корректна: terraform plan после apply должен говорить No changes. Если появляется стабильный diff — сверяйте его с terraform state show и с тем, что реально показывается в личном кабинете.

7. Регистр UUID и канонизация значений

UUID в облаке не имеет «правильного» регистра: один и тот же идентификатор может прийти как 2c37fed1-… и как 2C37FED1-…. Поэтому провайдер сравнивает UUID без учёта регистра — в том числе внутри JSON-параметров (например, startupConfiguration кластера Штурвала). Подробный разбор проблемы и всех мест, где она может проявиться, — в 60_strategy/terraform_case_sensitivity_fix.md (внутренний документ).

Для JSON-параметров сравнение также игнорирует порядок ключей и пробелы, а скаляры приводит к строковому виду — это нужно, чтобы plan не показывал «изменение» там, где API вернул то же значение в другом формате.

8. Чек-лист DevOps

  • Пин версии провайдера привязан к стенду: prod 1.*, dev 2.*, test 3.*; после смены версии — terraform init -upgrade.
  • Перед apply и после — смотрите plan; ожидаемый финальный результат — No changes.
  • Читайте предупреждения destroy: «заморожен», «оставлен как есть», «SNAT не выключался» — это не косметика, а отчёт о том, что объекты продолжают жить и тарифицироваться.
  • Не удаляйте объекты стенда руками в ЛК между destroy и apply: авто-усыновление ищет их по имени, и на удалённом объекте упрётся в состояние not created.
  • Долгие операции: задавайте operation_timeout (для Штурвала — 60m), не запускайте параллельные операции по одному объекту.
  • Проверяя состояние через API ЛК, помните про обязательные заголовки (браузерный User-Agent и Referer) — иначе отдаёт 403.

9. FAQ

Почему plan пишет will be created, если объект уже есть? Потому что проверка существования и усыновление выполняются на apply (в Create). В state ресурса нет → план честно планирует создание. Решение — adopt_existing_on_create = true.

Почему destroy сказал 5 destroyed, а в облаке всё живо? Сработала «заморозка»: кластер и vDC приостановлены, Edge, SNAT и квота IP не изменялись. Terraform «удалил» ресурсы только из своего состояния. Смотрите предупреждения в выводе.

Почему IP не освободились? Квота не может стать меньше числа занятых адресов, а их держит кластер. Пока кластер существует (даже в suspend), платформа не даст уменьшить count.

Почему Edge нельзя приостановить? У платформы для шлюза нет операции suspend — только delete, modify, reconcile.

Почему после apply кластер ожил сам? Сработало усыновление: ресурс нашёл инстанс по имени (adopt_existing_on_create), увидел статус suspended и выполнил resume.

State пуст, а объекты в облаке есть. Что делать? Это ожидаемый результат «заморозки». terraform apply в том же каталоге усыновит объекты обратно.

Кто-то удалил объект в ЛК. Что будет? Усыновление не найдёт его и провайдер завершится ошибкой с указанием статуса (not created) — нужно либо создать объект заново, либо разбираться с конфигурацией.

А можно всё-таки удалить стенд полностью? Да, но осознанно: выставить keep_on_destroy = false и suspend_on_destroy = false, затем удалять в порядке кластер → квота IP → SNAT → Edge → vDC, при необходимости — через поддержку (для vDC действует правило 14 дней).

10. Куда смотреть дальше

  • curated/pipeline/vdc_edge_ip_snat.md — пошаговый разбор цепочки vDC → Edge → IP → SNAT.
  • curated/modifiers/org_ip_and_snat.md — ресурсы-модификаторы.
  • 30_registry/guides/terraform-structure.md — структура манифестов.
  • Внутренние документы (не публикуются): 60_strategy/provider_philosophy.md, 60_strategy/modifier_resources_ideology_and_specification.md, 60_strategy/adopt_ref_validation.md, 60_strategy/terraform_case_sensitivity_fix.md.