doc: добавление документации по alb

This commit is contained in:
grodionov 2026-02-06 14:35:56 +03:00
parent fa78ccf6ae
commit 11d05b03ed
15 changed files with 522 additions and 13 deletions

View File

@ -1,11 +1,30 @@
# vm.dev.nubes.ru # tfTemplate — инфраструктура в VMware Cloud Director (OpenTofu)
Развёртывание ВМ в VMware Cloud Director (OpenTofu). Развёртывание ВМ в VMware Cloud Director (OpenTofu).
--- ## Внутренняя структура репозитория
**Нужно поднять простую ВМ?** — см. [simpleVm/README.md](simpleVm/README.md). ```
tfTemplate/
├── simpleVm/ # Модуль для развёртывания простой виртуальной машины
│ └── README.md # Документация по использованию simpleVm
├── alb/ # Модуль для создания Application Load Balancer (ALB)
│ ├── README.md # Документация по ALB
│ ├── terraform.tfvars # Пример параметров для ALB и подключения к облаку
│ └── albpool.tf # Логика работы ALB, примеры ресурса и firewall
├── README.md # Основная документация (этот файл)
└── ... # Прочие модули, файлы и переменные
```
- **simpleVm/** — развёртывание одиночных виртуальных машин.
- **alb/** — конфигурация балансировщиков нагрузки (ALB), сетевых профилей и firewall.
- **terraform.tfvars** — образец пользовательских переменных, включая настройку облака и ALB.
- **albpool.tf** — логика и правила балансировки и доступов.
- **README.md** — описание проекта, структура и ссылки на детали модулей.
_Для детального описания каждого модуля смотрите соответствующие README внутри директорий._
- **Простая ВМ** — [simpleVm/README.md](simpleVm/README.md)
- **ALB (Load Balancer)** — [alb/README.md](alb/README.md)
--- ---
*Здесь со временем можно добавить ссылки на другие сценарии (например, несколько vDC, сложные конфигурации).*

7
alb/.gitignore vendored Normal file
View File

@ -0,0 +1,7 @@
.terraform/
*.tfstate
*.tfstate.*
.terraform.lock.hcl
crash.*.log
# Секреты — лучше передавать через TF_VAR_*
# terraform.tfvars

31
alb/README.md Normal file
View File

@ -0,0 +1,31 @@
# alb — балансировщик и опционально ВМ (OpenTofu)
Создаётся NSX-T ALB в VMware Cloud Director: внешний IP (VIP), пулы по портам из `alb_ports`, Virtual Service. На Edge добавляется правило фаервола: входящий трафик на VIP разрешён (доступ к ВМ через балансировщик, например SSH на порт 22).
**Режимы backend'ов:**
- Если в `vcds` для vDC из `albConfiguration.vdc_key` заданы **`servers`** — создаются vApp и ВМ, затем пулы ALB получают их внутренние IP (сначала ВМ, потом балансировщик).
- Иначе backend'ы задаются списком **`albConfiguration.alb_backend_ips`** (внешние сервера; ВМ в конфиге не создаются).
## Что нужно в vCD
- Edge Gateway (имя → `albConfiguration.edge`)
- На Edge привязана Service Engine Group (`srv_engine_group`)
- IP Space (`vcd_ip_space`)
- Для создания ВМ: org-сеть `network_routed` в соответствующем vDC.
## Запуск
```bash
# Заполнить terraform.tfvars (или TF_VAR_*)
tofu init
tofu plan
tofu apply
```
После apply: будет выведен IP-адрес **`alb_vip`** — укажите его в DNS и используйте для внешнего доступа (Ingress). Подключение к сервисам ВМ осуществляется через балансировщик на соответствующих портах, например:
- HTTP: `http://<alb_vip>:80`
- HTTPS: `https://<alb_vip>:443`
- SSH (если настроено): `ssh deploy@<alb_vip>` (при наличии порта 22 в `alb_ports`).
На Edge автоматически создаётся правило фаервола, разрешающее входящий трафик на этот VIP по заданным портам ALB.

120
alb/albpool.tf Normal file
View File

@ -0,0 +1,120 @@
# ALB: на каждый порт из albConfiguration.alb_ports пул и Virtual Service. Члены пулов из ВМ или alb_backend_ips.
data "vcd_vdc_group" "vdcGroup" {
count = local.is_vdc_group && var.albConfiguration.enabled ? 1 : 0
provider = vcd.al[var.albConfiguration.vdc_key]
name = var.albConfiguration.vdcGroupName
}
data "vcd_nsxt_edgegateway" "nsxt-edge" {
count = var.albConfiguration.enabled ? 1 : 0
provider = vcd.al[var.albConfiguration.vdc_key]
org = var.vcds[var.albConfiguration.vdc_key].org
owner_id = local.is_vdc_group ? data.vcd_vdc_group.vdcGroup[0].id : null
vdc = local.is_vdc_group ? null : var.vcds[var.albConfiguration.vdc_key].vdc
name = var.albConfiguration.edge
}
data "vcd_nsxt_alb_edgegateway_service_engine_group" "engine" {
count = var.albConfiguration.enabled ? 1 : 0
provider = vcd.al[var.albConfiguration.vdc_key]
org = var.vcds[var.albConfiguration.vdc_key].org
edge_gateway_id = data.vcd_nsxt_edgegateway.nsxt-edge[0].id
service_engine_group_name = var.albConfiguration.srv_engine_group
}
data "vcd_ip_space" "ip_space" {
count = var.albConfiguration.enabled ? 1 : 0
provider = vcd.al[var.albConfiguration.vdc_key]
name = var.albConfiguration.vcd_ip_space
}
data "vcd_org" "org_name" {
count = var.albConfiguration.enabled ? 1 : 0
provider = vcd.al[var.albConfiguration.vdc_key]
name = var.vcds[var.albConfiguration.vdc_key].org
}
resource "vcd_ip_space_ip_allocation" "ip_ingress" {
count = var.albConfiguration.enabled ? 1 : 0
provider = vcd.al[var.albConfiguration.vdc_key]
org_id = data.vcd_org.org_name[0].id
ip_space_id = data.vcd_ip_space.ip_space[0].id
type = "FLOATING_IP"
}
resource "vcd_nsxt_alb_pool" "pool" {
for_each = var.albConfiguration.enabled ? toset([for p in var.albConfiguration.alb_ports : tostring(p)]) : toset([])
provider = vcd.al[var.albConfiguration.vdc_key]
org = var.vcds[var.albConfiguration.vdc_key].org
edge_gateway_id = data.vcd_nsxt_edgegateway.nsxt-edge[0].id
name = "${var.albConfiguration.alb_name}-${each.key}"
description = "Pool port ${each.key}"
algorithm = "ROUND_ROBIN"
default_port = tonumber(each.key)
dynamic "member" {
for_each = local.backend_ips_for_alb
content {
ip_address = member.value
}
}
depends_on = [vcd_vapp_vm.vm]
}
resource "vcd_nsxt_alb_virtual_service" "vs" {
for_each = var.albConfiguration.enabled ? toset([for p in var.albConfiguration.alb_ports : tostring(p)]) : toset([])
provider = vcd.al[var.albConfiguration.vdc_key]
org = var.vcds[var.albConfiguration.vdc_key].org
name = "${var.albConfiguration.alb_name}-${each.key}"
edge_gateway_id = data.vcd_nsxt_edgegateway.nsxt-edge[0].id
pool_id = vcd_nsxt_alb_pool.pool[each.key].id
service_engine_group_id = data.vcd_nsxt_alb_edgegateway_service_engine_group.engine[0].service_engine_group_id
virtual_ip_address = vcd_ip_space_ip_allocation.ip_ingress[0].ip
application_profile_type = "L4"
service_port {
start_port = tonumber(each.key)
type = "TCP_PROXY"
}
}
# ------------------------------------------------------------------------------
# Firewall: разрешаем входящий трафик на VIP балансировщика (доступ к ВМ через ALB).
# Внимание: vcd_nsxt_firewall управляет всеми пользовательскими правилами на Edge
# при наличии других правил добавьте их в блок rule[] ниже.
# ------------------------------------------------------------------------------
resource "vcd_nsxt_ip_set" "alb_vip" {
count = var.albConfiguration.enabled ? 1 : 0
provider = vcd.al[var.albConfiguration.vdc_key]
name = "${var.albConfiguration.alb_name}-vip"
description = "ALB VIP for firewall rule"
edge_gateway_id = data.vcd_nsxt_edgegateway.nsxt-edge[0].id
ip_addresses = [vcd_ip_space_ip_allocation.ip_ingress[0].ip]
}
resource "vcd_nsxt_firewall" "alb_access" {
count = var.albConfiguration.enabled ? 1 : 0
provider = vcd.al[var.albConfiguration.vdc_key]
org = var.vcds[var.albConfiguration.vdc_key].org
edge_gateway_id = data.vcd_nsxt_edgegateway.nsxt-edge[0].id
rule {
name = "${var.albConfiguration.alb_name}-allow-inbound"
direction = "IN"
ip_protocol = "IPV4"
action = "ALLOW"
enabled = true
logging = false
destination_ids = [vcd_nsxt_ip_set.alb_vip[0].id]
# source_ids пусто = любой источник (подключение с любого IP к VIP)
# app_port_profile_ids пусто = любые порты (ограничение по портам задаётся ALB)
}
}

45
alb/local.tf Normal file
View File

@ -0,0 +1,45 @@
locals {
is_vdc_group = lookup(var.albConfiguration, "edgeScope", "vdc") == "vdcGroup"
# Список ВМ для создания: ключ = "${server_name}.${server_group}", только из vDC с заданными servers
servers = merge([
for vcd_key, vcd_config in var.vcds :
try(length(vcd_config.servers), 0) > 0 ? merge([
for server_group, servers in vcd_config.servers :
{
for server_name, server_config in servers :
"${server_name}.${server_group}" => {
vcd_key = vcd_key
server_group = server_group
server_name = server_name
config = server_config
network_routed = vcd_config.network_routed
}
}
]...) : {}
]...)
# Доп. диски ВМ (dataDisk)
disks = merge([
for server_name, server_config in local.servers :
{
for disk_uid, disk_size in try(server_config.config.dataDisk, {}) :
"${server_name}-${disk_uid}" => {
vcd_key = server_config.vcd_key
vm_name = server_name
unit_num = disk_uid
disk_size = disk_size
}
}
]...)
admin_password = var.admin_password != "" ? var.admin_password : random_password.admin_pass.result
# Backend'ы для ALB: если в vdc_key есть создаваемые ВМ их IP, иначе albConfiguration.alb_backend_ips
have_alb_vdc_servers = length([for k, v in local.servers : 1 if v.vcd_key == var.albConfiguration.vdc_key]) > 0
alb_backend_ips_from_vms = local.have_alb_vdc_servers ? [
for k, v in local.servers : vcd_vapp_vm.vm[k].network[0].ip
if v.vcd_key == var.albConfiguration.vdc_key
] : []
backend_ips_for_alb = length(local.alb_backend_ips_from_vms) > 0 ? local.alb_backend_ips_from_vms : var.albConfiguration.alb_backend_ips
}

14
alb/outputs.tf Normal file
View File

@ -0,0 +1,14 @@
output "alb_vip" {
description = "Выделенный внешний IP балансировщика (указать в DNS и в Ingress)"
value = var.albConfiguration.enabled ? vcd_ip_space_ip_allocation.ip_ingress[0].ip : null
}
output "alb_backend_ips" {
description = "IP backend'ов пулов ALB (из созданных ВМ или из albConfiguration.alb_backend_ips)"
value = var.albConfiguration.enabled ? local.backend_ips_for_alb : null
}
output "vm_ips" {
description = "Имена и внутренние IP созданных ВМ (если заданы servers в vcds)"
value = length(local.servers) > 0 ? { for k, v in local.servers : k => vcd_vapp_vm.vm[k].network[0].ip } : {}
}

33
alb/provider.tf Normal file
View File

@ -0,0 +1,33 @@
# ALB и опционально ВМ. Backend'ы albConfiguration.alb_backend_ips или из созданных ВМ.
terraform {
required_providers {
vcd = {
source = "vmware/vcd"
version = "~> 3.14"
}
random = {
source = "hashicorp/random"
version = "~> 3.0"
}
}
# backend при необходимости раскомментируйте и укажите свой
# backend "http" {
# address = "https://..."
# lock_address = "https://..."
# unlock_address = "https://..."
# }
}
provider "vcd" {
alias = "al"
for_each = var.vcds
user = var.vmware_username
password = var.vmware_password
org = each.value.org
vdc = each.value.vdc
url = each.value.url
max_retry_timeout = "10"
allow_unverified_ssl = true
}

View File

@ -0,0 +1,20 @@
#cloud-config
hostname: ${hostname}
package_update: true
package_upgrade: false
packages:
- atop
users:
- name: ${username}
sudo: "ALL=(ALL) NOPASSWD:ALL"
groups: "sudo"
shell: /bin/bash
ssh_authorized_keys:
%{ if length(ssh_authorized_keys) > 0 ~}
%{ for key in ssh_authorized_keys ~}
- ${key}
%{ endfor ~}
%{ else ~}
[]
%{ endif ~}

71
alb/terraform.tfvars Normal file
View File

@ -0,0 +1,71 @@
# Учётные данные лучше не коммитить: TF_VAR_vmware_username=... TF_vmware_password=... tofu apply
# vmware_username = ""
# vmware_password = ""
#######################################################
# vcds — список (map) определённых vDC с настройками для подключения
vcds = {
"main" = {
url = "https://sandbox.nubes.ru/api" # URL к VMware Cloud Director API
org = "dev" # название организации в VCD
vdc = "dev" # имя виртуального датацентра (vDC)
network_routed = "dev-mgmt-network" # основная сетевая инфраструктура (routed network)
servers = { # описание групп серверов в данном vDC
"testservers" = { # имя группы серверов (любое)
"test01" = { # имя/идентификатор первой машины
cpu = "1" # Количество vCPU для ВМ
memory = "1024" # Количество памяти (RAM), в мегабайтах
osDisk = "18000" # Размер основного (системного) диска, в мегабайтах
dataDisk = { # Дополнительные диски: "номер" = "размер_в_МБ"
"1" = "3000"
}
network = { # Настройки сети
mode = "auto" # Режим назначения IP (auto/manual)
ip = "" # Статический IP (если mode = manual), иначе "" для автоматического назначения
}
catalog_name = "dev" # имя каталога с шаблоном ВМ
template_name = "RockyLinux_9-16G-cloudinit" # название шаблона операционной системы
init_username = "deploy" # имя пользователя для входа по SSH
ssh_authorized_keys = [ # список публичных SSH-ключей для авторизации
"ssh-ed25519 ********"
]
}
"test02" = {
cpu = "1"
memory = "1024"
osDisk = "18000"
dataDisk = {
"1" = "2000"
}
network = {
mode = "auto"
ip = ""
}
catalog_name = "dev"
template_name = "RockyLinux_9-16G-cloudinit"
init_username = "deploy"
ssh_authorized_keys = [
"ssh-ed25519 ********",
"ssh-rsa ********"
]
}
}
}
}
}
# albConfiguration — параметры ALB (Load Balancer)
albConfiguration = {
enabled = true # включить ALB (true) или выключить (false)
vdc_key = "main" # ключ vDC из vcds
edge = "dev" # имя Edge Gateway
edgeScope = "vdc" # область применения Edge: "vdc" или "vdcGroup"
vdcGroupName = "" # имя vdcGroup (если используется vdcGroup)
srv_engine_group = "SEGROUP-SANDBOX-CL1-SHARED-01" # имя группы Service Engine для ALB
vcd_ip_space = "internet-ipv4-v1" # название IP Space для ALB
alb_name = "my-lb-dev" # префикс имён VS и пулов
alb_ports = [80, 443] # список портов для ALB (один пул на порт)
alb_backend_ips = [] # IP backend'ов, [] — получить автоматически из servers
}

5
alb/tools.tf Normal file
View File

@ -0,0 +1,5 @@
# Пароль администратора ВМ (если не задан в переменной)
resource "random_password" "admin_pass" {
length = 20
special = false
}

17
alb/vapp.tf Normal file
View File

@ -0,0 +1,17 @@
# vApp и сеть только для vDC, в которых заданы servers (создаём ВМ)
resource "vcd_vapp" "vapp" {
for_each = { for k, v in var.vcds : k => v if try(length(v.servers), 0) > 0 }
provider = vcd.al[each.key]
name = each.key
power_on = false
}
resource "vcd_vapp_org_network" "routed_network" {
for_each = vcd_vapp.vapp
provider = vcd.al[each.key]
vapp_name = each.value.name
org_network_name = var.vcds[each.key].network_routed
reboot_vapp_on_removal = true
}

59
alb/var.tf Normal file
View File

@ -0,0 +1,59 @@
####################################################
variable "vmware_username" {
type = string
default = ""
}
variable "vmware_password" {
type = string
default = ""
sensitive = true
}
####################################################
# Пароль администратора ВМ (если пустой генерируется в tools.tf)
variable "admin_password" {
type = string
default = ""
}
####################################################
# vDC: url, org, vdc обязательно. network_routed и servers для создания ВМ (backend'ов ALB).
variable "vcds" {
type = map(object({
url = string
org = string
vdc = string
network_routed = optional(string)
servers = optional(map(map(object({
cpu = string
memory = string
osDisk = string
catalog_name = string
template_name = string
dataDisk = optional(map(string), {})
network = object({
mode = string
ip = string
})
init_username = optional(string)
ssh_authorized_keys = optional(list(string), [])
}))), {})
}))
}
####################################################
variable "albConfiguration" {
type = object({
enabled = bool
vdc_key = string # ключ из var.vcds
edge = string # имя Edge Gateway в vCD
edgeScope = string # "vdc" или "vdcGroup"
vdcGroupName = string # при edgeScope = "vdcGroup"
srv_engine_group = string # имя Service Engine Group на Edge
vcd_ip_space = string # имя IP Space для выделения внешнего IP
alb_name = string # префикс имён пулов и Virtual Service в vCD
alb_ports = list(number) # порты для проброса (один пул и один VS на порт)
alb_backend_ips = optional(list(string), []) # IP backend'ов; пустой из созданных ВМ при наличии servers в vcds
})
}

70
alb/vm.tf Normal file
View File

@ -0,0 +1,70 @@
# ВМ создаются только при наличии servers в var.vcds. Пулы ALB зависят от этих ВМ (backend = их IP).
resource "vcd_vapp_vm" "vm" {
for_each = local.servers
provider = vcd.al[each.value.vcd_key]
vapp_name = vcd_vapp.vapp[each.value.vcd_key].name
name = each.key
computer_name = each.key
catalog_name = each.value.config.catalog_name
template_name = each.value.config.template_name
memory = each.value.config.memory
cpus = each.value.config.cpu
network {
type = "org"
name = each.value.network_routed
ip_allocation_mode = lookup(each.value.config.network, "mode", "auto") == "manual" ? "MANUAL" : "POOL"
is_primary = true
ip = lookup(each.value.config.network, "mode", "auto") == "manual" ? lookup(each.value.config.network, "ip", null) : null
}
override_template_disk {
bus_type = "paravirtual"
size_in_mb = each.value.config.osDisk
bus_number = 0
unit_number = 0
}
customization {
enabled = true
force = false
allow_local_admin_password = true
auto_generate_password = false
admin_password = local.admin_password
}
set_extra_config {
key = "disk.EnableUUID"
value = "TRUE"
}
guest_properties = {
"user-data" = base64encode(templatefile("${path.module}/templates/cloudinit.yaml", {
hostname = each.key
username = lookup(each.value.config, "init_username", "deploy")
ssh_authorized_keys = lookup(each.value.config, "ssh_authorized_keys", [])
}))
}
lifecycle {
ignore_changes = [disk]
}
depends_on = [vcd_vapp_org_network.routed_network]
}
resource "vcd_vm_internal_disk" "vmdisk" {
for_each = local.disks
provider = vcd.al[each.value.vcd_key]
vapp_name = vcd_vapp.vapp[each.value.vcd_key].name
vm_name = each.value.vm_name
bus_type = "paravirtual"
bus_number = 0
unit_number = each.value.unit_num
size_in_mb = each.value.disk_size
allow_vm_reboot = true
depends_on = [vcd_vapp_vm.vm]
}

View File

@ -3,9 +3,8 @@ hostname: ${hostname}
package_update: true package_update: true
package_upgrade: false package_upgrade: false
packages: packages:
- curl - atop
- git
- jq
users: users:
- name: ${username} - name: ${username}

View File

@ -8,7 +8,7 @@
vcds = { vcds = {
"test" = { "test" = {
url = "https://ngcloud.ru/api" url = "https://sandbox.nubes.ru/api"
org = "dev" org = "dev"
vdc = "dev" vdc = "dev"
network_routed = "dev-mgmt-network" network_routed = "dev-mgmt-network"
@ -19,18 +19,17 @@ vcds = {
memory = "1024" memory = "1024"
osDisk = "35000" osDisk = "35000"
dataDisk = { dataDisk = {
"1" = "10000" "1" = "6000"
"2" = "5480"
} }
network = { network = {
mode = "auto" mode = "auto"
ip = "" ip = ""
} }
catalog_name = "dev" catalog_name = "dev"
template_name = "Ubuntu_24-20G" template_name = "Ubuntu_24-8G"
init_username = "deploy" init_username = "deploy"
ssh_authorized_keys = [ ssh_authorized_keys = [
"ssh-ed25519 *********" "ssh-ed25519 *******"
] ]
} }
} }