Terraform: создание и управление инфраструктурой

В этой статье расскажем о преимуществах использования Terraform: что это такое, какие задачи решает и опишем подробнее процесс установки на Windows, Ubuntu, centOS.

Terraform - это программный инструмент для создания инфраструктуры в облаке при помощи кода, так называемый IaC (Infrastructure as code). 

Terraform, как инструмент, несет преимущества для пользователей облачных сервисов, в том числе и в облаке mClouds.  Terraform позволит быстро развернуть и настроить инфраструктуру с помощью кода, упростит администрирование облачными ресурсами. В этом и состоит главная задача Terraform — доступное и простое управление состоянием ИТ-инфраструктуры.

В работе с инструментом отметим следующие преимущества:
  • Скорость развертывания пользовательских виртуальных стендов(теннантов - логических сущностей, использующих выделенные ресурсы в своих целях). Вся виртуальная среда может быть развернута одновременно с необходимыми параметрами с минимальным количеством кликов и без участия технической поддержки.
  • Моментальная проверка плана активации нового теннанта. С помощью описанного кода можно проверить, что и в каком порядке будет создано или изменено.
  • Удобное использование для создания демо-стендов под тестирование и отладку программного обеспечения. Вы можете создавать и передавать стенды для отдела тестирования, параллельно проверять ПО в разных средах, а также моментально изменять и удалять ресурсы, создав лишь один план построения ресурсов.

Из чего состоит Terraform 

Кратко разберемся с составляющими Terraform:

Providers (провайдеры)

В Terraform всякий тип инфраструктуры описывается как ресурс. Связь между ресурсами и платформой API обеспечивается благодаря модулям-провайдерам. Они позволяют создавать ресурсы в рамках нужной платформы, например, VMware vCloud Director. В рамках одного проекта можно работать сразу с несколькими провайдерами одновременно.

Resources (описание ресурсов)

Описание ресурсов, фактически - это описание вашей инфраструктуры. Сеть, виртуальная машина, правило для межсетевого экрана или для проброса порта - все это есть ресурс.

Вы можете самостоятельно создавать описание ресурсов для провайдера VMware vCloud Director и применять описание для создания ресурсов у любого хостинг-провайдера, которой использует vCloud Director. Достаточно лишь изменить параметры аутентификации и параметры подключения к необходимому провайдеру.

Provisioners

Эта составляющая позволяет выполнять операции по первоначальной настройке и обслуживанию операционной системы после создания виртуальных машин. После того, как будет создан ресурс виртуальной машины, с помощью provisioners, вы можете настроить и подключиться к ней по SSH и, например, выполнить обновление ОС, а также загрузить и выполнить скрипт.

Input и Output  переменные

Input переменные - входные переменные для любых типов блоков. Output переменные позволяют сохранить значения после создания ресурсов и могут быть использованы в качестве входных переменных в других модулях, например в блоке Provisioners.

States (состояния)

Хранят информацию о конфигурации ресурсов платформы провайдера. 

Структура блоков

В Terraform все состоит из блоков разных видов. Общая структура блока выглядит следующим образом:

BLOCK_TYPE “BLOCK_LABEL” “BLOCK_LABEL”{
#block body
<Identifier> = expression# пример объявления составляющих блоков
}


Язык Terraform является декларативным, поэтому порядок следования блоков значения не имеет, кроме блоков provisioner,  где описывается команды для выполнения при подготовке инфраструктуры. И нам важно, чтобы блоки шли в правильном порядке, иначе можно получить ошибку.

Terraform использует язык программирования HCL. Подробную информацию о синтаксисе можно почитать здесь.

Terraform можно загрузить с официального сайта разработчика. В Российской федерации он блокируется, поэтому для загрузки дистрибутива, доступа к документации и дальнейшего использования необходимо использовать proxy.

После загрузки исполняемого файла его необходимо будет положить в папку с проектом.

Создание инфраструктуры

С теорией разобрались. Настало время собственноручно создать инфраструктуру. В нашем примере мы будем создавать vAPP, три виртуальные машины:

  • Windows Server,
  • Debian с nginx в качестве web сервера,
  • CentOS в качестве FTP-сервера.

Также мы создадим локальную сеть, подключим к ней виртуальные машины и настроим для них доступ в интернет. Плюс мы настроим правила для удаленного доступа к виртуальным машинам из сети Интернет.

Структура нашего проекта будет проста и состоять всего из двух файлов:
  • tf - файл с описанием переменных.
  • tf - файл с объявлениями параметров для нашей виртуальной инфраструктуры.

