14 Commits
Author SHA1 Message Date
Repinoid 09e38928d0 chore: сохранить текущие изменения стендов и заметок 2026-09-29 11:16:57 +03:00
Repinoid a52170cf1e docs(history): vpn-transit-213 — итог оптимизации: автоматизация, провал mux и DNAT, разбор ошибок 2026-09-28 16:45:04 +03:00
Repinoid 25f339bc8a 1 2026-09-28 09:42:25 +03:00
Repinoid 5742457cc0 docs(rules): приведена стилистика copilot-instructions.md — разделы и списки вместо капслока, все правила сохранены 2026-09-28 09:41:27 +03:00
Repinoid 6b6252c42b docs(pipeline): fix vdc CPU example for VM 2026-09-28 09:40:12 +03:00
Repinoid 70b937ea2f docs(pipeline): встроена схема зависимостей сервисов 2026-09-28 09:20:25 +03:00
Repinoid bc1af681aa build(docs): скрипт 04 копирует картинки docs/diagrams (*.svg|png) в публикуемый docs_dir; README диаграмм — раздел о публикации 2026-09-28 09:20:25 +03:00
Repinoid 318b847ede docs(modifiers): квота IP — учёт внешнего адреса ВМ (count = 4), актуальная ссылка на страницу пайплайна 2026-09-28 09:19:14 +03:00
Repinoid 7d762387e7 docs(provider-behavior): vApp/ВМ в таблице ресурсов, в заморозке (suspend/adopt) и в §6-пайплайне (шаги 6-8, лимит vCPU vDC, 14 дней для vApp) 2026-09-28 09:19:00 +03:00
Repinoid 0d7b8c5310 docs(nav): пункт меню пайплайна — добавлены vApp и ВМ 2026-09-28 09:19:00 +03:00
Repinoid 2675495eef docs(pipeline): цепочка расширена vApp -> ВМ — требования (квота CPU vDC, 4-й IP, SSH-ключ), параметры ВМ и образы, внешний доступ, раздел 6 с чек-листом, destroy для vApp/ВМ 2026-09-28 09:18:36 +03:00
Repinoid 034096d16d chore: бэкап FPipeGmail перед добавлением ВМ (без tfvars/tfstate — они в .gitignore) + HISTORY по vpn-transit-213 2026-09-28 08:04:52 +03:00
Repinoid 15cd369ec3 feat(fpipeline): ВМ в пайплайн FPipeGmail — самодостаточный vm.tf (vApp 26 + ВМ 28), внешний IP через общий SNAT, suspend_on_destroy 2026-09-28 07:54:50 +03:00
Repinoid 2fd9ef8aba docs(resume): правки по фактам — ответы по vApp/ВМ (обязательные параметры, suspend, образы, сеть), реальное состояние аккаунтов, исправлено ложное наблюдение про список инстансов (ключ results) 2026-09-27 19:20:22 +03:00
23 changed files with 1426 additions and 46 deletions
+31 -17
View File
@@ -1,17 +1,31 @@
НИКАКОЙ САМОДЕЙТЕЛЬНОСТИ !!! делать ТОЛЬКО ТО НА ЧТО ПОЛУЧЕНО РАЗРЕШЕНИЕ !!!! # Правила работы в этом репозитории
НИКАКИХ ДОГАДОК !!! ЕСТь сомнения - СПРОСИ !!!
НИКОГДА НЕ ДЕЛАЙ ПРЕДПОЛОЖЕНИЙ !!! ## Разрешения и самодеятельность
ВСЕГДА СПРАШИВАЙ, ЕСЛИ НЕ УВЕРЕН !!!
НИКОГДА НЕ ИГНОРИРУЙ ПОЛУЧЕННЫЕ ИНСТРУКЦИИ !!! - **Никакой самодеятельности**: делать только то, на что получено разрешение.
ВСЕГДА ПОДТВЕРЖДАЙ ПОЛУЧЕННЫЕ ИНСТРУКЦИИ !!! - Полученные инструкции **не игнорировать**: соблюдать их и подтверждать.
НИКОГДА НЕ ИЗМЕНЯЙ ИНСТРУКЦИИ БЕЗ РАЗРЕШЕНИЯ !!! - Соблюдать порядок и последовательность инструкций.
ВСЕГДА СОБЛЮДАЙ ПОРЯДОК И ПОСЛЕДОВАТЕЛЬНОСТЬ В ИНСТРУКЦИЯХ !!! - Не превышать свои полномочия.
НИКОГДА НЕ ПРЕВЫШАЙ СВОИ ПОЛНОМОЧИЯ !!! - Соблюдать безопасность и конфиденциальность.
ВСЕГДА СОБЛЮДАЙ БЕЗОПАСНОСТЬ И КОНФИДЕНЦИАЛЬНОСТЬ !!! - Не изменять инструкции без разрешения.
НИКОГДА НЕ ПЕРЕДАВАЙ СЕКРЕТЫ ИНТЕРНЕТУ БЕЗ РАЗРЕШЕНИЯ !!! - Не вызывать другие агенты без разрешения.
НЕ ВЫЗЫВАТЬ ДРУГИЕ АГЕНТЫ БЕЗ РАЗРЕШЕНИЯ !!! - Не передавать секреты в интернет без разрешения.
коммитить после каждой правки, чтобы зафиксировать текущее состояние и избежать потери изменений. Использовать осмысленные сообщения коммитов, отражающие суть изменений.
ВСЕГДА СОХРАНЯТЬ РЕЗЕРВНЫЕ КОПИИ ВАЖНЫХ ФАЙЛОВ ПЕРЕД ВНЕСЕНИЕМ ИЗМЕНЕНИЙ. ## Сомнения и вопросы
НИКОГДА НЕ ПОЛАГАЙСЯ НА ПАМЯТЬ — ВСЕГДА ПРОВЕРЯЙ АКТУАЛЬНОСТЬ ИНСТРУКЦИЙ.
ВСЕГДА СОБЛЮДАЙ ИНСТРУКЦИИ, ДАВАЙТЕ ПОДТВЕРЖДЕНИЯ И НЕ ДЕЛАЙТЕ САМОСТОЯТЕЛЬНЫХ ИЗМЕНЕНИЙ. - **Никаких догадок**: есть сомнения — спроси.
Если не на 100% уверен в распоряжениях - СПРОСИ СНОВА И ПОДТВЕРДИ. НЕ ГАДАЙ ЧТО Я ИМЛ ВВИДУ !!!! - Никогда не делать предположений и не действовать по догадкам.
- Всегда спрашивать, если не уверен.
- Если уверенности в распоряжении нет на 100 % — остановиться, спросить снова и подтвердить, что имел в виду пользователь. Не гадать.
- Перепроверять всё несколько раз.
## Коммиты и бэкапы
- Коммитить после каждой правки — чтобы зафиксировать текущее состояние и не потерять изменения.
- Сообщения коммитов — осмысленные, отражающие суть изменений.
- Всегда сохранять резервные копии важных файлов перед внесением изменений.
## Общий принцип
- Соблюдать инструкции, давать подтверждения и не делать самостоятельных изменений.
- Не полагаться на память — всегда проверять актуальность инструкций.
+189
View File
@@ -0,0 +1,189 @@
# =============================================================================
# Виртуальная машина внутри vApp — услуги 26 (vApp) и 28 (ВМ)
#
# Всё, что относится к ВМ, лежит ТОЛЬКО в этом файле: переменные, их значения
# по умолчанию, оба ресурса и выводы. Чтобы выключить ВМ — удалить или
# закомментировать этот файл (по аналогии с shturval.tf).
#
# Место в цепочке:
# орга (вручную в ЛК) → vDC (21) → Edge (22) → внешние IP → SNAT
# → [ vApp (26) → ВМ (28) ] → Штурвал (150)
#
# Зависимости (из манифестов услуг, сгенерированные ресурсы):
# vApp (26) — nubes_vapp: требует vdc_uid (21) и nsxt_uid (22)
# ВМ (28) — nubes_vc_vm_v3: требует vapp_uid (26)
#
# Внешний доступ: ВМ публикуется за общим SNAT эджа (same_snat = false), для
# этого ipSpace должен быть выделен на организации и включён как SNAT
# (см. modifiers.tf). Нужен ВЫДЕЛЕННЫЙ внешний адрес — same_snat = true.
# ipSpace для ВМ берём тот же, что у SNAT (var.ip_space_name).
#
# Режим destroy: у обеих услуг операция delete требует предварительного
# suspend, поэтому по умолчанию suspend_on_destroy = true («заморозка»).
# Полное удаление vApp возможно только через 14 дней после suspend.
# =============================================================================
# --- Переменные vApp ---
variable "vapp_resource_name" {
type = string
default = "fullpipe-vapp"
description = "Имя услуги «Виртуальный каталог ВМ (vApp)» в ЛК"
}
variable "vapp_name" {
type = string
default = "fullpipe-vapp-01"
description = "Имя vApp. Маска ^[a-z0-9][a-z0-9.-]{3,61}[a-z0-9]$, уникально в организации; участвует в DNS-имени ВМ. НЕ оставлять дефолтом платформы."
}
# --- Переменные ВМ ---
variable "vm_resource_name" {
type = string
default = "fullpipe-vm-01"
description = "Имя услуги «Виртуальная машина» в ЛК"
}
variable "vm_name" {
type = string
default = "web01"
description = "Имя ВМ. Маска ^[a-z0-9][a-z0-9.-]{1,61}[a-z0-9]$. Определяет имя NSX-T IP Set: {vapp_name}-{vm_name}"
}
variable "vm_image" {
type = string
default = "Ubuntu_22-20G"
description = "Образ ОС. Доступные значения: RockyLinux_9-16G-cloudinit, Ubuntu_22-20G, Debian_13-20G. Не изменяется после создания"
}
variable "vm_cpu" {
type = number
default = 2
description = "vCPU (1..64), шт"
}
variable "vm_ram" {
type = number
default = 2
description = "RAM (1..256), GB"
}
variable "vm_disk" {
type = number
default = 20
description = "Дополнительный диск, GB (основной диск зависит от образа)"
}
variable "vm_user_login" {
type = string
default = "ubuntu"
description = "Учётка SSH. Не изменяется после создания"
}
variable "vm_user_public_key" {
type = string
# ВСЕ параметры ВМ живут в этом файле — включая ключ. Удалил файл — ВМ исключена
# из конфига полностью, в terraform.tfvars ничего про ВМ не остаётся.
# Здесь публичный ключ (не секрет), тот же, что в secrets/id_ed25519.pub.
# Переопределить можно в terraform.tfvars — но тогда при исключении ВМ
# надо удалить и эту строку (иного способа у Terraform нет).
default = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIPR8S07Mnku1VlVR/lq6hCKPo9fNzJ+7E0DoE7bkvy4p tazet@narod.ru"
description = "Публичная часть SSH-ключа в формате OpenSSH. Не изменяется после создания. По умолчанию — ключ tazet@narod.ru"
}
variable "vm_access_port_list" {
type = list(object({
port = string
type = string
}))
default = [
{ port = "22", type = "tcp" }
]
description = "Белый список портов для доступа извне; type: tcp | udp | all"
}
variable "vm_access_ip_list" {
type = list(string)
default = ["0.0.0.0/0"]
description = "Белый список адресов, которым разрешён доступ к ВМ. Требует выделенного внешнего IP"
}
variable "vm_same_snat" {
type = bool
default = false
description = "false — публикация за общим SNAT эджа; true — за выделенным внешним IP услуги"
}
# --- vApp (услуга 26) ---
resource "nubes_vapp" "vapp" {
resource_name = var.vapp_resource_name
vapp_name = var.vapp_name
vdc_uid = nubes_vc_vdc.vdc.id # ref 21 — вычислительная инфраструктура
nsxt_uid = nubes_vc_nsxt.edge.id # ref 22 — сеть/маршрутизация
# «Заморозка»: destroy переводит vApp в suspend (delete требует suspend).
suspend_on_destroy = true
# Повторный apply усыновляет существующий vApp, а не падает с
# «РЕСУРС С ТАКИМ ИМЕНЕМ УЖЕ СУЩЕСТВУЕТ».
adopt_existing_on_create = true
# vApp требует готовую сеть (ipSpace на организации + SNAT на эдже).
depends_on = [nubes_vc_nsxt_snat.snat]
}
# --- ВМ (услуга 28) ---
resource "nubes_vc_vm_v3" "vm" {
resource_name = var.vm_resource_name
vm_name = var.vm_name
vapp_uid = nubes_vapp.vapp.id # ref 26 — ВМ размещается в vApp
image_vm = var.vm_image
vm_cpu = var.vm_cpu
vm_ram = var.vm_ram
vm_disk = var.vm_disk
user_login = var.vm_user_login
user_public_key = var.vm_user_public_key
# Внешний доступ
ip_space_name = var.ip_space_name # тот же ipSpace, что у SNAT эджа
same_snat = var.vm_same_snat
access_port_list = jsonencode(var.vm_access_port_list)
access_ip_list = jsonencode(var.vm_access_ip_list)
# «Заморозка»: destroy переводит ВМ в suspend.
suspend_on_destroy = true
adopt_existing_on_create = true
# ВМ создаётся платформой долго — поднимаем таймаут ожидания.
operation_timeout = "15m"
}
# --- Выводы ---
output "vapp_id" {
description = "UID созданного vApp (услуга 26)"
value = nubes_vapp.vapp.id
}
output "vapp_name" {
description = "Имя vApp"
value = nubes_vapp.vapp.vapp_name
}
output "vm_id" {
description = "UID созданной ВМ (услуга 28)"
value = nubes_vc_vm_v3.vm.id
}
output "vm_state_flat" {
description = "Плоский state ВМ — IP-адреса, статус и т.д."
value = nubes_vc_vm_v3.vm.state_out_flat
}
+166
View File
@@ -0,0 +1,166 @@
# VPN transit via VM 213 and Vultr
Date: 2026-09-27 to 2026-09-28
## Goal
Provide access from Russian residential/mobile networks to services restricted by Russian network filtering, while retaining the existing foreign egress on Vultr.
## Verified network facts
- Test host `3060`: `46.39.251.163`, connection from Khimki / Iskratelecom.
- Transit VM `213`: `5.172.178.213`, public egress observed as `5.172.178.65`; hosted in NUBES data centre.
- Vultr addresses: primary `95.179.252.111`; secondary `104.238.177.67`.
- `3060 -> 213`: ICMP approximately 3 ms, 0% loss.
- `213 -> Vultr`: ICMP approximately 34 ms, 0% loss; HTTPS response returned in about 0.07-0.11 s.
- Direct `213 -> Vultr` test file transfer: 10 MiB in 1.59 s, about 6.27 MiB/s / 50.2 Mbit/s.
- Direct `3060 -> Vultr` test file transfer timed out / was throttled.
- Direct `213 -> OVH proof endpoint`: 10 MiB in 1.18 s, about 8.5 MiB/s.
- Direct access from `213` to YouTube and Telegram failed with `HTTP=000` and timeout/SSL errors, while OVH and Google returned HTTP 200. Therefore a foreign egress remains required for those services.
## Persistent changes on VM 213
- Created backup:
- `/etc/nginx/sites-available/check.kube5s.ru.bak_vpn`
- Modified:
- `/etc/nginx/sites-available/check.kube5s.ru`
- Added an Nginx `/ws` reverse-proxy location with:
- upstream `https://95.179.252.111:443`
- SNI `vipien.kube5s.ru`
- upstream Host header `vipien.kube5s.ru`
- WebSocket upgrade headers
- 3600-second proxy timeouts
- Ran `nginx -t` successfully and reloaded Nginx.
- Existing unrelated Nginx warnings about duplicate `contracts.kube5s.ru` server names remained.
## Persistent/previously existing changes on Vultr
The following configuration was read or used during validation:
- `/etc/nginx/conf.d/vipien.conf`: TLS/WebSocket endpoint for `vipien.kube5s.ru`.
- `/etc/v2ray-agent/xray/conf/08_VLESS_ws_inbound.json`: VLESS WebSocket inbound on `127.0.0.1:10086`, path `/ws`.
- `/etc/systemd/system/hysteria-server.service`: Hysteria service was stopped and disabled; it was not changed in this work.
- Xray service was confirmed active.
- Nginx service was confirmed active.
- Cloudflared tunnel configuration was inspected earlier, but it is not used by the final working route.
- A temporary 10 MiB test file was created on Vultr and removed after testing.
## Temporary files on test VM 3060
The following temporary client files were created under `/tmp/xray-test/` for validation and are not repository files:
- `client-cf.json`
- `client-213.json`
- `client-directip.json`
- temporary log/test artifacts where applicable
The files contained test Xray client configurations. They were used only to verify the route from `3060`; no permanent system service was installed there.
## Final tested route
`client in Russia -> 5.172.178.213:443 -> Nginx WebSocket proxy -> 95.179.252.111:443 -> Xray -> Internet`
Final test from `3060` through the route:
- observed outbound IP: `95.179.252.111`
- 10 MiB OVH download: 1.76-1.91 s
- measured speed: approximately 5.5-6.0 MiB/s
## Final client parameters
- Address: `5.172.178.213`
- Port: `443`
- UUID: existing UUID used by the Vultr Xray inbound
- TLS SNI: `check.kube5s.ru`
- WebSocket path: `/ws`
- WebSocket Host: `vipien.kube5s.ru`
The final direct-IP test used Xray 26.3.27. The client-side `allowInsecure` option was not used because this Xray version reports that the option was removed.
## Secondary Vultr IP
Before removal, the Nginx upstream on VM 213 was switched from `104.238.177.67` to `95.179.252.111`. A post-switch end-to-end test succeeded, with outbound IP `95.179.252.111` and approximately 6.0 MiB/s.
No Vultr IP deletion was performed in this work. The secondary address was only confirmed as no longer referenced by the transit configuration.
## Scope audit
- No repository source/configuration files were edited before this record.
- `git status` was clean before this documentation file was created.
- This documentation file is the only workspace file created by the current documentation action.
- Server-side files were changed on VM 213 and earlier on Vultr; temporary test files were also created on VM 3060.
- No commit was created for this record.
## Important limitations
The measurements prove the route worked at test time. They do not guarantee permanent availability: NUBES, Vultr, upstream providers, or network filtering policy can change independently.
## Later the same day: optimisation attempt and its outcome
### Automation created
A reusable, idempotent tool was created outside this repository:
```text
/home/naeel/nubes/HowTo/vpn-transit/vpn-setup.sh check | apply | verify | passthrough | verify-passthrough | client-config | rollback
/home/naeel/nubes/HowTo/vpn-transit/client-config.json generated client config (chmod 600, contains UUID)
/home/naeel/nubes/HowTo/vpn-transit/README.md description, measurements, rollback
/home/naeel/nubes/HowTo/howto-vpn-transit-213-vultr-2026-09-28.md full report
```
Every change is preceded by a timestamped backup and followed by a config test (`nginx -t`, `xray run -test`) with automatic rollback on failure.
### Changes applied
| Host | File | Change | Backup |
|---|---|---|---|
| 213 | `/etc/nginx/sites-available/check.kube5s.ru` | `proxy_buffering off;` added inside `location /ws`, marked `# vpn-transit: proxy_buffering off` | `check.kube5s.ru.bak.1790601681` |
| Vultr | `/etc/v2ray-agent/xray/conf/00_log.json` | `loglevel`: `debug` → `warning` (log had grown to 76 MB), service restarted | `00_log.json.bak.1790601723` |
| 213 | `/usr/local/sbin/vpn-transit-dnat.sh`, `/etc/systemd/system/vpn-transit-dnat.service` | DNAT `213:8443 → 95.179.252.111:443` plus FORWARD rules, enabled at boot | none (rules tagged `vpn-transit`) |
### Measurements after the changes
- Outbound IP: `95.179.252.111`
- Throughput: `5.6–7.3 MiB/s` (10 MiB in 1.4–1.9 s)
- Per-connection latency: `0.23–0.37 s`
- WebSocket upgrade success rate on 213: `14569 / 14573` (99.97%), one `upstream timed out` error
### Hypothesis that was disproved: mux
`verify` compared the tunnel with and without `"mux": {"enabled": true, "concurrency": 8}`:
| Mode | 10 MiB download | Connection behaviour |
|---|---|---|
| without mux | 7.32 MiB/s in 1.43 s | stable |
| with mux | **0 B/s, failed** | after 4 requests connections hang for 15 s |
Conclusion: mux is harmful in the `VLESS + WebSocket behind nginx` combination. It is excluded from the client config. The test remains in the script for re-checking on future Xray versions.
### Optimisation that could not be delivered: removing the second TLS layer
The intended speed fix was to drop one TLS handshake (`client → 213`, then `213 → Vultr`) by forwarding TCP straight through to Vultr.
- `ngx_stream_module.so` is absent on 213, so nginx cannot do SNI-based passthrough without installing `libnginx-mod-stream`.
- Kernel-level DNAT on port 8443 was installed instead, but **does not work**: from outside, port 8443 returns `Connection refused` and the DNAT counter on 213 stays at 0 packets — traffic never reaches the machine.
- Cause: the provider firewall in front of 213 exposes only ports 80 and 443. Measured from `3060`: `3001, 8080, 8443, 8766, 8767, 8888, 18080, 40229` are closed.
- Therefore the second TLS layer can only be removed after the provider opens an additional port. The rules are already installed and would start working immediately once that happens.
### Errors made during this work
1. **Recommended `mux` before measuring it.** The recommendation was given as the main fix and was later disproved by measurement. Correct order: measure first, recommend after.
2. **Changed server configuration before measuring the benefit.** `proxy_buffering off` has no effect on a WebSocket connection after the `101 Switching Protocols` upgrade, and `loglevel` affects only log size. Neither change improves speed, so from the user's point of view nothing changed.
3. **Changed the client config to port 8443 before verifying the port was reachable from outside.** The config was regenerated back to port 443 immediately.
### Net result for the user
Nothing changed for the client: address `5.172.178.213`, port `443`, SNI `check.kube5s.ru`, path `/ws` and the UUID are unchanged, and the previously used link still works. No client-side reconfiguration is required.
The only actionable finding is client-side: the Xray log on Vultr contained **331** `connect: connection refused` to `127.0.0.1:45987`, i.e. the client requested a loopback address, plus Telegram advertises AAAA records while the tunnel is IPv4-only. The generated `client-config.json` addresses both (remote DNS, `queryStrategy: UseIPv4`), but the device itself was not modified.
Separately: **10170** `reset by peer` entries to `157.240.0.13` (Meta infrastructure) are blocking by those sites, unrelated to the transit.
### Scope audit (this action)
- Repository files changed: this document only. `git status` also showed unrelated pre-existing changes (`DEV_STAND/FullPipe/shturval.tf` deletion, `TMP/*` files) that were **not** touched or committed.
- Server-side files changed: as listed in the table above.
- Temporary test files on 3060: `/tmp/xray-test/*` (no permanent service installed).
@@ -86,12 +86,12 @@
| 22 Edge | `fullpipe-edge` | `2c37fed1-e8f8-4a84-8434-7851c7c8b5d6` | `running`, SNAT `internet-ipv4-v1` | | 22 Edge | `fullpipe-edge` | `2c37fed1-e8f8-4a84-8434-7851c7c8b5d6` | `running`, SNAT `internet-ipv4-v1` |
| 19 Организация | `organ` | `57eeacd1-dc7f-4a52-b903-7e5f7d3c1164` | `running`, `vIPConfigure` = `[{internet-ipv4-v1, count: 3}]` | | 19 Организация | `organ` | `57eeacd1-dc7f-4a52-b903-7e5f7d3c1164` | `running`, `vIPConfigure` = `[{internet-ipv4-v1, count: 3}]` |
В том же аккаунте лежат ~10 старых `fullpipe-edge` со `isDeleted=true` (история) — не пугаться. Всего в аккаунте 13 инстансов, в том числе: `dummy-5`/`dummy-6` (1), `External ip` и `vIp` (25, `running`), `A-record` (111, `running`), инстансы mgmt-кластера (21/22, `deleted`). Из 3 инстансов `fullpipe-edge` — 2 `deleted` (история), 1 `running` — не пугаться.
**Облако, аккаунт `tazetdinovn@gmail.com` (токен `secrets/dev.token`, срок до 2026-12-27)** **Облако, аккаунт `tazetdinovn@gmail.com` (токен `secrets/dev.token`, срок до 2026-12-27)**
- Организация `kontra` (`df5ec5f2-5f5d-4c82-952c-3dffa91c61d3`, услуга 19): `running`, `isTrial`, создана 2026-09-24 08:20, `vIPConfigure` = `count: 3`, realm `sandbox.nubes.ru`. - Организация `kontra` (`df5ec5f2-5f5d-4c82-952c-3dffa91c61d3`, услуга 19): `running`, `isTrial`, создана 2026-09-24 08:20, `vIPConfigure` = `count: 3`, realm `sandbox.nubes.ru`.
- Ни vDC, ни эджа, ни Штурвала в этом аккаунте нет. - В этом аккаунте **уже созданы объекты стенда FPipeGmail**: `fullpipe-vdc` (21) — `suspended`, `fullpipe-edge` (22) — `running`, `shturval-dev1` (150) — `suspended` (локальный `state` при этом пуст → следующий `apply` усыновит их по имени и разморозит). Всего 12 инстансов, плюс `kontra` (19), S3-бакеты (13), `s3naeeldev` (12), `mariamdb` (115), `vc_v`/`vc_vdc-1`/`tedj` — `deleted`.
- ⚠️ Наблюдение (требует уточнения перед работами): `GET /instances?page=1&size=200&isDeleted=false` в этом аккаунте вернул **0** инстансов, хотя `GET /instances/{uid}` по `kontra` работает. 25.09 тот же список отдавал 8 записей. Если понадобится поиск по имени — учитывать этот риск. - ⛔ **Исправление ранее ложного наблюдения** (в первой версии этого резюме было «список отдаёт 0 инстансов»): это была **ошибка парсинга ответа с моей стороны** — API отдаёт список в ключе `results`, а не `instances`. Оба аккаунта отдают данные корректно (narod — 13, gmail — 12). Никакой аномалии платформы нет.
**Провайдер / реестр / доки** **Провайдер / реестр / доки**
- DEV в реестре: **`2.0.23`** (последняя), пины `2.0.23` в `DEV_STAND/FullPipe/versions.tf`, `DEV_STAND/FPipeGmail/versions.tf`, `tf_examples/fullpipe_chain/versions.tf`, `.terraform.lock.hcl` FullPipe. - DEV в реестре: **`2.0.23`** (последняя), пины `2.0.23` в `DEV_STAND/FullPipe/versions.tf`, `DEV_STAND/FPipeGmail/versions.tf`, `tf_examples/fullpipe_chain/versions.tf`, `.terraform.lock.hcl` FullPipe.
@@ -127,6 +127,15 @@
- `docs/50_history/12_vm_resource_hardening.md` — правки по надёжности ресурса ВМ. - `docs/50_history/12_vm_resource_hardening.md` — правки по надёжности ресурса ВМ.
- `docs/50_history/23_vapp_uid_inconsistency_displayname_resolve_bug.md` — резолв по имени мог вернуть **удалённый** инстанс (лечится фильтром `isDeleted=false`); проверить, что фикс жив в 2.0.23. - `docs/50_history/23_vapp_uid_inconsistency_displayname_resolve_bug.md` — резолв по имени мог вернуть **удалённый** инстанс (лечится фильтром `isDeleted=false`); проверить, что фикс жив в 2.0.23.
**Что уже выяснено фактами (проверено 27.09 — чтобы не гадать)**
- vApp (26), обязательные параметры create: `vappName`, `vdcUid`, `nsxtUid`. Операции: `create`, `delete`, `suspend`, `resume`; в YAML — `suspend_on_destroy_default: true`. Из инструкции услуги: `delete` требует **предварительного `suspend`**, полное удаление — через 14 дней. Отдельных параметров сети/подсети/gateway у vApp нет — сеть даёт Edge (`nsxtUid`).
- ВМ (28), обязательные параметры create: `vappUid`, `vmName`, `vmCpu`, `vmRam`, `ipSpaceName`, `imageVm`, `userLogin`, `userPublicKey`, `accessPortList` (`vmDisk` — дополнительный диск, не обязателен). Операции: `create`, `delete`, `modify`, `reconcile`, `redeploy`, `resume`, `suspend`; в YAML — `suspend_on_destroy_default: true`. В modify-наборе есть параметр `sameSnat` (разобраться, что даёт).
- `ipSpaceName` у ВМ — «Public IP setting», допускает значение `no-needed` (без публичного адреса). Если ВМ нужен внешний IP, квоту организации придётся увеличивать: сейчас `count = 3` и все три держит кластер Штурвала, а `suspend` адреса не освобождает.
- Соответствие кодов и ID параметров ВМ (`docs/20_discovery/vm_service.md`): `vappUid` 407, `vmName` 408, `vmCpu` 409/493, `vmRam` 410/494, `vmDisk` 411/495, `ipSpaceName` 412/496, `accessIpList` 413/497, `imageVm` 414, `cloudInit` 415, `userLogin` 416, `userPublicKey` 417, `accessPortList` 448/498, `needAddZabbixTemplate` 449/499.
- **Список образов (`imageVm`) в репозитории отсутствует**: в документации зафиксирован только пример `Ubuntu_22-20G`; HAR с созданием ВМ (`vmOK.har`, `vmsuspendresumemodify.har`) были в старом репо `/home/naeel/terra` — в этом репо в `HAR/` их нет (там `org*`, `vdc`, `edge_`, `ipSpace0`, `pgmodify`, `dummy`, `globak`). Значит: смотреть список в ЛК либо снять свежий HAR.
- Живых инстансов vApp (26) и ВМ (28) сейчас **нет ни в одном аккаунте** → `availableOperations` по ним снять не с чего, проверять после первого создания.
- Наличие `suspend` у обоих сервисов означает, что режим «заморозка» (`suspend_on_destroy` / `keep_on_destroy`) у них применим — как у кластера и vDC.
**Что предстоит решить (НЕ выбрано, требует решения пользователя)** **Что предстоит решить (НЕ выбрано, требует решения пользователя)**
1. Где создавать vApp: в `DEV_STAND/FullPipe` (проверенный стенд, аккаунт `narod`) или отдельным файлом-аналогом `shturval.tf` (например `vm.tf`) — по сложившейся конвенции «всё про сервис в одном файле». 1. Где создавать vApp: в `DEV_STAND/FullPipe` (проверенный стенд, аккаунт `narod`) или отдельным файлом-аналогом `shturval.tf` (например `vm.tf`) — по сложившейся конвенции «всё про сервис в одном файле».
2. Очерёдность: ВМ до Штурвала или после (влияет и на внешние адреса, и на время apply). 2. Очерёдность: ВМ до Штурвала или после (влияет и на внешние адреса, и на время apply).
@@ -244,5 +253,5 @@ curl -s https://tf-registry.containerk8s.services.ngcloud.ru/v1/providers/nubes-
2. Регистр ключей в `lookupLiveParam` — `provider/internal/core/operation_cfs.go:38` (в прежних заметках был указан неверный файл); нужна живая проверка. 2. Регистр ключей в `lookupLiveParam` — `provider/internal/core/operation_cfs.go:38` (в прежних заметках был указан неверный файл); нужна живая проверка.
3. Тикеты в платформу: TTL/очистка Failed-подов установщика Штурвала (порядок установки компонентов до готовности Cilium); у Edge нет операции `suspend`. 3. Тикеты в платформу: TTL/очистка Failed-подов установщика Штурвала (порядок установки компонентов до готовности Cilium); у Edge нет операции `suspend`.
4. `TOOLS/config/dev/profile.env`: `VERSION="2.0.17"` — конфиг отстаёт от реестра (`2.0.23`); правка конфига без команды не делалась. 4. `TOOLS/config/dev/profile.env`: `VERSION="2.0.17"` — конфиг отстаёт от реестра (`2.0.23`); правка конфига без команды не делалась.
5. Аномалия списка инстансов в аккаунте `tazetdinovn@gmail.com` (0 записей при работающем GET по uid) — разобраться перед работами по FPipeGmail/ВМ. 5. `DEV_STAND/FPipeGmail`: локальный `state` пуст, но объекты в облаке **уже есть** (`fullpipe-vdc` suspended, `fullpipe-edge` running, `shturval-dev1` suspended) — при применении Terraform их усыновит.
6. `DEV_STAND/FPipeGmail` — применять или нет (сейчас `plan` = 5 ресурсов, `state` пуст) — решение за пользователем. 6. `DEV_STAND/FPipeGmail` — применять или нет (сейчас `plan` = 5 ресурсов, `state` пуст) — решение за пользователем.
+28
View File
@@ -0,0 +1,28 @@
resource "nubes_vc_nsxt" "edge" {
resource_name = var.nsxt_resource_name
# Тип родительской услуги: "vdc" (нужен vdc_uid) или "vdcGroup" (нужен vdc_group_uid)
vdc_type = var.nsxt_vdc_type
# refSvc-поле: принимает UUID или имя. Здесь берём UID созданного VDC,
# чтобы Edge гарантированно создавался после vDC.
vdc_uid = nubes_vc_vdc.vdc.id
need_enable_avi = var.nsxt_need_enable_avi
virtual_services_count = var.nsxt_virtual_services_count
# routed-сеть, которую разворачивает Edge (SingleNestedAttribute -> объект)
routed_net_configuration = {
ip_addr_pool = var.nsxt_ip_addr_pool
main_dns = var.nsxt_main_dns
second_dns = var.nsxt_second_dns
}
# «Заморозка»: destroy НЕ удаляет эдж (у платформы для эджа нет операции suspend),
# а только убирает его из состояния. Для полного удаления — keep_on_destroy = false.
keep_on_destroy = true
# Повторный apply усыновляет уже работающий эдж, а не падает с
# «РЕСУРС С ТАКИМ ИМЕНЕМ УЖЕ СУЩЕСТВУЕТ (RUNNING)».
adopt_existing_on_create = true
}
@@ -0,0 +1,60 @@
# =============================================================================
# Ресурсы-модификаторы (операции modify, которых нет в create-схеме ресурсов)
#
# Порядок строго такой:
# орга (создана вручную в ЛК)
# -> nubes_vc_vdc.vdc
# -> nubes_vc_nsxt.edge
# -> nubes_vc_org_ip_allocation (выделение внешних IP на орге)
# -> nubes_vc_nsxt_snat (SNAT на эдже этим ipSpace)
#
# Почему аллокация ПОСЛЕ эджа: платформа строит список ipSpace из состояния
# `job.vcd.networkProvider` / `job.vcd.providerGateway`, то есть требует уже
# созданный vDC и Edge. Иначе modify на орге падает
# («Can't cast Complex Object Type Struct to String»).
# =============================================================================
# 1. Внешние IP на организации (modify: vIPConfigure, массив перезаписывается целиком)
resource "nubes_vc_org_ip_allocation" "org_ip" {
organization = var.organization
vip_configure = jsonencode([
{
name = var.ip_space_name
count = var.ip_count
}
])
# true = «заморозка»: destroy не трогает квоту внешних IP (кластер Штурвала держит
# адреса, опустить count ниже занятых платформа не даёт). Для полного удаления — false
# (и только после удаления кластера).
keep_on_destroy = true
depends_on = [nubes_vc_nsxt.edge]
}
# 2. SNAT на эдже (modify: ipSpaceName)
resource "nubes_vc_nsxt_snat" "snat" {
nsxt_uid = nubes_vc_nsxt.edge.id
ip_space_name = var.ip_space_name
# true = «заморозка»: destroy не выключает SNAT на эдже. Для полного удаления — false.
keep_on_destroy = true
# ipSpace должен быть уже выделен на организации
depends_on = [nubes_vc_org_ip_allocation.org_ip]
}
output "allocated_org_ip" {
description = "Выделено внешних IP на организации"
value = {
organization = var.organization
ip_space_name = var.ip_space_name
ip_count = var.ip_count
}
}
output "snat_ip_space" {
description = "ipSpace, включённый как SNAT на эдже"
value = nubes_vc_nsxt_snat.snat.ip_space_name
}
@@ -0,0 +1,29 @@
output "vdc_id" {
description = "UID созданного VDC"
value = nubes_vc_vdc.vdc.id
}
output "vdc_name" {
description = "Имя VDC"
value = nubes_vc_vdc.vdc.resource_name
}
output "vdc_state_params" {
description = "Параметры состояния VDC из API"
value = nubes_vc_vdc.vdc.state_params
}
output "nsxt_id" {
description = "UID созданного Edge (vc_nsxt)"
value = nubes_vc_nsxt.edge.id
}
output "nsxt_name" {
description = "Имя Edge (vc_nsxt)"
value = nubes_vc_nsxt.edge.resource_name
}
output "nsxt_state_params" {
description = "Параметры состояния Edge (vc_nsxt) из API"
value = nubes_vc_nsxt.edge.state_params
}
@@ -0,0 +1,4 @@
provider "nubes" {
api_token = var.api_token
api_endpoint = var.api_endpoint
}
@@ -0,0 +1,161 @@
# =============================================================================
# Kubernetes кластер Штурвал — сервис 150, ресурс nubes_k8s_sthutrval_cluster
# (НЕ 148 «Менеджмент Kubernetes кластер Штурвал» — это другой сервис)
#
# Всё, что относится к Штурвалу, лежит ТОЛЬКО в этом файле: переменные, их
# значения по умолчанию и сам ресурс. Чтобы выключить Штурвал — удалить файл
# или закомментировать ресурс.
#
# Порядок (чек-лист из инструкции на услугу в ЛК):
# 1) Организация в Cloud Director — создана вручную в ЛК
# 2) nubes_vc_vdc.vdc — есть
# 3) nubes_vc_nsxt.edge — есть, обязательно ALB + AVI VS >= 3
# 4) внешние адреса в организации — суммарно >= 3 (nubes_vc_org_ip_allocation)
# 5) SNAT на Edge — nubes_vc_nsxt_snat
# 6) Kubernetes кластер Штурвал — этот ресурс
#
# Минимальные требования к кластеру: мастер-нод >= 1, воркер-нод >= 1,
# 4 vCPU / 8 GB RAM / 50 GB диска на ноду.
# =============================================================================
# --- Переменные Штурвала ---
variable "shturval_resource_name" {
type = string
default = "shturval-dev1"
description = "Имя услуги «Kubernetes кластер Штурвал» в ЛК"
}
variable "shturval_cluster_name" {
type = string
default = "shturval-dev-01"
description = "Имя кластера внутри Штурвала"
}
variable "shturval_app_version" {
type = string
default = "2.14.0"
description = "Версия Штурвала (значение по умолчанию платформы — 2.14.0)"
}
variable "shturval_cp_sizing_policy" {
type = string
default = "TKG 4CPU 8RAM"
description = "Политика размера control plane: 4 vCPU / 8 GB (минимум по инструкции). Должна существовать в ресурсной платформе vDC — список политик берётся из услуги «Виртуальный датацентр»"
}
variable "shturval_cp_sizing_disk" {
type = number
default = 50
description = "Диск control plane, ГБ (минимум 50)"
}
variable "shturval_cp_count" {
type = number
default = 1
description = "Количество мастер-нод: 1, 3 или 5"
}
variable "shturval_worker_group_name" {
type = string
default = "workers-shturval-dev"
description = "Имя группы воркеров (уникальное в кластере; допустимы строчные латинские буквы, цифры и дефис)"
}
variable "shturval_worker_sizing_policy" {
type = string
default = "TKG 4CPU 8RAM"
description = "Политика размера воркеров: 4 vCPU / 8 GB (минимум по инструкции)"
}
variable "shturval_worker_sizing_disk" {
type = number
default = 50
description = "Диск воркеров, ГБ (минимум 50)"
}
variable "shturval_worker_count" {
type = number
default = 1
description = "Количество воркер-нод (минимум 1)"
}
# --- Значения, которые собираются из переменных ---
locals {
# Группы воркеров передаются JSON-строкой ВНУТРЬ услуги как есть, поэтому ключи
# должны быть ровно такими, как в манифесте услуги 150: groupName, sizingPolicy,
# sizingDisk, count, autoscale, labelDeck.
# ВНИМАНИЕ: в сгенерированном примере провайдера (docs → Example) ключи показаны
# в snake_case — это ошибка генератора, платформа на них падает с
# «Cannot invoke method split() on null object» (не находит groupName → null).
shturval_worker_config = jsonencode([
{
groupName = var.shturval_worker_group_name
sizingPolicy = var.shturval_worker_sizing_policy
sizingDisk = var.shturval_worker_sizing_disk
count = var.shturval_worker_count
autoscale = false # автоскейл выключен
labelDeck = true # разрешить разворачивать услуги из ЛК на этих нодах
}
])
}
# --- Ресурс Штурвала ---
resource "nubes_k8s_sthutrval_cluster" "shturval" {
resource_name = var.shturval_resource_name
# Кластер Штурвала уже существует (инстанс «shturval-dev») и в проде не
# удаляется неделями, поэтому ресурс должен УСЫНОВИТЬ существующий инстанс,
# а не падать с «РЕСУРС С ТАКИМ ИМЕНЕМ УЖЕ СУЩЕСТВУЕТ (SUSPEND)».
# Проверка/adopt выполняются в Create на apply (в plan будет «will be created»).
adopt_existing_on_create = true
# «Заморозка»: destroy приостанавливает кластер (suspend), а не удаляет.
# Следующий apply усыновит его и разморозит (resume).
suspend_on_destroy = true
# Штурвал создаётся долго (десятки минут) — поднимаем таймаут ожидания,
# иначе провайдер сдаётся на дефолтных 600 с.
operation_timeout = "60m"
startup_configuration = {
# vDC и Edge из этого же конфига (обязательные поля)
vdc_uid = nubes_vc_vdc.vdc.id
nsxt_uid = nubes_vc_nsxt.edge.id
cluster_name = var.shturval_cluster_name
# Дополнительные возможности кластера (в ЛК — галочки при создании)
ex_logging = true # логи в Loki (без него логи услуг не видны в ЛК)
ex_monitoring = true # метрики в VictoriaMetrics (без него метрик в ЛК нет)
ex_local_csi = true
ex_vip = true
ex_update = true
ex_ingress = true
ex_named_csi = true
}
cluster_configuration = {
app_version = var.shturval_app_version
}
control_plane_configuration = {
sizing_policy = var.shturval_cp_sizing_policy
sizing_disk = var.shturval_cp_sizing_disk
count = var.shturval_cp_count
}
worker_configuration = local.shturval_worker_config
access_configuration = {
need_external_address_api = true # внешний адрес для Kubernetes API (false недопустим)
access_ip_list_api = jsonencode([]) # пусто = доступ всем
need_external_address_ingress = true # внешний адрес для Ingress
access_ip_list_ingress = jsonencode([]) # пусто = доступ всем
}
# Кластер поднимается только после готовой сети: vDC -> Edge -> внешние IP -> SNAT
depends_on = [nubes_vc_nsxt_snat.snat]
}
@@ -0,0 +1,116 @@
variable "api_token" {
type = string
sensitive = true
description = "API-токен Nubes"
}
variable "api_endpoint" {
type = string
default = "https://lk-api-gateway-dev.ngcloud.ru/api/v1/svc"
description = "API Gateway URL"
}
# Имя (display_name, напр. "kontora") ИЛИ UUID организации из ЛК
variable "organization" {
type = string
description = "Имя или UUID организации (vc_org)"
}
# --- Модификаторы (IP на орге + SNAT на эдже) ---
variable "ip_space_name" {
type = string
description = "Имя ipSpace, доступное организации (смотреть в ЛК, напр. internet-ipv4-v1)"
}
variable "ip_count" {
type = string
default = "3"
description = "Сколько внешних IP выделить на организации (count — строка)"
}
variable "vdc_resource_name" {
type = string
default = "fullpipe-vdc"
description = "Имя VDC"
}
variable "vdc_network_provider" {
type = string
default = null
description = "Сетевой провайдер. Заполнить значением из текущей страницы ЛК"
}
variable "vdc_provider_vdc" {
type = string
default = null
description = "Provider VDC. Заполнить значением из текущей страницы ЛК"
}
variable "vdc_cpu_allocated" {
type = number
default = 8
description = "vCPU (шт.)"
}
variable "vdc_cpu_guaranteed" {
type = number
default = 0
description = "Резервирование vCPU (%, допустимо: 0, 50, 80)"
}
variable "vdc_mem_allocated" {
type = number
default = 32
description = "RAM (GB)"
}
variable "vdc_storage_config" {
type = string
default = "[{\"name\":\"SATA\",\"size\":\"200\"}]"
description = "Дисковое хранилище (JSON-массив, size в GB). Имя политики должно существовать в ресурсном пуле (например, SATA, SSD)"
}
# --- vc_nsxt (Сетевой шлюз периметра / Edge) ---
variable "nsxt_resource_name" {
type = string
default = "fullpipe-edge"
description = "Имя Edge (vc_nsxt)"
}
variable "nsxt_vdc_type" {
type = string
default = "vdc"
description = "Тип родительской услуги: vdc или vdcGroup"
}
variable "nsxt_need_enable_avi" {
type = bool
default = true
description = "Включить AVI Load Balancer (ALB)"
}
variable "nsxt_virtual_services_count" {
type = number
default = 3
description = "Кол-во виртуальных сервисов на AVI (1..4; Штурвал: ≥ 3)"
}
variable "nsxt_ip_addr_pool" {
type = string
default = "10.10.102.0/24"
description = "Адресный пул routed-сети (маска /24 обязательна)"
}
variable "nsxt_main_dns" {
type = string
default = "81.22.46.22"
description = "Основной DNS"
}
variable "nsxt_second_dns" {
type = string
default = "185.247.187.77"
description = "Второй DNS"
}
+20
View File
@@ -0,0 +1,20 @@
resource "nubes_vc_vdc" "vdc" {
resource_name = var.vdc_resource_name
# Организация: имя из ЛК ("kontora") или точный UUID
organization_uid = var.organization
network_provider = var.vdc_network_provider
provider_vdc = var.vdc_provider_vdc
cpu_allocated = var.vdc_cpu_allocated
cpu_guaranteed = var.vdc_cpu_guaranteed
mem_allocated = var.vdc_mem_allocated
# JSON-массив дисковых политик (size в GB)
storage_config = var.vdc_storage_config
suspend_on_destroy = true
adopt_existing_on_create = true
}
@@ -0,0 +1,10 @@
terraform {
required_version = ">= 1.5.0"
required_providers {
nubes = {
source = "tf-registry.containerk8s.services.ngcloud.ru/nubes-dev/nubes"
version = "2.0.23"
}
}
}
+183
View File
@@ -0,0 +1,183 @@
# =============================================================================
# Виртуальная машина внутри vApp — услуги 26 (vApp) и 28 (ВМ)
#
# Всё, что относится к ВМ, лежит ТОЛЬКО в этом файле: переменные, их значения
# по умолчанию, оба ресурса и выводы. Чтобы выключить ВМ — удалить или
# закомментировать этот файл (по аналогии с shturval.tf).
#
# Место в цепочке:
# орга (вручную в ЛК) → vDC (21) → Edge (22) → внешние IP → SNAT
# → [ vApp (26) → ВМ (28) ] → Штурвал (150)
#
# Зависимости (из манифестов услуг, сгенерированные ресурсы):
# vApp (26) — nubes_vapp: требует vdc_uid (21) и nsxt_uid (22)
# ВМ (28) — nubes_vc_vm_v3: требует vapp_uid (26)
#
# Внешний доступ: ВМ публикуется за общим SNAT эджа (same_snat = false), для
# этого ipSpace должен быть выделен на организации и включён как SNAT
# (см. modifiers.tf). Нужен ВЫДЕЛЕННЫЙ внешний адрес — same_snat = true.
# ipSpace для ВМ берём тот же, что у SNAT (var.ip_space_name).
#
# Режим destroy: у обеих услуг операция delete требует предварительного
# suspend, поэтому по умолчанию suspend_on_destroy = true («заморозка»).
# Полное удаление vApp возможно только через 14 дней после suspend.
# =============================================================================
# --- Переменные vApp ---
variable "vapp_resource_name" {
type = string
default = "fullpipe-vapp"
description = "Имя услуги «Виртуальный каталог ВМ (vApp)» в ЛК"
}
variable "vapp_name" {
type = string
default = "fullpipe-vapp-01"
description = "Имя vApp. Маска ^[a-z0-9][a-z0-9.-]{3,61}[a-z0-9]$, уникально в организации; участвует в DNS-имени ВМ. НЕ оставлять дефолтом платформы."
}
# --- Переменные ВМ ---
variable "vm_resource_name" {
type = string
default = "fullpipe-vm-01"
description = "Имя услуги «Виртуальная машина» в ЛК"
}
variable "vm_name" {
type = string
default = "web01"
description = "Имя ВМ. Маска ^[a-z0-9][a-z0-9.-]{1,61}[a-z0-9]$. Определяет имя NSX-T IP Set: {vapp_name}-{vm_name}"
}
variable "vm_image" {
type = string
default = "Ubuntu_22-20G"
description = "Образ ОС. Доступные значения: RockyLinux_9-16G-cloudinit, Ubuntu_22-20G, Debian_13-20G. Не изменяется после создания"
}
variable "vm_cpu" {
type = number
default = 2
description = "vCPU (1..64), шт"
}
variable "vm_ram" {
type = number
default = 2
description = "RAM (1..256), GB"
}
variable "vm_disk" {
type = number
default = 20
description = "Дополнительный диск, GB (основной диск зависит от образа)"
}
variable "vm_user_login" {
type = string
default = "ubuntu"
description = "Учётка SSH. Не изменяется после создания"
}
variable "vm_user_public_key" {
type = string
description = "Публичная часть SSH-ключа в формате OpenSSH. Не изменяется после создания. Значение — в terraform.tfvars"
}
variable "vm_access_port_list" {
type = list(object({
port = string
type = string
}))
default = [
{ port = "22", type = "tcp" }
]
description = "Белый список портов для доступа извне; type: tcp | udp | all"
}
variable "vm_access_ip_list" {
type = list(string)
default = ["0.0.0.0/0"]
description = "Белый список адресов, которым разрешён доступ к ВМ. Требует выделенного внешнего IP"
}
variable "vm_same_snat" {
type = bool
default = false
description = "false — публикация за общим SNAT эджа; true — за выделенным внешним IP услуги"
}
# --- vApp (услуга 26) ---
resource "nubes_vapp" "vapp" {
resource_name = var.vapp_resource_name
vapp_name = var.vapp_name
vdc_uid = nubes_vc_vdc.vdc.id # ref 21 — вычислительная инфраструктура
nsxt_uid = nubes_vc_nsxt.edge.id # ref 22 — сеть/маршрутизация
# «Заморозка»: destroy переводит vApp в suspend (delete требует suspend).
suspend_on_destroy = true
# Повторный apply усыновляет существующий vApp, а не падает с
# «РЕСУРС С ТАКИМ ИМЕНЕМ УЖЕ СУЩЕСТВУЕТ».
adopt_existing_on_create = true
# vApp требует готовую сеть (ipSpace на организации + SNAT на эдже).
depends_on = [nubes_vc_nsxt_snat.snat]
}
# --- ВМ (услуга 28) ---
resource "nubes_vc_vm_v3" "vm" {
resource_name = var.vm_resource_name
vm_name = var.vm_name
vapp_uid = nubes_vapp.vapp.id # ref 26 — ВМ размещается в vApp
image_vm = var.vm_image
vm_cpu = var.vm_cpu
vm_ram = var.vm_ram
vm_disk = var.vm_disk
user_login = var.vm_user_login
user_public_key = var.vm_user_public_key
# Внешний доступ
ip_space_name = var.ip_space_name # тот же ipSpace, что у SNAT эджа
same_snat = var.vm_same_snat
access_port_list = jsonencode(var.vm_access_port_list)
access_ip_list = jsonencode(var.vm_access_ip_list)
# «Заморозка»: destroy переводит ВМ в suspend.
suspend_on_destroy = true
adopt_existing_on_create = true
# ВМ создаётся платформой долго — поднимаем таймаут ожидания.
operation_timeout = "15m"
}
# --- Выводы ---
output "vapp_id" {
description = "UID созданного vApp (услуга 26)"
value = nubes_vapp.vapp.id
}
output "vapp_name" {
description = "Имя vApp"
value = nubes_vapp.vapp.vapp_name
}
output "vm_id" {
description = "UID созданной ВМ (услуга 28)"
value = nubes_vc_vm_v3.vm.id
}
output "vm_state_flat" {
description = "Плоский state ВМ — IP-адреса, статус и т.д."
value = nubes_vc_vm_v3.vm.state_out_flat
}
@@ -0,0 +1,18 @@
НИКАКОЙ САМОДЕЙТЕЛЬНОСТИ !!! делать ТОЛЬКО ТО НА ЧТО ПОЛУЧЕНО РАЗРЕШЕНИЕ !!!!
НИКАКИХ ДОГАДОК !!! ЕСТь сомнения - СПРОСИ !!!
НИКОГДА НЕ ДЕЛАЙ ПРЕДПОЛОЖЕНИЙ !!!
ВСЕГДА СПРАШИВАЙ, ЕСЛИ НЕ УВЕРЕН !!!
НИКОГДА НЕ ИГНОРИРУЙ ПОЛУЧЕННЫЕ ИНСТРУКЦИИ !!!
ВСЕГДА ПОДТВЕРЖДАЙ ПОЛУЧЕННЫЕ ИНСТРУКЦИИ !!!
НИКОГДА НЕ ИЗМЕНЯЙ ИНСТРУКЦИИ БЕЗ РАЗРЕШЕНИЯ !!!
ВСЕГДА СОБЛЮДАЙ ПОРЯДОК И ПОСЛЕДОВАТЕЛЬНОСТЬ В ИНСТРУКЦИЯХ !!!
НИКОГДА НЕ ПРЕВЫШАЙ СВОИ ПОЛНОМОЧИЯ !!!
ВСЕГДА СОБЛЮДАЙ БЕЗОПАСНОСТЬ И КОНФИДЕНЦИАЛЬНОСТЬ !!!
НИКОГДА НЕ ПЕРЕДАВАЙ СЕКРЕТЫ ИНТЕРНЕТУ БЕЗ РАЗРЕШЕНИЯ !!!
НЕ ВЫЗЫВАТЬ ДРУГИЕ АГЕНТЫ БЕЗ РАЗРЕШЕНИЯ !!!
коммитить после каждой правки, чтобы зафиксировать текущее состояние и избежать потери изменений. Использовать осмысленные сообщения коммитов, отражающие суть изменений.
ВСЕГДА СОХРАНЯТЬ РЕЗЕРВНЫЕ КОПИИ ВАЖНЫХ ФАЙЛОВ ПЕРЕД ВНЕСЕНИЕМ ИЗМЕНЕНИЙ.
НИКОГДА НЕ ПОЛАГАЙСЯ НА ПАМЯТЬ — ВСЕГДА ПРОВЕРЯЙ АКТУАЛЬНОСТЬ ИНСТРУКЦИЙ.
ВСЕГДА СОБЛЮДАЙ ИНСТРУКЦИИ, ДАВАЙТЕ ПОДТВЕРЖДЕНИЯ И НЕ ДЕЛАЙТЕ САМОСТОЯТЕЛЬНЫХ ИЗМЕНЕНИЙ.
Если не на 100% уверен в распоряжениях - СПРОСИ СНОВА И ПОДТВЕРДИ. НЕ ГАДАЙ ЧТО Я ИМЛ ВВИДУ !!!!
ЕСЛИ В ЧЁМ совменваешься - ОСТАНОВИСЬ И СПРОСИ !!!! НИКОГДА не действуй по догадкам и преположенгиям !!! ПЕРЕПРОВЕРЯЙ ВСЁ НЕСКОЛЬКО РАЗ !!!
+78
View File
@@ -0,0 +1,78 @@
# Список языковых моделей и агентов
> Данные перенесены из предоставленного списка. Достоверность названий, параметров и стоимости отдельно не проверялась.
## 1. DeepSeek
| Имя | Размер контекста | Возможности | Вход | Выход |
|---|---:|---|---:|---:|
| DeepSeek V4 Flash | 1M | Инструменты, Видение | Не указана | Не указана |
| DeepSeek V4 Flash Vision Exp | 1M | Инструменты, Видение | Не указана | Не указана |
| DeepSeek V4 Pro | 1M | Инструменты, Видение | Не указана | Не указана |
| DeepSeek V4.1 Hash | 1M | Инструменты, Видение | Не указана | Не указана |
## 2. Copilot
| Имя | Размер контекста | Возможности | Вход | Выход |
|---|---:|---|---:|---:|
| Auto | Не указан | Инструменты | Не указана | Не указана |
| Claude Fable 5 | 1M | Инструменты, Видение | 1000 | 5000 |
| Claude Fable 5.1 | 1M | Инструменты, Видение | 1000 | 5000 |
| Claude Haiku 4.5 | 160K | Инструменты, Видение | 100 | 500 |
| Claude Opus 4.7 | 1M | Инструменты, Видение | 500 | 2500 |
| Claude Opus 4.8 | 1M | Инструменты, Видение | 500 | 2500 |
| Claude Opus 4.8 (fast mode) (Preview) | 1M | Инструменты, Видение | 1000 | 5000 |
| Claude Opus 5 | 1M | Инструменты, Видение | 500 | 2500 |
| Claude Opus 5.5 | 1M | Инструменты, Видение | 400 | 2000 |
| Claude Sonnet 5 | 1M | Инструменты, Видение | 200 | 1000 |
| Gemini 3.5 Flash | 1M | Инструменты, Видение | 150 | 900 |
| Gemini 3.6 Flash | 1M | Инструменты, Видение | 75 | 375 |
| Gemini 3.7 Flash | 1M | Инструменты, Видение | 75 | 375 |
| Gemini 3.8 Flash | 1M | Инструменты, Видение | 75 | 375 |
| GPT-5 mini | 192K | Инструменты, Видение | 25 | 200 |
| GPT-5.3-Codex | 400K | Инструменты, Видение | 175 | 1400 |
| GPT-5.4 | 1M | Инструменты, Видение | 250 | 1500 |
| GPT-5.4 mini | 400K | Инструменты, Видение | 75 | 450 |
| GPT-5.5 | 1M | Инструменты, Видение | 500 | 3000 |
| GPT-5.6 Luna | 1M | Инструменты, Видение | 20 | 120 |
| GPT-5.6 Sol | 1M | Инструменты, Видение | 400 | 2000 |
| GPT-5.6 Terra | 1M | Инструменты, Видение | 200 | 1200 |
| GPT-6 Astra | 1M | Инструменты, Видение | 1000 | 5000 |
| GPT-6 Luna | 1M | Инструменты, Видение | 10 | 50 |
## 3. Общий список языковых моделей
Стоимость указана в кредитах за 1 млн токенов.
| Имя | Размер контекста | Возможности | Вход | Выход |
|---|---:|---|---:|---:|
| Claude Fable 5 | 1M | Инструменты, Видение | 1000 | 5000 |
| Claude Fable 5.1 | 1M | Инструменты, Видение | 1000 | 5000 |
| Claude Haiku 4.5 | 160K | Инструменты, Видение | 100 | 500 |
| Claude Opus 4.7 | 1M | Инструменты, Видение | 500 | 2500 |
| Claude Opus 4.8 | 1M | Инструменты, Видение | 500 | 2500 |
| Claude Opus 4.8 (fast mode) | 1M | Инструменты, Видение | 1000 | 5000 |
| Claude Opus 5 | 1M | Инструменты, Видение | 500 | 2500 |
| Claude Opus 5.5 | 1.1M | Инструменты, Видение | 400 | 2000 |
| Claude Sonnet 5 | 1M | Инструменты, Видение | 200 | 1000 |
| Gemini 3.5 Flash | 1M | Инструменты, Видение | 150 | 900 |
| Gemini 3.6 Flash | 1M | Инструменты, Видение | 75 | 375 |
| Gemini 3.7 Flash | 1M | Инструменты, Видение | 75 | 375 |
| Gemini 3.8 Flash | 1M | Инструменты, Видение | 75 | 375 |
| GPT-5 mini | 192K | Инструменты, Видение | 25 | 200 |
| GPT-5.3-Codex | 400K | Инструменты, Видение | 175 | 1400 |
| GPT-5.4 | 1M | Инструменты, Видение | 250 | 1500 |
| GPT-5.4 mini | 400K | Инструменты, Видение | 75 | 450 |
| GPT-5.5 | 1M | Инструменты, Видение | 500 | 3000 |
| GPT-5.6 Luna | 1M | Инструменты, Видение | 20 | 120 |
| GPT-5.6 Sol | 1M | Инструменты, Видение | 400 | 2000 |
| GPT-5.6 Terra | 1M | Инструменты, Видение | 200 | 1200 |
| GPT-6 Astra | 1.1M | Инструменты, Видение | 1000 | 5000 |
| GPT-6 Luna | 1M | Инструменты, Видение | 10 | 50 |
| GPT-6 Sol | 1M | Инструменты, Видение | 200 | 1000 |
| Grok 4.5 | 500K / 553K | Инструменты, Видение | 200 | 600 |
| Grok 4.6 | 500K / 553K | Инструменты, Видение | 200 | 600 |
| Grok 4.7 | 500K / 553K | Инструменты, Видение | 200 | 600 |
| Kimi K2.7 Code | 256K | Инструменты, Видение | 95 | 400 |
| Kimi K3 | 1M | Инструменты, Видение | 300 | 1500 |
| MAI Code 1.1-Flash | 256K | Инструменты, Видение | 20 | 120 |
+176
View File
@@ -0,0 +1,176 @@
# adidas Terrex Anylander Unisex Trail Shoes
## Навигация и доступность
- Нажмите Alt+1 для режима чтения с экрана.
- Нажмите Alt+0 для отмены.
- Используйте сайт в режиме чтения с экрана.
- Руководство по доступности для чтения с экрана, отзывов и сообщений о проблемах.
- Перейти к содержимому.
## Магазин
Cosmos Sport
- 22 594000
- Заработайте x10 CASHBACK при первой покупке GoX.
- EASY RETURN
- FIND YOUR ORDER
- «GOX» и найдите предложение.
- Новинки
- Мужчины
- Женщины
- Дети
- Аксессуары
- Спорт
- Бренды
- Распродажи
- Подарочная карта
## Предложения
- Новинки. Только что запущено.
- Не пропустите: заработайте до 20 € в GoX Wallet.
- Не пропустите: On Shoes.
- Откройте коллекцию.
- Чёрные кроссовки.
- Исследуйте коллекцию.
## Товар
**Главная → Спорт → Лыжи → adidas Terrex Anylander Unisex Trail Shoes**
- Посмотреть похожие товары
- Бренд: adidas Terrex
- Название: adidas Terrex Anylander Unisex Trail Shoes
- Цена: €75.00
- Артикул: `9000198351_63596`
- Размер: выберите размер
- Таблица размеров
- Добавить в корзину
- Оформить заказ
- Наличие в магазинах
- Самовывоз из магазина за 2 часа
- Выберите цвет
Участник GoX получает 0,75 € кэшбэка. Зарегистрироваться.
- Бесплатный возврат в течение 30 дней.
- BOX NOW Lockers: быстрая доставка 24/7.
- Ваш заказ уже в пути!
- История цен.
## Характеристики
- Вес: 390 г.
- Перепад: 10 мм.
## Описание
### Уход за обувью
### Доставка и возврат
Бесплатная доставка и возврат.
### Описание товара
adidas Terrex Anylander Unisex Trail Shoes предназначены для коротких прогулок по лесу и длительных однодневных походов. Эти туристические кроссовки adidas Terrex обеспечивают поддержку и комфорт на разных типах троп. Низкая посадка и мягкая амортизация в межподошве обеспечивают лёгкость и комфорт. Рельефная подошва Traxion даёт сцепление во всех направлениях и помогает уверенно держаться на поверхности.
Используя переработанные материалы, adidas повторно применяет уже созданные материалы и сокращает отходы. Возобновляемые материалы помогают снизить зависимость от ограниченных ресурсов. В моделях, изготовленных из смеси переработанных и возобновляемых материалов, их общая доля составляет не менее 20%.
### Особенности
- Обычная посадка.
- Шнуровка.
- Текстильный верх с усиленным носком.
- Текстильная подкладка.
- Межподошва EVA.
- Подошва Traxion.
- Вес: 390 г, размер UK 8,5.
- Перепад межподошвы: 10 мм; пятка 27 мм, носок 17 мм.
- Содержит не менее 20% переработанных и возобновляемых материалов.
- Цвет: чёрный.
- SKU: `ID0895`
- Артикул: `9000198351_63596`
## О бренде adidas Terrex
adidas Terrex — линейка экипировки adidas для пешего туризма, альпинизма, трейлраннинга и скалолазания. Она является значимым продолжением спортивных традиций adidas и предлагает инновационные решения для активного отдыха.
Линейка была запущена в конце 2000-х годов, когда adidas заметила растущую потребность в прочной и высокопроизводительной экипировке. Ассортимент Terrex значительно расширился и включает товары, отличающиеся долговечностью, технологичностью и функциональностью.
Terrex уделяет внимание устойчивому развитию и стремится уменьшить воздействие на окружающую среду. Бренд использует переработанные материалы и внедряет экологичные производственные практики.
adidas Terrex продолжает развиваться и внедрять инновации, помогая спортсменам расширять свои возможности и безопасно наслаждаться природой.
## Связанные категории
- Купить больше: мужская одежда, обувь и аксессуары.
- Купить больше: женская коллекция.
- Купить больше: мужская одежда, обувь и аксессуары → мужская обувь.
- Купить больше: женская коллекция → женская обувь.
- Купить больше: модели для трейлраннинга и походов.
- Купить больше: мужская одежда, обувь и аксессуары adidas.
- Купить больше: мужская одежда, обувь и аксессуары → мужская обувь → мужские трейловые кроссовки.
## Сервисы Cosmos Sport
- Электронная подарочная карта: подарки на выбор среди более чем 20 000 моделей; номинал от 20 €.
- Бесплатный Click & Collect в 40 пунктах.
- BOX NOW: бесплатная доставка в постаматы.
- GoX Cashback: 1% кэшбэка в GoX Wallet для следующих покупок.
- Магазин для спортсменов: более 60 брендов.
- Бесплатный Click & Collect в более чем 40 пунктах.
- Бесплатный возврат в течение 30 дней.
- Бесплатная доставка при заказе от 49 €.
## Новости и контакты
CosmosNews: получайте первые предложения со скидками до 30% и эксклюзивные предложения.
Интересует:
- Мужчина.
- Женщина.
- Ребёнок.
Популярные ссылки:
- Найти заказ.
- Бесплатная доставка при заказе от 49 €.
- Бесплатный возврат в течение 30 дней.
- Контакты.
- 22 594000.
- Часы работы колл-центра: понедельник–пятница 09:00–21:00, суббота 09:00–20:00.
## Компания
- Магазины и пункты выдачи.
- Наша история.
- GoX.
- Условия и положения.
- Вакансии.
- Условия использования.
- Корпоративная социальная ответственность.
- Cosmos People.
- Льготы.
- TEAM WEAR.
## Обслуживание
- Служба поддержки.
- Контакты.
- Бесплатный возврат любым способом.
- Доставка и получение товаров.
- Способы оплаты.
- Бесплатный Click & Collect.
- Часто задаваемые вопросы.
- Подарочные карты и электронные подарочные карты.
- Политика конфиденциальности.
- Файлы cookie.
- Декларация о файлах cookie.
Присоединяйтесь: Facebook, Instagram.
© 2026 CosmosSport. Разработано Sleed.
@@ -164,6 +164,13 @@ if [[ -n "${MKDOCS_DOCS_DIR:-}" ]]; then
mkdir -p "${MKDOCS_DOCS_DIR}/curated" mkdir -p "${MKDOCS_DOCS_DIR}/curated"
cp -r "${ROOT_DIR}/docs/curated/"* "${MKDOCS_DOCS_DIR}/curated/" 2>/dev/null || true cp -r "${ROOT_DIR}/docs/curated/"* "${MKDOCS_DOCS_DIR}/curated/" 2>/dev/null || true
fi fi
# Картинки схемы зависимостей (docs/diagrams/*.svg|png) — чтобы их можно было
# показывать на опубликованных страницах. Исходники (.mmd, .py) не копируются.
if [[ -d "${ROOT_DIR}/docs/diagrams" ]]; then
mkdir -p "${MKDOCS_DOCS_DIR}/diagrams"
cp "${ROOT_DIR}/docs/diagrams/"*.svg "${MKDOCS_DOCS_DIR}/diagrams/" 2>/dev/null || true
cp "${ROOT_DIR}/docs/diagrams/"*.png "${MKDOCS_DOCS_DIR}/diagrams/" 2>/dev/null || true
fi
fi fi
# Per-стенд подстановка во все скопированные Markdown-файлы. # Per-стенд подстановка во все скопированные Markdown-файлы.
+21 -6
View File
@@ -24,6 +24,8 @@
| Виртуальный датацентр (21) | `nubes_vc_vdc` | создаёт vDC с ресурсами (CPU/RAM/Storage) | | Виртуальный датацентр (21) | `nubes_vc_vdc` | создаёт vDC с ресурсами (CPU/RAM/Storage) |
| Сетевой шлюз периметра (22) | `nubes_vc_nsxt` | создаёт Edge (routed-сеть, ALB/AVI) | | Сетевой шлюз периметра (22) | `nubes_vc_nsxt` | создаёт Edge (routed-сеть, ALB/AVI) |
| Сетевой шлюз периметра (22) | `nubes_vc_nsxt_snat` | **модификатор**: включает/переключает SNAT (`ipSpaceName`) на существующем Edge | | Сетевой шлюз периметра (22) | `nubes_vc_nsxt_snat` | **модификатор**: включает/переключает SNAT (`ipSpaceName`) на существующем Edge |
| Виртуальный каталог ВМ (26) | `nubes_vapp` | создаёт vApp (каталог для ВМ) на существующих vDC и Edge |
| Виртуальная машина (28) | `nubes_vc_vm_v3` | создаёт ВМ внутри vApp (CPU/RAM/диск, образ, SSH-ключ, внешний IP) |
| Kubernetes кластер Штурвал (150) | `nubes_k8s_sthutrval_cluster` | создаёт кластер (control plane + группы воркеров) | | Kubernetes кластер Штурвал (150) | `nubes_k8s_sthutrval_cluster` | создаёт кластер (control plane + группы воркеров) |
Общее правило. Ресурс, у которого есть свой «объект в облаке», **создаёт** этот объект. А то, что платформа Общее правило. Ресурс, у которого есть свой «объект в облаке», **создаёт** этот объект. А то, что платформа
@@ -74,7 +76,7 @@
| Флаг | Где применим | Поведение при `destroy` | Предупреждение в выводе | | Флаг | Где применим | Поведение при `destroy` | Предупреждение в выводе |
|---|---|---|---| |---|---|---|---|
| `suspend_on_destroy = true` | кластер Штурвала, vDC (по умолчанию `true`) | объект **приостанавливается**, не удаляется | «Ресурс заморожен, а не удалён» | | `suspend_on_destroy = true` | кластер Штурвала, vDC, vApp, ВМ (по умолчанию `true`) | объект **приостанавливается**, не удаляется | «Ресурс заморожен, а не удалён» |
| `keep_on_destroy = true` | Edge, SNAT, квота IP (по умолчанию `false`) | объект **не трогается в облаке**, только убирается из state | «Оставлен как есть» (для SNAT — «SNAT не выключался», для квоты IP — «Аллокация IP не снималась») | | `keep_on_destroy = true` | Edge, SNAT, квота IP (по умолчанию `false`) | объект **не трогается в облаке**, только убирается из state | «Оставлен как есть» (для SNAT — «SNAT не выключался», для квоты IP — «Аллокация IP не снималась») |
| оба `false` | любой | обычное удаление | — | | оба `false` | любой | обычное удаление | — |
@@ -96,7 +98,8 @@ Edge **физически не умеет `suspend`**: в списке дост
то есть SNAT для виртуальных машин остаётся в прежнем состоянии. то есть SNAT для виртуальных машин остаётся в прежнем состоянии.
- Квота внешних IP: уменьшать `count` **ниже фактически занятых адресов платформа не разрешает** - Квота внешних IP: уменьшать `count` **ниже фактически занятых адресов платформа не разрешает**
(ошибка вида «Кол-во занятых Ip … Невозможно выставить параметр count ниже этого параметра»). (ошибка вида «Кол-во занятых Ip … Невозможно выставить параметр count ниже этого параметра»).
Адреса держит кластер Штурвала (API + ingress), и `suspend` кластера их **не освобождает**. Адреса держит кластер Штурвала (API + ingress), и `suspend` кластера их **не освобождает**;
если у ВМ есть внешний адрес, он тоже держится инстансом ВМ — даже остановленной.
Поэтому при «заморозке» квота не изменяется вовсе, а реальное освобождение адресов возможно только после Поэтому при «заморозке» квота не изменяется вовсе, а реальное освобождение адресов возможно только после
удаления кластера — отдельным шагом. удаления кластера — отдельным шагом.
@@ -109,6 +112,8 @@ Edge **физически не умеет `suspend`**: в списке дост
|---|---|---| |---|---|---|
| Кластер Штурвала | `suspend` | adopt по имени + `resume` | | Кластер Штурвала | `suspend` | adopt по имени + `resume` |
| vDC | `suspend` | adopt по имени + `resume` | | vDC | `suspend` | adopt по имени + `resume` |
| vApp | `suspend` | adopt по имени + `resume` |
| ВМ | `suspend` | adopt по имени + `resume` |
| Edge | не трогается (`running`) | adopt (инстанс уже работает) | | Edge | не трогается (`running`) | adopt (инстанс уже работает) |
| SNAT | не трогается (включён) | повторный `modify` теми же значениями (фактически no-op) | | SNAT | не трогается (включён) | повторный `modify` теми же значениями (фактически no-op) |
| Квота IP | не трогается | `modify` с тем же `count` (no-op) | | Квота IP | не трогается | `modify` с тем же `count` (no-op) |
@@ -117,7 +122,7 @@ Edge **физически не умеет `suspend`**: в списке дост
Полностью удалить такой стенд можно только явным отказом от заморозки (`keep_on_destroy = false` / Полностью удалить такой стенд можно только явным отказом от заморозки (`keep_on_destroy = false` /
`suspend_on_destroy = false`) и в правильном порядке (см. §6). `suspend_on_destroy = false`) и в правильном порядке (см. §6).
## 6. Конкретный пайплайн стенда: vDC → Edge → внешние IP → SNAT → Штурвал ## 6. Конкретный пайплайн стенда: vDC → Edge → внешние IP → SNAT → vApp → ВМ → Штурвал
Порядок создания и зависимости: Порядок создания и зависимости:
@@ -125,14 +130,24 @@ Edge **физически не умеет `suspend`**: в списке дост
2. `nubes_vc_vdc` — виртуальный датацентр. 2. `nubes_vc_vdc` — виртуальный датацентр.
3. `nubes_vc_nsxt` — Edge. Обязательно включать балансировщик (параметр `need_enable_avi = true`) 3. `nubes_vc_nsxt` — Edge. Обязательно включать балансировщик (параметр `need_enable_avi = true`)
и задать не меньше 3 виртуальных сервисов (`virtual_services_count >= 3`) — это нужно кластеру Штурвала. и задать не меньше 3 виртуальных сервисов (`virtual_services_count >= 3`) — это нужно кластеру Штурвала.
4. `nubes_vc_org_ip_allocation` — внешние адреса в организации (минимум 3 по инструкции услуги Штурвала). 4. `nubes_vc_org_ip_allocation` — внешние адреса в организации (минимум 3 по инструкции услуги Штурвала,
плюс ещё один, если у ВМ будет внешний адрес).
5. `nubes_vc_nsxt_snat` — SNAT на Edge (по `depends_on` после аллокации адресов). 5. `nubes_vc_nsxt_snat` — SNAT на Edge (по `depends_on` после аллокации адресов).
6. `nubes_k8s_sthutrval_cluster` — кластер Штурвала (по `depends_on` после SNAT: нодам нужен выход в интернет). 6. `nubes_vapp` — vApp: привязка к vDC (`vdc_uid`) и Edge (`nsxt_uid`); создаётся после SNAT,
чтобы сеть была уже готова.
7. `nubes_vc_vm_v3` — ВМ внутри vApp (`vapp_uid`): образ, CPU/RAM/диск, учётка и SSH-ключ, порты FW.
8. `nubes_k8s_sthutrval_cluster` — кластер Штурвала (по `depends_on` после SNAT: нодам нужен выход в интернет).
При удалении Terraform идёт в обратном порядке. Ограничения платформы, которые встречаются на этом пути: При удалении Terraform идёт в обратном порядке. Ограничения платформы, которые встречаются на этом пути:
- **1 кластер Штурвала = 1 vDC** (действующее ограничение услуги). - **1 кластер Штурвала = 1 vDC** (действующее ограничение услуги).
- vDC удаляется только **через 14 дней после `suspend`**; при живых Edge/vApp/VM/кластере — через поддержку. - vDC удаляется только **через 14 дней после `suspend`**; при живых Edge/vApp/VM/кластере — через поддержку.
- У vApp удаление требует **предварительного `suspend`**, полное удаление — тоже через 14 дней.
- У vApp и ВМ есть `suspend` и `resume`; у Edge операции `suspend` нет вообще.
- **ВМ не включится, если в vDC не осталось vCPU**: Штурвал при настройках по умолчанию занимает всё
(2 ноды × 4 vCPU при `vdc_cpu_allocated = 8`). Платформа отвечает общей ошибкой
`[400:VALIDATION] Unable to perform this action` на этапе power-on, инстанс остаётся в `not created`.
- Создание ВМ идёт дольше остальных ресурсов (customization + power-on) — задавайте `operation_timeout = "15m"`.
- Edge не удаляется при живых зависимых объектах (по инструкции услуги). - Edge не удаляется при живых зависимых объектах (по инструкции услуги).
- Квота IP не опускается ниже занятых адресов (см. §5). - Квота IP не опускается ниже занятых адресов (см. §5).
@@ -198,7 +213,7 @@ UUID в облаке не имеет «правильного» регистра
## 10. Куда смотреть дальше ## 10. Куда смотреть дальше
- [Как развернуть vDC, Edge, внешние IP, SNAT и кластер Штурвал](https://tf-docs.nodejsk8s.dev.nubes.ru/nubes-dev/curated/pipeline/vdc_edge_ip_snat/) — - [Как развернуть цепочку: vDC → Edge → внешние IP → SNAT → vApp → ВМ → Штурвал](https://tf-docs.nodejsk8s.dev.nubes.ru/nubes-dev/curated/pipeline/vdc_edge_ip_snat/) —
пошаговая инструкция по всей цепочке вашего стенда. пошаговая инструкция по всей цепочке вашего стенда.
- `curated/modifiers/org_ip_and_snat.md` — ресурсы-модификаторы. - `curated/modifiers/org_ip_and_snat.md` — ресурсы-модификаторы.
- `30_registry/guides/terraform-structure.md` — структура манифестов. - `30_registry/guides/terraform-structure.md` — структура манифестов.
+7 -4
View File
@@ -10,8 +10,8 @@
Оба ресурса проверены живым прогоном: создание, проверка состояния в ЛК и удаление. Оба ресурса проверены живым прогоном: создание, проверка состояния в ЛК и удаление.
Полный рабочий пример (vDC + Edge + IP + SNAT) — на странице Полный рабочий пример (vDC + Edge + IP + SNAT + vApp + ВМ + Штурвал) — на странице
[«Как развернуть vDC, Edge, внешние IP и SNAT»](../pipeline/vdc_edge_ip_snat.md), [«Как развернуть цепочку: vDC → Edge → внешние IP → SNAT → vApp → ВМ → Штурвал»](../pipeline/vdc_edge_ip_snat.md),
файлы — в репозитории `tf_examples`, папка `fullpipe_chain`. файлы — в репозитории `tf_examples`, папка `fullpipe_chain`.
## `nubes_vc_org_ip_allocation` — внешние IP на организации ## `nubes_vc_org_ip_allocation` — внешние IP на организации
@@ -19,7 +19,7 @@
| Атрибут | Тип | Описание | | Атрибут | Тип | Описание |
|---|---|---| |---|---|---|
| `organization` | string, обязательный | Организация: название услуги из ЛК или её UUID | | `organization` | string, обязательный | Организация: название услуги из ЛК или её UUID |
| `vip_configure` | string (JSON), обязательный | Сколько IP выделить: `[{"name":"internet-ipv4-v1","count":"3"}]`. `count` — строкой | | `vip_configure` | string (JSON), обязательный | Сколько IP выделить: `[{"name":"internet-ipv4-v1","count":"3"}]`. `count` — строкой. Для Штурвала нужно не меньше 3 адресов, ещё один — если у ВМ будет внешний адрес |
| `keep_on_destroy` | bool, по умолчанию `false` | `false` — при удалении квота обнуляется; `true` — остаётся как есть | | `keep_on_destroy` | bool, по умолчанию `false` | `false` — при удалении квота обнуляется; `true` — остаётся как есть |
```hcl ```hcl
@@ -39,7 +39,10 @@ resource "nubes_vc_org_ip_allocation" "this" {
- массив `vip_configure` записывается **целиком**, поэтому ресурс владеет всей аллокацией организации: - массив `vip_configure` записывается **целиком**, поэтому ресурс владеет всей аллокацией организации:
если нужно поменять количество — меняйте значение и делайте `apply`; если нужно поменять количество — меняйте значение и делайте `apply`;
- снять аллокацию через пустой массив нельзя — для этого удалите ресурс - снять аллокацию через пустой массив нельзя — для этого удалите ресурс
(`terraform destroy -target=nubes_vc_org_ip_allocation.this`), тогда отправится запрос с `count = "0"`. (`terraform destroy -target=nubes_vc_org_ip_allocation.this`), тогда отправится запрос с `count = "0"`;
- **считайте потребителей адресов**: кластер Штурвал держит 3 адреса (API и Ingress), а ВМ с внешним
адресом (`ip_space_name = "internet-ipv4-v1"`) занимает ещё один — итого `count = "4"`.
Адреса освобождаются только после удаления потребителя: `suspend` их не снимает.
## `nubes_vc_nsxt_snat` — SNAT на шлюзе ## `nubes_vc_nsxt_snat` — SNAT на шлюзе
+95 -14
View File
@@ -1,4 +1,4 @@
# Как развернуть vDC, Edge, внешние IP, SNAT и кластер Штурвал # Как развернуть цепочку: vDC → Edge → внешние IP → SNAT → vApp → ВМ → Штурвал
Пошаговая инструкция. Готовые файлы примера — в репозитории `tf_examples`, папка `fullpipe_chain`. Пошаговая инструкция. Готовые файлы примера — в репозитории `tf_examples`, папка `fullpipe_chain`.
@@ -8,11 +8,15 @@
- сетевой шлюз периметра (Edge) с балансировщиком AVI; - сетевой шлюз периметра (Edge) с балансировщиком AVI;
- внешние IP на организации; - внешние IP на организации;
- SNAT на шлюзе; - SNAT на шлюзе;
- виртуальный каталог (vApp) и виртуальная машина (ВМ) внутри него — с доступом по SSH;
- Kubernetes-кластер Штурвал (сервис 150) на этой сети. - Kubernetes-кластер Штурвал (сервис 150) на этой сети.
![Схема зависимостей облачных сервисов](../../diagrams/infra_services_diagram.svg)
**Организацию создайте заранее в ЛК** — Terraform её не создаёт и не удаляет. **Организацию создайте заранее в ЛК** — Terraform её не создаёт и не удаляет.
Кластер Штурвал создаётся десятки минут, поэтому провайдер ждёт его до часу Кластер Штурвал создаётся десятки минут, поэтому провайдер ждёт его до часу
(`operation_timeout = "60m"`); остальные ресурсы — минуты. (`operation_timeout = "60m"`); ВМ тоже создаётся небыстро (у неё отдельный таймаут `15m`),
остальные ресурсы — минуты.
## Что нужно перед началом ## Что нужно перед началом
@@ -22,12 +26,15 @@
| Токен API | доступ к ЛК | ЛК → Профиль → Токены → «Технический» | | Токен API | доступ к ЛК | ЛК → Профиль → Токены → «Технический» |
| Организация в Cloud Director | всё создаётся внутри неё | услуга «Организация в Cloud Director» | | Организация в Cloud Director | всё создаётся внутри неё | услуга «Организация в Cloud Director» |
| Edge с балансировщиком AVI | без ALB кластер Штурвал не поднимется | `nsxt_need_enable_avi = true`, `nsxt_virtual_services_count >= 3` | | Edge с балансировщиком AVI | без ALB кластер Штурвал не поднимется | `nsxt_need_enable_avi = true`, `nsxt_virtual_services_count >= 3` |
| Не меньше 3 внешних IP | адрес Kubernetes API, адрес Ingress и запас | `ip_count = "3"` | | Внешние IP: не меньше 3 + 1 под ВМ | адрес Kubernetes API, адрес Ingress, адрес ВМ и запас | `ip_count = "4"` |
| SNAT на Edge | выход в интернет для машин кластера | ресурс `nubes_vc_nsxt_snat` | | SNAT на Edge | выход в интернет для машин кластера и для ВМ | ресурс `nubes_vc_nsxt_snat` |
| Публичный SSH-ключ | вход на ВМ по SSH | `~/.ssh/id_ed25519.pub` → `vm_user_public_key` |
| Запас vCPU в vDC | Штурвал занимает 8 vCPU из 8 при настройках по умолчанию, и тогда ВМ не включится | `vdc_cpu_allocated` |
Порядок из чек-листа услуги 150 (именно так связаны ресурсы в примере): Порядок из чек-листа услуги 150 (именно так связаны ресурсы в примере):
организация → vDC → Edge (ALB, AVI ≥ 3) → внешние IP (≥ 3) → SNAT → кластер Штурвал. организация → vDC → Edge (ALB, AVI ≥ 3) → внешние IP (≥ 3) → SNAT → **vApp → ВМ** → кластер Штурвал.
Минимум для кластера: 1 мастер-нода и 1 воркер-нода по 4 vCPU / 8 ГБ RAM / 50 ГБ диска. Минимум для кластера: 1 мастер-нода и 1 воркер-нода по 4 vCPU / 8 ГБ RAM / 50 ГБ диска;
минимум для ВМ: 1 vCPU / 1 ГБ RAM (в примере — 2 vCPU / 2 ГБ).
## 1. Скачайте пример ## 1. Скачайте пример
@@ -67,13 +74,13 @@ organization = "organ"
# Внешние IP # Внешние IP
ip_space_name = "internet-ipv4-v1" ip_space_name = "internet-ipv4-v1"
ip_count = "3" ip_count = "4" # 3 под Штурвал + 1 под ВМ
# vDC # vDC
vdc_resource_name = "fullpipe-vdc" # имя услуги в ЛК, любое vdc_resource_name = "fullpipe-vdc" # имя услуги в ЛК, любое
vdc_network_provider = "snb1" # ЛК → создание vDC → «Сетевой провайдер» vdc_network_provider = "snb1" # ЛК → создание vDC → «Сетевой провайдер»
vdc_provider_vdc = "Intel Broadwell 2.4" # ЛК → создание vDC → «Provider VDC» vdc_provider_vdc = "Intel Broadwell 2.4" # ЛК → создание vDC → «Provider VDC»
vdc_cpu_allocated = 8 # vCPU, шт. vdc_cpu_allocated = 12 # 8 под Штурвал + 2 под ВМ + 2 запаса
vdc_cpu_guaranteed = 0 # резервирование vCPU, %: 0, 50 или 80 vdc_cpu_guaranteed = 0 # резервирование vCPU, %: 0, 50 или 80
vdc_mem_allocated = 32 # RAM, ГБ vdc_mem_allocated = 32 # RAM, ГБ
vdc_storage_config = "[{\"name\":\"SATA\",\"size\":\"200\"}]" # политика и размер, ГБ vdc_storage_config = "[{\"name\":\"SATA\",\"size\":\"200\"}]" # политика и размер, ГБ
@@ -86,6 +93,17 @@ nsxt_virtual_services_count = 3 # виртуальных серв
nsxt_ip_addr_pool = "10.10.102.0/24" # пул адресов routed-сети, маска /24 nsxt_ip_addr_pool = "10.10.102.0/24" # пул адресов routed-сети, маска /24
nsxt_main_dns = "81.22.46.22" # основной DNS nsxt_main_dns = "81.22.46.22" # основной DNS
nsxt_second_dns = "185.247.187.77" # второй DNS nsxt_second_dns = "185.247.187.77" # второй DNS
# vApp и ВМ
vapp_name = "fullpipe-vapp-01" # имя vApp: маска ^[a-z0-9][a-z0-9.-]{3,61}[a-z0-9]$, уникально в организации
vm_name = "web01" # имя ВМ: задаёт имя IP Set {vapp_name}-{vm_name}
vm_image = "Ubuntu_22-20G" # RockyLinux_9-16G-cloudinit | Ubuntu_22-20G | Debian_13-20G
vm_cpu = 2 # vCPU, 1..64
vm_ram = 2 # RAM, ГБ, 1..256
vm_disk = 20 # дополнительный диск, ГБ
vm_user_login = "ubuntu" # учётка SSH
vm_user_public_key = "ssh-ed25519 AAAA..." # содержимое ~/.ssh/id_ed25519.pub
vm_same_snat = false # false — за общим SNAT шлюза
``` ```
Описание всех параметров — в `variables.tf`. `terraform.tfvars` с токеном никому не передавайте Описание всех параметров — в `variables.tf`. `terraform.tfvars` с токеном никому не передавайте
@@ -95,7 +113,7 @@ nsxt_second_dns = "185.247.187.77" # второй DNS
```bash ```bash
terraform init # один раз — скачает провайдер terraform init # один раз — скачает провайдер
terraform plan # покажет, что будет создано: 5 ресурсов terraform plan # покажет, что будет создано: 7 ресурсов (vDC, Edge, IP, SNAT, vApp, ВМ, Штурвал)
terraform apply # создаст (подтвердить: yes) terraform apply # создаст (подтвердить: yes)
``` ```
@@ -105,14 +123,73 @@ terraform apply # создаст (подтвердить: yes)
terraform output # UUID и имена созданных услуг terraform output # UUID и имена созданных услуг
``` ```
И в ЛК: появились vDC, Edge и кластер Штурвал, на организации выделены внешние IP, И в ЛК: появились vDC, Edge, vApp с ВМ и кластер Штурвал, на организации выделены внешние IP,
на шлюзе включён SNAT. Повторный `terraform plan` должен показать `No changes`. на шлюзе включён SNAT. Повторный `terraform plan` должен показать `No changes`.
Адрес ВМ виден в `terraform output vm_state_flat` — поля `externalConnect` (внешний IP),
`internalConnect` (адрес в routed-сети) и `fqdn`: `ssh <vm_user_login>@<externalConnect>`.
Кластер создаётся десятки минут — провайдер ждёт его (`operation_timeout = "60m"`), Кластер создаётся десятки минут — провайдер ждёт его (`operation_timeout = "60m"`),
в ЛК он появится со статусом `running`. Адреса Kubernetes API и Ingress — в в ЛК он появится со статусом `running`. Адреса Kubernetes API и Ingress — в
`terraform output shturval_state_params` (поля `kubernetesApiAddress` и `ingressAddress`). `terraform output shturval_state_params` (поля `kubernetesApiAddress` и `ingressAddress`).
## 6. Удаление: «заморозка» вместо удаления ## 6. vApp и ВМ
Виртуальная машина живёт **внутри vApp**: vApp привязывается к vDC (`vdc_uid`) и к Edge (`nsxt_uid`),
а ВМ — к vApp (`vapp_uid`) и получает адрес в routed-сети шлюза. Своей сети у vApp нет.
| Ресурс | Услуга | Ключевые параметры |
|---|---|---|
| `nubes_vapp` | 26 | `vapp_name` (уникальное в организации, маска DNS-имени), `vdc_uid`, `nsxt_uid` |
| `nubes_vc_vm_v3` | 28 | `vapp_uid`, `vm_name`, `vm_cpu`, `vm_ram`, `vm_disk`, `image_vm`, `user_login`, `user_public_key`, `ip_space_name`, `access_port_list` |
### Параметры ВМ
| Параметр | Значения |
|---|---|
| `image_vm` | `RockyLinux_9-16G-cloudinit`, `Ubuntu_22-20G`, `Debian_13-20G` |
| `vm_cpu` / `vm_ram` | 1..64 шт. / 1..256 ГБ |
| `vm_disk` | дополнительный диск, ГБ (основной диск зависит от образа) |
| `user_login` | учётка SSH (по умолчанию `myuser`) |
| `access_port_list` | `[{ "port": "22", "type": "tcp" }]`; `type` — `tcp`, `udp` или `all` |
| `access_ip_list` | белый список адресов; по умолчанию `["0.0.0.0/0"]` |
`image_vm`, `vm_name`, `user_login`, `user_public_key` и `vapp_uid` — **create-only** (менять нельзя).
`vm_cpu`, `vm_ram`, `vm_disk`, `access_port_list`, `ip_space_name` меняются операцией `modify`.
### Внешний доступ
| `ip_space_name` | Что это значит |
|---|---|
| `internet-ipv4-v1` | у ВМ есть внешний адрес: в `vm_state_flat` появятся `externalConnect` и `fqdn` |
| `no-needed` | внешнего адреса нет, доступ только изнутри |
Адрес берётся из квоты организации, поэтому при внешнем доступе нужен **4-й адрес** (`ip_count = "4"`).
При `same_snat = false` (по умолчанию) ВМ публикуется за общим SNAT шлюза.
Пример результата на живом стенде:
```
vm_state_flat = {
"externalConnect" = "185.247.187.235"
"internalConnect" = "10.10.102.4"
"fqdn" = "web01.85ea682f-...dev.nubes.ru"
}
```
### Что проверять при создании ВМ
1. **Запас vCPU в vDC.** Штурвал при настройках по умолчанию занимает 8 vCPU из 8 (2 ноды × «TKG 4CPU 8RAM»).
Тогда ВМ не включится: платформа отдаёт `[400:VALIDATION] Unable to perform this action` на этапе
power-on, а инстанс остаётся в статусе `not created`. Лечится увеличением `vdc_cpu_allocated`.
2. **Квота IP.** `ip_count ≥ 4`, иначе внешний адрес для ВМ не выделится.
3. **Ключ.** Публичный ключ кладите в `terraform.tfvars`, не в репозиторий.
4. **Время.** ВМ создаётся и включается с customization (запись учётки и ключа) — на живом стенде
операция шла больше 15 минут. В примере стоит `operation_timeout = "15m"`; при медленной платформе
увеличьте его. Симптом таймаута — «операция … не завершилась за установленный таймаут».
5. **Проверка.** `ssh <vm_user_login>@<externalConnect>` и `terraform plan` → `No changes`.
## 7. Удаление: «заморозка» вместо удаления
```bash ```bash
terraform destroy terraform destroy
@@ -127,15 +204,18 @@ terraform destroy
| Edge | не трогается: у эджа нет операции `suspend` | `keep_on_destroy = true` | | Edge | не трогается: у эджа нет операции `suspend` | `keep_on_destroy = true` |
| SNAT | не выключается | `keep_on_destroy = true` | | SNAT | не выключается | `keep_on_destroy = true` |
| Квота внешних IP | не меняется: адреса держит кластер Штурвала | `keep_on_destroy = true` | | Квота внешних IP | не меняется: адреса держит кластер Штурвала | `keep_on_destroy = true` |
| vApp | `suspend`: выключаются все ВМ внутри каталога | `suspend_on_destroy = true` |
| ВМ | `suspend` | `suspend_on_destroy = true` |
Приоритет флагов: `keep_on_destroy` важнее `suspend_on_destroy`. Следующий `apply` Приоритет флагов: `keep_on_destroy` важнее `suspend_on_destroy`. Следующий `apply`
усыновит объекты по имени и разморозит кластер и vDC усыновит объекты по имени и разморозит кластер и vDC
(`adopt_existing_on_create = true` → `resume`). **Организация не удаляется никогда.** (`adopt_existing_on_create = true` → `resume`). **Организация не удаляется никогда.**
Полное удаление — осознанно: поставьте `keep_on_destroy = false` и Полное удаление — осознанно: поставьте `keep_on_destroy = false` и
`suspend_on_destroy = false` и удаляйте по порядку: кластер → квота IP (`count = 0`) → `suspend_on_destroy = false` и удаляйте по порядку: кластер → ВМ → vApp → квота IP (`count = 0`) →
SNAT → Edge → vDC. Квоту нельзя опустить ниже занятых адресов, поэтому — только SNAT → Edge → vDC. Квоту нельзя опустить ниже занятых адресов, поэтому — только
после удаления кластера; vDC удаляется лишь через 14 дней после `suspend`. после удаления кластера и ВМ; vApp удаляется лишь после `suspend` (полное удаление — через 14 дней),
а vDC — через 14 дней после `suspend`.
## Файлы примера ## Файлы примера
@@ -147,8 +227,9 @@ SNAT → Edge → vDC. Квоту нельзя опустить ниже зан
| `vdc.tf` | виртуальный датацентр | | `vdc.tf` | виртуальный датацентр |
| `edge.tf` | сетевой шлюз периметра (Edge) | | `edge.tf` | сетевой шлюз периметра (Edge) |
| `modifiers.tf` | внешние IP на организации + SNAT на шлюзе | | `modifiers.tf` | внешние IP на организации + SNAT на шлюзе |
| `vm.tf` | vApp и ВМ: переменные vApp/ВМ, оба ресурса и выводы (отключается удалением файла) |
| `shturval.tf` | Kubernetes-кластер Штурвал: переменные Штурвала, группы воркеров и сам ресурс | | `shturval.tf` | Kubernetes-кластер Штурвал: переменные Штурвала, группы воркеров и сам ресурс |
| `outputs.tf` | UUID и имена созданных услуг (vDC, Edge, Штурвал) | | `outputs.tf` | UUID и имена созданных услуг (vDC, Edge, vApp, ВМ, Штурвал) |
| `terraform.tfvars.example` | шаблон значений (копируется в `terraform.tfvars`) | | `terraform.tfvars.example` | шаблон значений (копируется в `terraform.tfvars`) |
Подробнее про два последних ресурса — на странице Подробнее про два последних ресурса — на странице
+13
View File
@@ -50,6 +50,19 @@ python3 docs/diagrams/render_infra_diagram.py
(`/usr/share/fonts/truetype/dejavu/DejaVuSans.ttf`, `...-Bold.ttf`). (`/usr/share/fonts/truetype/dejavu/DejaVuSans.ttf`, `...-Bold.ttf`).
Сторонние библиотеки для SVG не нужны — SVG формируется текстом. Сторонние библиотеки для SVG не нужны — SVG формируется текстом.
### Публикация картинок на сайте
Скрипт `TOOLS/scripts/04_build_and_publish_docs.sh` копирует **только картинки**
(`docs/diagrams/*.svg`, `*.png`) в публикуемый `docs_dir` → `diagrams/`: исходники (`.mmd`, `.py`)
на сайт не попадают. Поэтому картинку можно вставлять в опубликованные страницы относительной
ссылкой, например со страницы `docs/curated/pipeline/vdc_edge_ip_snat.md`:
```markdown
![Схема зависимостей облачных сервисов](../../diagrams/infra_services_diagram.svg)
```
Изменения в `docs/diagrams/` попадают на сайт только при следующей публикации документации.
Для Mermaid-версии нужен рендерер (`mermaid.ink`, `mmdc` или предпросмотр Для Mermaid-версии нужен рендерер (`mermaid.ink`, `mmdc` или предпросмотр
Markdown в VS Code); в этом репозитории использовался `mermaid.ink` через Markdown в VS Code); в этом репозитории использовался `mermaid.ink` через
`python3` + `urllib`. `python3` + `urllib`.
+1 -1
View File
@@ -61,5 +61,5 @@ nav:
- Глоссарий: 30_registry/guides/glossary.md - Глоссарий: 30_registry/guides/glossary.md
- Проверенные примеры: - Проверенные примеры:
- PostgreSQL: curated/postgres/pg_user_db.md - PostgreSQL: curated/postgres/pg_user_db.md
- Пайплайн vDC → Edge → IP → SNAT → Штурвал: curated/pipeline/vdc_edge_ip_snat.md - Пайплайн vDC → Edge → IP → SNAT → vApp → ВМ → Штурвал: curated/pipeline/vdc_edge_ip_snat.md
- Ресурсы-модификаторы (IP организации, SNAT): curated/modifiers/org_ip_and_snat.md - Ресурсы-модификаторы (IP организации, SNAT): curated/modifiers/org_ip_and_snat.md