22 KiB
Как работает провайдер 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 → Штурвал
Порядок создания и зависимости:
- Организация — вручную в ЛК (Terraform её не создаёт).
nubes_vc_vdc— виртуальный датацентр.nubes_vc_nsxt— Edge. Обязательно включать балансировщик (параметрneed_enable_avi = true) и задать не меньше 3 виртуальных сервисов (virtual_services_count >= 3) — это нужно кластеру Штурвала.nubes_vc_org_ip_allocation— внешние адреса в организации (минимум 3 по инструкции услуги Штурвала).nubes_vc_nsxt_snat— SNAT на Edge (поdepends_onпосле аллокации адресов).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.*, dev2.*, test3.*; после смены версии —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. Куда смотреть дальше
- Как развернуть 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.