Вообще можно создавать отдельные файлы с описанием различных параметров, например, файл network.tf, где мы могли бы описать только сетевые параметры. Но мы ограничимся только двумя файлами для простоты примера.

Для работы мы будем использовать следующий набор:
  1. Ноутбук с Windows 11 и официальный дистрибутив версии 1.3.7.
  2. Для редактирования кода будем применять Visual Studio Code с установленным расширением Terraform для удобства.

Конфигурация виртуальных машин

Конфигурация виртуальных машин выглядит так:

  1. Итак, создадим нашу инфраструктуру и начнем с файла settings.tf, где опишем необходимые нам переменные.

Перво-наперво, опишем переменную провайдера, где укажем параметры подключения к виртуальному дата-центру:

variable "connect" {
 type = map
 default = {
 "organization" = "organization-name"
 "url_connect" = "organization-api url"
 "data_center" = "data-center name"
 "user" = "organization-administrator name"
 "password" = "organization-administrator password"
 }
}

Что же мы тут написали? Мы указали переменную connect и присвоили ей тип map - словарь, то есть, он будет содержать в себе набор значений по принципу “ключ-значение”. И далее в блоке default мы указываем наши данные:

  • organization - название нашей организации,
  • url_connect - ссылка для отправки api запросов в наш дата центр,
  • data_center - название дата центра,
  • user - имя пользователя-администратора дата центра,
  • password - пароль пользователя.

Параметры можно найти в vCloud Director: подменю Administration в разделе Settings -> General.

2. Далее опишем внешнюю сеть, к которой будет подключаться наша инфраструктура:

variable "main_net"{
    type = map
    default = {
        name = "EXTENDED-NET-NAME"
        type = "ext"
    }
}

Здесь мы также используем тип map.

3. После этого перейдем к ответственному шагу - переменной, описывающую нашу локальную сеть (саму сеть мы создадим далее):

variable "routed_net"{
    type = map
    default = {
        "edge" = "sanya-test-EDGE" # название виртуального маршрутизатора
        "name" = "test_net" # название сети
        "gateway" = "10.10.10.254" # шлюз по умолчанию
        "cidr" = "10.10.10.0/24" # идентификатор сети
        "static_start" = "10.10.10.1" # начальный адрес пула
        "static_end" = "10.10.10.200" # конечный адрес пула
        "type" = "org" # тип сети(в нашем случае - сеть организации)
        "netmask" = "255.255.255.0" # маска сети
        "dns_1" = "1.1.1.1" # первый dns сервер
        "dns_2" = "8.8.8.8" # второй dns сервер
    }
}

И здесь мы тоже использовали тип map и указали параметры нашей будущей сети.

4. И последняя переменная - это переменная “edge”, описывающая наш граничный маршрутизатор, его название и внешний IP-адрес:

variable "edge"{
    type = map
    default = {
        "name" = "test-EDGE"
        "ip" = "public ip"
    }
}

На этом мы закончили описывать переменные и теперь переходим к файлу main.tf. В нём мы будем описывать нашу будущую инфраструктуру.

В этом файле порядок блоков важен, так как в противном случае, мы не сможем, например, подключить сеть к виртуальной машине, если она не будет создана.

Итак, начнём.

  1. Первым делом, напишем блок провайдера:
terraform {
        required_providers {
        vcd = {
        source = "vmware/vcd"
        version = "3.7"
        }
    }
    required_version = ">= 0.13"
}

В этом блоке указываем каким провайдером мы будем пользоваться, уточняем необходимую версию. В нашем случае будем пользоваться провайдером vcd, так как наша инфраструктура построена на VMWare. Версию Terraform мы указываем 3.7. Такую мы используем в нашей компании.

2. Далее идёт блок подключения к vCloud Director:

provider "vcd" {
    auth_type = "integrated"
    max_retry_timeout = 10
    user = var.connect["user"]
    password = var.connect["password"]
    org = var.connect["organization"]
    url = var.connect["url_connect"]
}

Тут мы указываем тип аутентификации - внутренний, максимальную задержку попытки и данные для подключения: пользователя, пароль, организацию и url-ссылку для обработки api запросов. Если вы уже заметили, мы используем данные из переменной connect, которую описывали в файле settings.tf.

Обратите внимание на синтаксис. В общем случае он выглядит вот так: var.variable_name[“parameter_name”].

Очень похоже на обращение к элементу словаря по его ключу в языках программирования. Этим мы еще не раз воспользуемся в дальнейшем.

3. Далее идёт блок создания vApp, в котором будут храниться наши виртуальные машины:

resource "vcd_vapp" "vapp" {
    org = var.connect["organization"]
    vdc = var.connect["data_center"]
    name = "testVApp"
    power_on = true
    depends_on = [vcd_network_routed.net]
}

