Перейти к содержанию

s3e23

Модульная застройка

Такс… Чтож такое эти ваши модули? Если на простом: модуль нужен, чтобы не копировать один и тот же код, а использовать его много раз.

Создаем модуль:

terraform-project/
├── main.tf 
├── providers.tf
├── variables.tf
├── modules/
   └── vm_volume_boot/
       ├── main.tf
       ├── variables.tf
       └── outputs.tf

И заполняем файлы модуля:

modules/main.tf

data "openstack_images_image_v2" "image" {
  name        = var.image_name
  most_recent = true
  visibility  = "public"
}

resource "openstack_blockstorage_volume_v3" "root" {
  name        = "${var.name}-root"
  size        = var.root_volume_size
  image_id   = data.openstack_images_image_v2.image.id
}

resource "openstack_compute_instance_v2" "vm" {
  name      = var.name
  flavor_id = var.flavor_id

  block_device {
    uuid                  = openstack_blockstorage_volume_v3.root.id
    source_type           = "volume"
    destination_type      = "volume"
    boot_index            = 0
    delete_on_termination = true
  }
}

modules/variables.tf

variable "name" {
  description = "VM name"
  type        = string
}

variable "image_name" {
  description = "OS image name"
  type        = string
  default     = "Ubuntu 24.04 LTS 64-bit"
}

variable "flavor_id" {
  description = "Flavor ID (diskless)"
  type        = string
}

variable "root_volume_size" {
  description = "Root volume size in GB"
  type        = number

  validation {
    condition     = var.root_volume_size >= 10
    error_message = "Root volume must be at least 10 GB"
  }
}

А в корневом main.tf делаем так:

module "vm1" {
  source = "./modules/vm_volume_boot"

  name             = "linuxfactory-1-20"
  flavor_id        = "1"
  root_volume_size = 20
}

Готовые IDшники flavors можно подсмотреть в официальной документации. Для примера выше, я взял ID=1 что соответствует 1 CPU / 512 RAM и 0 диска. Диск мы создаем с помощью модуля и цепляем к этому серверу.

Запускаем:

terraform init
terraform apply

Проверяем и видим, что сервер успешно создан:

Теперь добавляем в корневой main.tf еще один сервер, но с другими параметрами:

module "vm1" {
  source = "./modules/vm_volume_boot"

  name             = "linuxfactory-1-20"
  flavor_id        = "1"
  root_volume_size = 20
}

module "vm2" {
  source = "./modules/vm_volume_boot"

  name             = "linuxfactory-2-10"
  flavor_id        = "2"
  root_volume_size = 10
}

Запускаем и смотрим что получилось:

А ведь всё правильно!

Модуль это что-то вроде шаблона, по которому ты будешь создавать ресурсы. Terraform модуль это функция. Ты прячешь сложную логику внутрь модуля, а в основном коде пишешь просто и понятно.

Если еще проще — Каждый раз готовишь бургер с нуля: месишь тесто, жаришь котлету, нарезаешь овощи. Каждый раз одно и то же.

А с модулями будет так — Сделай бургер с говядиной, сделай бургер с курицей. Рецепт один — параметры разные.

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

При первом запуске с модулем, ты можешь наступить на грабли:

Пофикси эту проблему. Подсказывать не буду, там всё достаточно просто.

С модулями ты стандартизируешь создание инфраструктуры, у тебя одинаковые диски, одинаковые Security Group, одинаковые user_data и т.п. Изменяешь в одном месте один параметр и все ресурсы в корневом main.tf это подхватывают.

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


Домашнее задание

  • Создай подобный модуль, добавь туда сети и security group. Пусть твоя инфраструктура создается на модульной основе.