From 3df93ad07fd0b1931a0ba7f5457d4f940b224864 Mon Sep 17 00:00:00 2001 From: Nail Date: Thu, 24 Sep 2026 18:59:53 +0300 Subject: [PATCH] =?UTF-8?q?docs(notes):=20=D0=B4=D0=B8=D0=B0=D0=B3=D0=BD?= =?UTF-8?q?=D0=BE=D1=81=D1=82=D0=B8=D0=BA=D0=B0=20=D0=A8=D1=82=D1=83=D1=80?= =?UTF-8?q?=D0=B2=D0=B0=D0=BB=20dev-00=20(44/48=20=D0=BF=D0=BE=D0=B4=D0=BE?= =?UTF-8?q?=D0=B2,=20=D0=BC=D1=83=D1=81=D0=BE=D1=80=20init-job)=20+=20?= =?UTF-8?q?=D1=80=D0=B0=D0=B7=D0=B1=D0=BE=D1=80=20=D0=BE=D1=88=D0=B8=D0=B1?= =?UTF-8?q?=D0=BA=D0=B8=20destroy=20=D0=BF=D0=BE=20=D0=BA=D0=B2=D0=BE?= =?UTF-8?q?=D1=82=D0=B5=20IP=20=D0=B8=20=D0=B4=D0=B8=D0=B7=D0=B0=D0=B9?= =?UTF-8?q?=D0=BD=20freeze-on-destroy=20=D1=87=D0=B5=D1=80=D0=B5=D0=B7=20?= =?UTF-8?q?=D0=B3=D0=B5=D0=BD=D0=B5=D1=80=D0=B0=D1=82=D0=BE=D1=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ...DEV00_DIAG_AND_FREEZE_DESIGN_2026-09-24.md | 195 ++++++++++++++++++ .../CHAT_RESUME_2026-09-24_shturval_freeze.md | 45 ++++ 2 files changed, 240 insertions(+) create mode 100644 NOTES/30_analysis/SHTURVAL_DEV00_DIAG_AND_FREEZE_DESIGN_2026-09-24.md create mode 100644 NOTES/40_chat_summaries/CHAT_RESUME_2026-09-24_shturval_freeze.md diff --git a/NOTES/30_analysis/SHTURVAL_DEV00_DIAG_AND_FREEZE_DESIGN_2026-09-24.md b/NOTES/30_analysis/SHTURVAL_DEV00_DIAG_AND_FREEZE_DESIGN_2026-09-24.md new file mode 100644 index 0000000..65424d2 --- /dev/null +++ b/NOTES/30_analysis/SHTURVAL_DEV00_DIAG_AND_FREEZE_DESIGN_2026-09-24.md @@ -0,0 +1,195 @@ +# Штурвал dev-00: диагностика, destroy-засада с квотой IP и дизайн «freeze on destroy» (2026-09-24) + +> Источники: `kubectl` из локали (контекст `tazet@narod.ru@shturval-dev-00`), API ЛК dev +> (`https://lk-api-gateway-dev.ngcloud.ru/api/v1/svc`, токен `secrets/narodDEV.token`), код провайдера +> (`provider/`), генератор (`TOOLS/resource-generator/`), конфиг стенда `DEV_STAND/FullPipe/`. +> Все выводы — только из этих источников; где не проверено, отмечено «не проверено». + +--- + +## 1. Стенд и кластер + +- Услуга **150 «Kubernetes кластер Штурвал»**, инстанс `shturval-dev`, uid `94627ff4-33a5-48f2-aca1-695741e0b6a2`. + Создан 24.09.2026 17:18:01, операция `create` завершена 17:36:58 (`isSuccessful=true`, `errorLog=null`). +- `state.out`: `webUrl=https://k8s.ngcloud.ru/clusters/shturval-dev-00/dashboard`, + `kubernetesApiAddress=185.247.187.146`, `ingressAddress=185.247.187.148`. +- Параметры: `clusterName=shturval-dev-00`, `vdcUid=d0937335-…` (`fullpipe-vdc`), + `nsxtUid=2C37FED1-E8F8-4A84-8434-7851C7C8B5D6` (эдж `fullpipe-edge`), `appVersion=2.14.0`, + `exLogging/exMonitoring/exLocalCsi/exVip/exUpdate/exIngress/exNamedCsi = true`, CP 1× `TKG 4CPU 8RAM` / 50 ГБ, + workers 1× `TKG 4CPU 8RAM` / 50 ГБ (`workers-shturval-dev`, labelDeck=true). +- kubeconfig: сервер `https://185.247.187.146:6443`; client v1.34.1, server v1.35.1; узлы 2× Ready + (control-plane + worker), Ubuntu 24.04.5, containerd 2.2.1. +- Организация `organ` (uid `57eeacd1-dc7f-4a52-b903-7e5f7d3c1164`, CD-имя `WZ01325-saas`, realm `sandbox.nubes.ru`). + +### Состояние кластера (снимок 17:52 MSK) + +- Подов 49 (готовых 44). Не-Running остались только подвисшие поды установщика: + `shturval-init-job-489mk`, `-98n4k`, `-vkwk7` (Error), `-l8457` (Unknown); рядом `-9587k` (Completed). +- Job `kube-system/shturval-init-job`: label `shturval.tech/init`, **без ownerReferences**, + `backoffLimit: 10`, `ttlSecondsAfterFinished: 86400`, nodeSelector `control-plane`, + образ `r.shturval.tech/shturval-install:2.14.0`, `/scripts/run.sh`. Итог: `failed: 4`, `succeeded: 1`, + завершён 14:31:46 UTC (17:31 MSK) → поды удалятся сами ~25.09 14:31 UTC. +- Причина падений (лог пода): `UPGRADE FAILED: failed to create resource: conversion webhook for + ops.shturval.tech/v1beta1, Kind=ShturvalRepoConfig failed: Post + "https://shturval-services-webhook-service.shturval-services-system.svc:443/convert?timeout=30s": + dial tcp 10.97.147.91:443: connect: operation not permitted`. То же в логе оператора: + `failed calling webhook "vshturvalupdate.kb.io" … connect: operation not permitted` — до готовности Cilium + webhook-и недоступны. Следующая попытка прошла (релиз `shturval-services.v3`), всё поднялось. +- Остальное зелёное: `shturvalserviceconfigs` 41/41 `ready=true` (24 в режиме `auto`, 17 в `absent`), + `nodeconfigitems` 4/4 `ready=true`, `nodeconfigs` 2/2, у всех 35 сервисов есть endpoints, + IngressClass `nginx` 1, ingress-controller 1/1. + +### UI-счётчики (расшифровка) + +| Колонка | Что это на самом деле | +|---|---| +| `Pods 44/48` | готовые/всего поды (48 = все поды минус `Completed`) | +| «Системные сервисы» | число `ShturvalServiceConfig` в режиме `auto`: 17/24 во время установки → 24/24 | +| «Конфигурация узлов» 4/4 | `nodeconfigitems.node.shturval.tech` `ready=true` | +| «Ingress» | домен `*.shturval-dev-00.ip-185-247-187-148.shturval.link`, **не** счётчик | +| ⚠️ на «Pods» | ровно 4 подвисших пода `shturval-init-job` | + +Статус «Работает с ошибками» в первом снимке (31/53 подов) был снят во время установки; после догрузки — «Работает». + +--- + +## 2. Что DevOps может сделать с мусором init-job + +Ответ на вопрос «что выставить в настройках деплоя Штурвала»: + +- В услуге 150 и в конфиге стенда таких ручек **нет** (есть только `vdc_uid`/`nsxt_uid`, `cluster_name`, + галочки `ex_*`, sizing/count, внешние адреса). `backoffLimit` и `ttlSecondsAfterFinished` зашиты + в манифест установщика платформы. +- Поэтому вариантов два: подождать самоочистку по `ttlSecondsAfterFinished: 86400`, либо тикет в команду + Штурвала: уменьшить TTL и/или не запускать установку компонентов до готовности Cilium + (иначе снова `operation not permitted` на webhook-ах). + +--- + +## 3. Destroy стенда и засада с квотой IP + +Порядок destroy: `nubes_vc_nsxt_snat.snat` → `nubes_vc_org_ip_allocation.org_ip` → `nubes_vc_nsxt.edge` → `nubes_vc_vdc.vdc`. + +- `snat` удалился успешно (2m27s), отправив modify `ipSpaceName = "no-needed"` (warning «SNAT выключен»). +- `org_ip_allocation` упал: + `Error: Ошибка клиента — операция D9FB606D-C86E-4D30-A58D-44282C4508AE завершилась с ошибкой: + Кол-во зантяых Ip в тенанте 'WZ01325-saas': 2. Невозможно выставить параметр count ниже этого параметра`. +- Причина в коде: `Delete` аллокации при `keep_on_destroy = false` отправляет обратный modify + `vIPConfigure = [{"name":"internet-ipv4-v1","count":"0"}]` + (`provider/internal/resources_core/org_ip_allocation_resource.go:258-274`); при `keep_on_destroy = true` + ничего не отправляется (строки 212-215). В стенде сейчас `keep_on_destroy = false` + (`DEV_STAND/FullPipe/modifiers.tf:39` для квоты, `:29` для SNAT). +- Кто держит 2 адреса: инстанс кластера Штурвала — `.146` (API) и `.148` (ingress). `suspend` адреса + **не** освобождает (suspend выполнен 17:59:59 MSK успешно, `isDeleted=false`, `uptime=0`, адреса в `state.out` остались). +- Последствие упавшего destroy: прерван, до edge/vDC дело не дошло; в state остались `vdc`, `edge`, + `org_ip_allocation`, а `snat` уже удалён — «рваное» состояние. + +### Метаданные платформы (проверено через API ЛК) + +- Обязательные заголовки: `Authorization: Bearer `, + браузерный `User-Agent`, `Referer: https://deck-dev.ngcloud.ru/` — без них DDoS-Guard отдаёт `403 Forbidden`. +- Эндпоинты: `GET /instances?page=1&size=200`, `GET /instances/{uid}`; параметры — в + `instance.state.params` (верхнеуровневый `instance.params = null`), статус — `explainedStatus`. +- `availableOperations`: кластер — `delete, modify, suspend, resume, reconcile, create_user, delete_user`; + vDC — `delete, modify, suspend, resume, reconcile`; **эдж — `delete, modify, reconcile` (suspend отсутствует)**. +- `dependencies`/`dependentInstances`: у кластера и эджа пусто; у `fullpipe-vdc` в зависимых три инстанса + `fullpipe-edge` (`b289beb8…`, `86a01033…`, `2c37fed1…`) — рабочий только `2c37fed1…`, два других сироты + от прошлых прогонов. Кластера в зависимых нет → платформа не блокирует удаление эджа/квоты при живом кластере. +- vDC (услуга 21) по инструкции удаляется только через 14 дней после `suspend`; при живых Edge/vApp/VM/кластере + удаление — через поддержку. + +--- + +## 4. `adopt_existing_on_create` — как усыновление реально работает + +- Кластера не было в state, а ресурс есть в `shturval.tf` → план показывал `will be created`. Это **не** + доказательство отсутствия adopt: проверка/adopt выполняются в `Create` на apply + (`provider/internal/resources_gen/150_k8s_sthutrval_cluster_resource.go:211`). +- Дефолт adopt — `false` (там же, строка 136). При существующем инстансе: + `running` либо `suspended` без adopt → hard error «РЕСУРС С ТАКИМ ИМЕНЕМ УЖЕ СУЩЕСТВУЕТ (…RUNNING/SUSPEND)» + (`provider/internal/resources_core/resource_diagnostics_required.go:241-259`). Дубль при этом не создаётся. +- Исправление: добавлен `adopt_existing_on_create = true` в `DEV_STAND/FullPipe/shturval.tf` + (коммит `57abb7b`; бэкап `TMP/backup_2026-09-24/shturval.tf.before-adopt`). +- Результат apply (проверено): state получил `id=94627ff4-…`; провайдер сам выполнил `resume` + (18:26:30, success) — инстанс `running`, `isSuspended=false`; кластер жив (2 ноды Ready); + `nubes_vc_nsxt_snat.snat` в state (`internet-ipv4-v1`), live эдж `ipSpaceName=internet-ipv4-v1` + (modify 18:23:12, success). План после apply: действий по ресурсам нет, только дрейф state + (`edge.state_params.ipSpaceName`: `no-needed` → `internet-ipv4-v1`) и `Changes to Outputs`. + +--- + +## 5. Дизайн «freeze on destroy» (решение) + +**Требование заказчика:** пользователь стенда не должен ничего делать руками и не должен звать DevOps. +`destroy` не удаляет, а «замораживает»: кластер → `suspend`, vDC → `suspend`, эдж → оставить как есть, +SNAT → не выключать, квота IP → не трогать. Следующий `apply` возвращает всё в работу. + +**Что уже есть в ядре:** + +- `resources_core/crud.go:64-96` — `DeleteResourceWithTimeout(..., destroyBehavior, ...)`, режимы + `state_only`/`detach` (ничего не делаем, ресурс забывается) и `suspend` (шлём операцию `suspend`). +- `crud.go:162-214` — adopt на create: для `StateSuspended` при `resumeIfExists` шлёт `resume` и ждёт готовности. +- `nubes_k8s_sthutrval_cluster` и `nubes_vc_vdc` — `suspend_on_destroy` (default `true`) + `adopt_existing_on_create`. +- `nubes_vc_nsxt_snat` и `nubes_vc_org_ip_allocation` — `keep_on_destroy` (в стенде `false`). +- `nubes_vc_nsxt` (эдж) — только `adopt_existing_on_create`; в `provider/resources_yaml/22_vc_nsxt.yaml` + нет операции `suspend` → генератор ставит `deleteMode := "delete"` + (`TOOLS/resource-generator/internal/templates/instance.go:546-552`), т.е. эдж удаляется по-настоящему. + +**Решение:** три режима в одной общей логике — `delete` (дефолт), `suspend` (где сервис умеет), +`keep` → `state_only` (эдж, SNAT, IP-квота). Дефолты провайдера остаются разрушающими; freeze включается +явно в `.tf` стенда. Обязательны предупреждения в выводе destroy («заморожено (suspend)», «оставлен как есть: +эдж», «квота IP не изменена») — иначе freeze выглядит как успешное удаление. + +**Реализация — только через генератор (ручные правки `resources_gen/` затрёт регенерация):** + +1. `TOOLS/resource-generator/internal/types/types.go` — в `LifecycleSpec` добавить + `KeepOnDestroyDefault *bool \`yaml:"keep_on_destroy_default"\``, в `GenResource` — `KeepOnDestroy bool`. +2. `TOOLS/resource-generator/internal/loader/loader.go` — читать новый ключ (дефолт `false`), + как сейчас читается `suspend_on_destroy_default` (строки ~114-118). +3. `TOOLS/resource-generator/internal/templates/instance.go` — эмитить атрибут `keep_on_destroy` + (Optional+Computed, дефолт из YAML) в schema и модель; в `Delete` собирать режим: + `suspend` → `state_only` (keep) → `delete`. +4. `provider/resources_yaml/22_vc_nsxt.yaml` — `keep_on_destroy_default: false`. +5. Регенерация + проверка воспроизводимости (`TOOLS/scripts/10_yaml_stability_run.sh`) → сборка/релиз. + +`resources_core`-ресурсы (SNAT, квота IP) менять не нужно — флаг там уже есть. + +**Конфиг стенда для freeze:** + +| Файл / ресурс | Сейчас | Надо | +|---|---|---| +| `modifiers.tf` → `nubes_vc_org_ip_allocation.org_ip` | `keep_on_destroy = false` (:39) | `true` | +| `modifiers.tf` → `nubes_vc_nsxt_snat.snat` | `keep_on_destroy = false` (:29) | `true` | +| `edge.tf` → `nubes_vc_nsxt.edge` | атрибутов нет | `keep_on_destroy = true` + `adopt_existing_on_create = true` | +| `shturval.tf` → `nubes_k8s_sthutrval_cluster.shturval` | `adopt=true`, `suspend_on_destroy` дефолт | `adopt=true` (есть) + `suspend_on_destroy = true` явно | +| `vdc.tf` → `nubes_vc_vdc.vdc` | `suspend_on_destroy = true`, `adopt = true` (:17,19) | без изменений | + +**Что будет при destroy в режиме freeze:** из state ресурсы уйдут, но в облаке ничего не изменится — +SNAT останется включённым (Delete при `keep=true` печатает «SNAT не выключался», `nsxt_snat_resource.go:185-190`), +квота IP — `count=3`, эдж — running, vDC и кластер — suspended. При следующем `apply` ресурсы создадутся заново +и усыновят живые объекты (`suspend` → `resume`, `running` → просто UID), SNAT/квота отправят те же значения → no-op. + +**Полный teardown** — только явный opt-out (`keep_on_destroy=false` / `suspend_on_destroy=false`) и в порядке: +кластер → `count=0` → SNAT → эдж → vDC. Иначе `count` ниже занятых не опустить, а удаление эджа оставит кластер +без внешнего API/ingress. + +--- + +## 6. Мои ошибки в этой сессии (обязательно к фиксации) + +1. Сказал, что apply «либо даст ошибку, либо создаст дубль кластера» — **неверно**: будет hard error + «РЕСУРС С ТАКИМ ИМЕНЕМ УЖЕ СУЩЕСТВУЕТ», дубль не создаётся (проверено в коде). +2. Интерпретировал `will be created` в плане как доказательство отсутствия adopt — adopt работает в `Create`, не в plan. +3. Перепутал колонки UI: «17/24» — это «Системные сервисы», а «Ingress» — домен-шаблон, а не счётчик. +4. Предлагал ручные обходы (`terraform state rm`, `removed`-блок, `-target`) там, где требуется автоматический + freeze флагами — пользователь это отклонил. + +--- + +## 7. Открытые вопросы / тикет в платформу + +1. Job установщика: Failed-поды живут сутки (`ttlSecondsAfterFinished: 86400`), `backoffLimit: 10`, + очистки нет; установка компонентов идёт до готовности Cilium → EPERM на webhook-ах. +2. Эдж: в `availableOperations` нет `suspend` → «заморозить» его платформенно невозможно, только «не трогать». +3. Квота IP: `count` нельзя опустить ниже занятых, штатного API «занято N» нет — только текст ошибки. +4. vDC: полное удаление только через 14 дней после `suspend`; при живых сущностях — через поддержку. diff --git a/NOTES/40_chat_summaries/CHAT_RESUME_2026-09-24_shturval_freeze.md b/NOTES/40_chat_summaries/CHAT_RESUME_2026-09-24_shturval_freeze.md new file mode 100644 index 0000000..0471f4a --- /dev/null +++ b/NOTES/40_chat_summaries/CHAT_RESUME_2026-09-24_shturval_freeze.md @@ -0,0 +1,45 @@ +# CHAT RESUME — Штурвал dev-00: диагностика + дизайн «freeze on destroy» (2026-09-24) + +> Полная версия с источниками: `NOTES/30_analysis/SHTURVAL_DEV00_DIAG_AND_FREEZE_DESIGN_2026-09-24.md` + +## Что сделано + +1. **Проверка кластера из локали** (контекст `tazet@narod.ru@shturval-dev-00`, API `185.247.187.146:6443`): + всё зелёное — 2 ноды Ready, `shturvalserviceconfigs` 41/41 `ready`, `nodeconfigitems` 4/4, + у всех 35 сервисов есть endpoints. Остался только «мусор»: 4 подвисших пода `kube-system/shturval-init-job` + (3 Error + 1 Unknown) — Job уже `Complete 1/1`, поды уйдут сами по `ttlSecondsAfterFinished: 86400` + (~25.09 14:31 UTC). Причина падений — webhook-вызовы до готовности Cilium: `connect: operation not permitted`. +2. **Расшифрованы счётчики ЛК**: `Pods 44/48` = готовые/всего (48 = поды без Completed); «Системные сервисы» + = число сервисов в режиме `auto` (17/24 → 24/24 после установки); «Ingress» = домен, не счётчик; + ⚠️ на «Pods» = те 4 подвисших пода. +3. **Разобрана ошибка destroy**: `nubes_vc_org_ip_allocation` шлёт `count=0`, платформа не даёт опустить + `count` ниже занятых (2 адреса держит кластер: `.146` API и `.148` ingress; `suspend` адреса не освобождает). + Destroy прервался на аллокации, SNAT успел сняться → «рваное» состояние. +4. **Adopt починен**: добавлен `adopt_existing_on_create = true` в `DEV_STAND/FullPipe/shturval.tf` + (коммит `57abb7b`). Apply усыновил существующий инстанс `94627ff4-…` и сам сделал `resume` (18:26:30) — + кластер снова running, SNAT восстановлен (`internet-ipv4-v1`, modify 18:23:12). +5. **Решение по дизайну** (Опус + наше): три режима destroy в одной логике — `delete` (дефолт), + `suspend` (где сервис умеет), `keep` → `state_only` (эдж, SNAT, квота IP). Реализация — **через генератор** + (`TOOLS/resource-generator`: types/loader/templates + `keep_on_destroy_default` в YAML), не ручными правками + `resources_gen/`. Дефолты провайдера остаются разрушающими; freeze включается явно в `.tf` стенда. + +## Что осталось сделать (по команде) + +1. Правка генератора: `keep_on_destroy` для инстанс-ресурсов (эдж в первую очередь) + предупреждения в `Delete`. +2. `DEV_STAND/FullPipe`: `keep_on_destroy = true` в `modifiers.tf` (:29 snat, :39 квота) и в `edge.tf` + (+ `adopt_existing_on_create = true`), кластеру — явный `suspend_on_destroy = true`. +3. Регенерация + проверка воспроизводимости `10_yaml_stability_run.sh`, сборка/релиз провайдера. +4. Тикет в платформу: TTL/очистка Failed-подов установщика, отсутствие `suspend` у эджа, `count` ниже занятых. + +## Полезное для воспроизведения + +```bash +# состояние кластера +kubectl get nodes; kubectl get pods -A | grep -v -E "Running|Completed" +kubectl -n kube-system get job shturval-init-job -o json | jq '.spec.backoffLimit,.spec.ttlSecondsAfterFinished,.status' +# API ЛК dev (нужны User-Agent и Referer, иначе 403) +TOK=$(tr -d '\n' < secrets/narodDEV.token) +curl -s -H "Authorization: Bearer $TOK" -H "User-Agent: Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36" \ + -H "Referer: https://deck-dev.ngcloud.ru/" \ + 'https://lk-api-gateway-dev.ngcloud.ru/api/v1/svc/instances/94627ff4-33a5-48f2-aca1-695741e0b6a2' +```