В этом блоке, кода мы указываем организацию и виртуальный дата-центр (их надо указывать в начале каждого блока создания ресурсов в дата-центре, иначе может возникнуть ошибка).

4. Далее мы указываем имя нашего VApp - testVapp, говорим, что он будет сразу включен после создания.

5. Далее идет важная часть -  зависимость от другого блока. Это делается для того, чтобы быть точно уверенным, что нужный ресурс будет создан, чтобы не получить ошибку на моменте создания. В нашем случае мы указываем, что обязательна должна быть создана сеть, чтобы её можно было добавить в VApp. Синтаксис обращения к ресурсу отличается от синтаксиса обращения к переменной: сначала указывается название ресурса в провайдере, затем через точку пишется произвольное название нашего ресурса. Первая часть обязательно должна быть ресурсом, входящим в состав провайдера, иначе мы не сможем создать и получим ошибку.

Более подробно ознакомиться с перечнем ресурсов провайдера vcd и списком параметров каждого ресурса можно здесь.

6. Далее мы создаем нашу локальную сеть, используя следующий код:

resource "vcd_network_routed" "net"{
    org = var.connect["organization"]
    vdc = var.connect["data_center"]
    name = var.routed_net["name"]
    edge_gateway = var.routed_net["edge"]
    gateway = var.routed_net["gateway"]
    netmask = var.routed_net["netmask"]
    dns1 = var.routed_net["dns_1"]
    dns2 = var.routed_net["dns_2"]
    static_ip_pool { #Пул статик IP-адресов
        start_address = var.routed_net["static_start"] #первый адрес пула
        end_address = var.routed_net["static_end"] #последний адрес пула
    }
}

В нём мы создаем ресурс vcd_network_routed, так как наша сеть будет маршрутизируемой и иметь доступ в Интернет, и называем его net. В его параметрах мы указываем данные из переменной routed_net, не забывая в начале описания ресурса указать организацию и дата-центр.

7. Далее мы подключаем нашу сеть к VApp через описание ресурса vcd_vapp_org_network:

resource "vcd_vapp_org_network" "direct_network" {
    org = var.connect["organization"]
    vdc = var.connect["data_center"]
    vapp_name        = vcd_vapp.vapp.name
    org_network_name = vcd_network_routed.net.name
}

Так как в блоке создания VApp мы указали зависимость от ресурса сети, то мы точно уверены, что добавляемая нами сеть создана с нужными нам параметрами, и остается  лишь присвоить параметрам ресурса нужные значения.

8. Далее идёт самые большие по количество строк кода ресурсы - наши виртуальные машины:

resource "vcd_vapp_vm" "ubuntu23"{
    org = var.connect["organization"]
    vdc = var.connect["data_center"]
    vapp_name = vcd_vapp.vapp.name
    name = "ubuntu23"
    computer_name = "ubuntu23"
    catalog_name = "Templates"
    template_name = "GoldUbuntu23"
    power_on = true
    cpus = 1
    cpu_cores = 1
    memory = 4096
    storage_profile = "Gold Storage Policy"   
    network {
        type = "org"
        name = vcd_vapp_org_network.direct_network.org_network_name
        is_primary = true
        adapter_type = "VMXNET3"
        ip_allocation_mode = "MANUAL"
        ip = "10.10.10.4"
    }
    override_template_disk {
        bus_type = "paravirtual"
        size_in_mb = "32768"
        bus_number = 0
        unit_number = 0
        storage_profile = "Silver Storage Policy"
    }
    depends_on = [vcd_vapp_org_network.direct_network]
}

 *описание виртуальной машины с Ubuntu23.

Итак, как видим, мы создали ресурс виртуальной машины centos, указали имя каталога и имя темплейта, из которого виртуальная машина будет создана. Далее мы поставили параметр power_on в значение true, что говорит о том, что машина будет сразу включена после создания.

После мы выдали нашей машине 1 виртуальное ядро процессора и 4 ГБ оперативной памяти и указали политику жестких дисков.

После этого идёт блок с описанием сетевых параметров:
  • мы обозначили тип сети как “сеть организации”(сеть именно такого типа мы создали чуть выше) и её имя. Наш сетевой интерфейс будет главным у виртуальной машины (параметр is_primary стоит в значении true).
  • мы указали тип адаптера как VMXNET3 (такой тип надо указывать всегда), способ получения ip адреса как “ручной”, то есть мы вручную настроим его и выдали нашей машине ip address 10.10.10.4.
  • далее мы изменяем объем дискового пространства машины (параметр override_template_disk). Здесь мы назначили тип шины как “паравиртуальная”, указали размер нашего диска - 32 ГБ (для Windows server мы будем выделять 40 ГБ дискового пространства). Следует отметить, что размер диска указывается в МБ.

Каждый блок ресурса виртуальной машины идентичен по наполнению, за исключением используемой ОС, имени компьютера, PI-адресу и пространству на диске. Поэтому мы не будем описывать ресурсы двух оставшихся виртуальных машин, отметим, что в них мы применим кастомизацию ОС.

resource "vcd_vapp_vm" "debian"{
    org = var.connect["organization"]
    vdc = var.connect["data_center"]
    vapp_name = vcd_vapp.vapp.name
    name = "debian12"
    computer_name = "debian-vm"
    catalog_name = "Templates-name"
    template_name = "debian-template-name"
    power_on = true
    cpus = 1
    cpu_cores = 1
    memory = 4096
    storage_profile = "Gold Storage Policy"   
    network {
        type = "org"
        name = vcd_vapp_org_network.direct_network.org_network_name
        is_primary = true
        adapter_type = "VMXNET3"
        ip_allocation_mode = "MANUAL"
        ip = "10.10.10.2"
    }
    override_template_disk {
        bus_type = "paravirtual"
        size_in_mb = "32768"
        bus_number = 0
        unit_number = 0
        storage_profile = "Gold Storage Policy"
    }
    customization {
        enabled                    = true
        force                      = true
        allow_local_admin_password = true
        auto_generate_password     = false
        admin_password             = "password-example"
    }
    depends_on = [vcd_vapp_org_network.direct_network]
}

*Описание виртуальной машины с debian

resource "vcd_vapp_vm" "winsrv"{

    org = var.connect["organization"]

    vdc = var.connect["data_center"]

    vapp_name = vcd_vapp.vapp.name

    name = "windowsServer2019"

    computer_name = "winserver"

    catalog_name = "templates-name"

    template_name = "windows-server-2019-template-name"

    power_on = true

    cpus = 1

    cpu_cores = 1

    memory = 4096

    storage_profile = "Gold Storage Policy"   

    network {

        type = "org"

        name = vcd_vapp_org_network.direct_network.org_network_name

        is_primary = true

        adapter_type = "VMXNET3"

        ip_allocation_mode = "MANUAL"

        ip = "10.10.10.3"

    }

    override_template_disk {

        bus_type = "paravirtual"

        size_in_mb = "61440"

        bus_number = 0

        unit_number = 0

        storage_profile = "Gold Storage Policy"

    }

    customization {

        enabled                    = true

        force                      = true

        allow_local_admin_password = true

        auto_generate_password     = false

        admin_password             = "password-example"

    }

    depends_on = [vcd_vapp_org_network.direct_network]

}

*Описание виртуальной машины с Windows Server

Кастомизация ОС делается следующим образом:

  • сначала мы включаем ее (enabled - true) - это обязательный параметр, без него сделать кастомизацию не выйдет,
  • после мы ставим параметр force в true, что говорит о том, что виртуальная машина будет запущена с применением кастомизации.
  • далее мы меняем пароль администраторской учетной записи (в Unix-системах - это пользователь root, в Windows - Администратор). Указываем, что мы будем менять пароль администратора (allow_local_admin_password = true), отключаем автогенерацию пароля (auto_generate_password = false) и указываем сам пароль (admin_password= “password-example”).

Создание правил межсетевого экрана и NAT

Теперь мы переходим к созданию правил межсетевого экрана и NAT.

resource "vcd_nsxv_snat" "internet_access"{

    org = var.connect["organization"]

    vdc = var.connect["data_center"]




    edge_gateway = var.edge["name"]

    network_type = var.main_net["type"]

    network_name = var.main_net["name"]




    original_address = var.routed_net["cidr"]

    translated_address = var.edge["ip"]

    depends_on = [vcd_network_routed.net]

}

Этим ресурсом мы создаем правило для доступа нашей локальной сети в Интернет. Здесь нам интересны следующие параметры:

  1. network-name - имя сети, к которой будет применимо правило (в нашем случае - это внешняя сеть, к которой подключен ЦОД),
  2. original_address - адрес источника до преобразования. В него мы передаем CIDR нашей внутренней сети организации, то есть под это правило будет попадать любой трафик, исходящий из локальной сети,
  3. далее идет адрес после преобразования  - наш внешний IP-адрес.

Таким нехитрым образом мы даем всем нашим виртуальным машинам доступ в Интернет. Следующими тремя ресурсами мы создадим правила для проброса портов из Интернета во внутреннюю сеть для удаленного доступа к виртуальным машинам:

resource "vcd_nsxv_dnat" "ubuntu"{

    org = var.connect["organization"]

    vdc = var.connect["data_center"]

  

    edge_gateway = var.edge["name"]

    network_type = var.main_net["type"]

    network_name = var.main_net["name"]




    original_address = var.edge["ip"]

    original_port = "38004"

    translated_address = "10.10.10.4"

    translated_port = "22"

    protocol = "tcp"

}

*правило для доступа к ubuntu по SSH

resource "vcd_nsxv_dnat" "debian"{

    org = var.connect["organization"]

    vdc = var.connect["data_center"]

  

    edge_gateway = var.edge["name"]

    network_type = var.main_net["type"]

    network_name = var.main_net["name"]




    original_address = var.edge["ip"]

    original_port = "38002"

    translated_address = "10.10.10.2"

    translated_port = "22"

    protocol = "tcp"

}

*правило для доступа к debian по SSH

resource "vcd_nsxv_dnat" "windows"{

    org = var.connect["organization"]

    vdc = var.connect["data_center"]

  

    edge_gateway = var.edge["name"]

    network_type = var.main_net["type"]

    network_name = var.main_net["name"]




    original_address = var.edge["ip"]

    original_port = "38003"

    translated_address = "10.10.10.3"

    translated_port = "3389"

    protocol = "tcp"

}

*правило для доступа к Windows server по RDP

Отметим, что в целях безопасности, удаленный доступ происходит по нестандартным портам (напомним, что для SSH стандартным является порт 22 tcp, для протокола RDP - 3389 tcp), и они подменяются на этапе прохождения трафика через маршрутизатор edge.

Запуск кода и создание инфраструктуры

Теперь, когда инфраструктура описана, ее можно создать.

Для этого перейдите в папку проекта, где уже находятся два файла с кодом и загруженный ранее дистрибутив, и выполните команду terraform.exe init. (если вы используете PowerShell, то команда выглядит следующим образом: ./terraform init):

Как мы видим, Terraform успешно проинициализирован и готов к последующей работе. Далее мы вводим команду terraform.exe plan, которой добавляем наш код в очередь, проверяем его на ошибки и проверяет дата центр на то, что описанная выше инфраструктура уже существует. Та, которой нет, будет добавлена в очередь на создание:

* Неполный вывод команды terraform.exe plan

После ввода команды мы получим следующий результат:  

Такой план связан с тем, что до этого уже создавались ресурсы при помощи Terraform , 2 ресурса будут удалены, так как их параметры поменялись, и Terraform удалит их, чтобы пересоздать, и 6 ресурсов будут созданы с нуля. В случае, если конфигурация создается с нуля, то terraform будет только создавать и ничего не изменять и удалять.

Для создания ресурсов вводим команду terraform.exe apply:

* Неполный вывод команды terraform.exe plan

Далее вводим yes в терминал для подтверждения и ждем, пока Terraform создаст всю вышеописанную инфраструктуру.

Результат - успех. Вся инфраструктура создана!

* Часть вывода команды terraform.exe apply

Если мы зайдем в vCloud Director, то мы увидим, что инфраструктура создана.

Далее можно подключаться к виртуальным машинам и производить дополнительную настройку.

Итоги

Подведем итоги, что мы узнали:

  • Terraform - это OpenSource гибкая система управления состоянием инфраструктуры, который использует декларативный стиль написания кода.
  • Вся инфраструктура может быть развернута одновременно с необходимыми параметрами.
  • Подключение к облачным провайдерам происходит через специальные модули - провайдеры. Для подключения к VMWare vCloud Director используется провайдер VCD
  • Terraform использует собственный язык программирования HCL. Синтаксис языка напоминает JSON, но имеет блочную структуру.
  • IaC код можно хранить, например, на GitHub, что дает единую точку входа для работы с инфраструктурой и повышает прозрачность кода - все внесенные в код правки должны пройти согласование.

Также мы создали свою инфраструктуру, состоящую из локальной сети, одного VApp, трех виртуальных машин, правил межсетевого экрана для удаленного доступа к машинам из сети Интернет по нестандартным портам и доступа внутренней сети в Интернет.

В заключение хочется сказать, что  Terraform - очень удобный инструмент для развертывания инфраструктуры, использующий код для ее описания. Да, итоговый код может выглядеть громоздким и на его написание может уйти время, но потом введя всего три команды, мы получаем готовую инфраструктуру, которую можно использовать сразу либо дополнительно настроить, а после уже использовать.

 

 

 

 

Наш телеграм-канал
Пишем про облака, кейсы, вебинары
Подписаться