Terraform удаляет предыдущий экземпляр при повторном запуске для создания нового - PullRequest
1 голос
/ 05 марта 2020

Я новичок в terraform и использую версию v0.12.20. Я заметил, что это удаляет существующий ресурс, как только я повторно запускаю его, чтобы создать новый. Например, если я создаю 10 vms в определенной среде и хочу создать 5 новых, он должен просто создавать новые, обнаруживающие изменение, а не удаляя предыдущие и создавая новые.

 resource "vsphere_virtual_machine" "test_vms" {
      name = "${var.environment_test}${count.index + 1}"
      resource_pool_id = data.vsphere_compute_cluster.cluster.resource_pool_id
      datastore_id     = "${element(data.vsphere_datastore.datastore.*.id, count.index)}"

      num_cpus         = var.cpu
      count            = var.count
      memory           = var.memory
      guest_id         = "ubuntu64Guest"
      folder           = var.vmfolder
      cpu_hot_add_enabled    = var.cpu_hot_add_enabled
      memory_hot_add_enabled = var.memory_hot_add_enabled
      scsi_type        = data.vsphere_virtual_machine.template.scsi_type
      wait_for_guest_net_timeout = -1

      network_interface {
         network_id    = data.vsphere_network.network[1].id
        adapter_type = data.vsphere_virtual_machine.template.network_interface_types[0]
      }


      disk {
        label            = "disk0"
         size            = "${var.disk_size}"
        thin_provisioned = data.vsphere_virtual_machine.template.disks.0.thin_provisioned
      }

      clone {
        template_uuid = data.vsphere_virtual_machine.template.id

        customize {
          linux_options {
            host_name     =  "${var.environment_test}${count.index + 1}"
            domain       = var.vmdomain
          }

          network_interface {
              ipv4_address =   "${var.ips_test}${var.test_index + count.index}"
               ipv4_netmask =  "${var.netmask_app}"
         }
     dns_server_list = "${var.dns_server_list}"
             dns_suffix_list = "${var.dns_suffix_list}"

             ipv4_gateway = "${var.gateway_app}"
        }

          }

    }

Вывод terraform план -

Terraform will perform the following actions:

  # vsphere_virtual_machine.small_vm[0] must be replaced
-/+ resource "vsphere_virtual_machine" "test_vm" {

      ~ memory_share_count                      = 81920 -> (known after apply)
        memory_share_level                      = "normal"
        migrate_wait_timeout                    = 30
      ~ name                                    = "tests01" -> "testm01

      ~ clone {


              ~ linux_options {
                    domain       = "X.X.X.X."
                  ~ host_name    = "tests01" -> "testm01" # forces replacement
                    hw_clock_utc = true
                }

              ~ network_interface {
                  - dns_server_list = [] -> null
                  ~ ipv4_address    = "X.X.X.X" -> "X.X.X.X" # forces replacement
                    ipv4_netmask    = 24
                  - ipv6_netmask    = 0 -> null


1 Ответ

2 голосов
/ 07 марта 2020

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

Внутренне Terraform связывает каждый экземпляр ресурса, объявленный в вашей конфигурации, с удаленным объектом в поставщике. удаленный API. В вашем случае каждый из vsphere_virtual_machine.test_vm экземпляров (которые Terraform вызывает vsphere_virtual_machine.small_vm[0], vsphere_virtual_machine.small_vm[1], et c в зависимости от вашего значения count) связан с одной реальной виртуальной машиной в vSphere, и когда вы позже внесите изменения в конфигурацию. Terraform планирует обновить или заменить удаленные объекты, чтобы удаленная система соответствовала изменениям в конфигурации.

Я думаю, что наиболее близкий образец использования, которого вы хотите достичь, определить входную переменную, которая представляет собой карту описаний виртуальных машин, которые должны существовать, например:

variable "virtual_machines" {
  type = map(object({
    num_cpus = number
  }))

  default = {
    tests01 = {
      num_cpus = 2
    }
    testm01 = {
      num_cpus = 1
    }
  }
}

Я включил этот атрибут num_cpus в объекты только для иллюстрации того, как вы можете определить атрибуты для представления значений, отличных от имени, которые могут потребоваться для разных виртуальных машин. (Если вам не нужно, чтобы эти виртуальные машины отличались каким-либо образом , кроме их имен, вы можете установить тип на map(object({})), чтобы использовать пустые объекты на данный момент, поэтому у вас есть место для расширения позже .)

С этой переменной вы можете использовать for_each вместо count, чтобы сказать Terraform, что нужно создать один экземпляр виртуальной машины для каждого элемента в карте var.virtual_machines:

resource "vsphere_virtual_machine" "test_vms" {
  for_each = var.virtual_machines

  name             = each.key
  resource_pool_id = data.vsphere_compute_cluster.cluster.resource_pool_id
  datastore_id     = data.vsphere_datastore.datastore.id

  num_cpus                   = each.value.num_cpus
  memory                     = var.memory
  guest_id                   = "ubuntu64Guest"
  folder                     = var.vmfolder
  cpu_hot_add_enabled        = var.cpu_hot_add_enabled
  memory_hot_add_enabled     = var.memory_hot_add_enabled
  scsi_type                  = data.vsphere_virtual_machine.template.scsi_type
  wait_for_guest_net_timeout = -1

  network_interface {
    network_id   = data.vsphere_network.network[1].id
    adapter_type = data.vsphere_virtual_machine.template.network_interface_types[0]
  }

  disk {
    label            = "disk0"
    size             = var.disk_size
    thin_provisioned = data.vsphere_virtual_machine.template.disks[0].thin_provisioned
  }

  clone {
    template_uuid = data.vsphere_virtual_machine.template.id

    customize {
      dns_server_list = var.dns_server_list
      dns_suffix_list = var.dns_suffix_list

      ipv4_gateway = var.gateway_app

      linux_options {
        host_name = each.key
        domain    = var.vmdomain
      }

      network_interface {
        ipv4_address = "${var.ips_test}${var.test_index + count.index}"
        ipv4_netmask = var.netmask_app
      }
    }
  }
}

Внутри блока resource "vsphere_virtual_machine" "test_vms" я использовал each.key для доступа к каждому ключу из var.virtual_machines (в данном случае имен виртуальных машин) и each.value для доступа к соответствующему объекту, который содержит атрибут num_cpus.

Учитывая значение по умолчанию var.virtual_machines, которое я объявил выше, Terraform будет интерпретировать это как запрос на создание двух виртуальных машин со следующими адресами отслеживания:

  • vsphere_virtual_machine.test_vms["tests01"] (с двумя ЦП)
  • vsphere_virtual_machine.test_vms["testm01"] (с одним ЦП)

Обратите внимание, что Terraform использует ключи с карты для отслеживания каждого отдельного экземпляра этого ресурса. Если вы хотите добавить новую виртуальную машину, не мешая другим, вы можете добавить новую запись к значению var.virtual_machines, , оставив все существующие элементы в покое . Каждый раз, когда вы добавляете новый элемент на эту карту и снова запускаете terraform apply, Terraform планирует создать новый экземпляр виртуальной машины. Аналогичным образом, если вы удалите или отредактируете запись в этой карте, Terraform планирует уничтожить или обновить / заменить соответствующую виртуальную машину.


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

Общий подход для этого использовать конфигурацию, которую вы уже написали как общий модуль , и затем для каждой новой среды, которую вы хотите создать, вы можете написать небольшую новую конфигурацию, которая содержит только один вызов этого модуля с настройками которые применяются к этой указанной c среде:

module "environment1" {
  # This is a relative path to whatever directory contains
  # the module whose configuration we've been discussing so far.
  source = "../../modules/environment"

  virtual_machines = {
    env1foo = {
      num_cpus = 2
    }
    env1bar = {
      num_cpus = 2
    }
  }

  vmfolder            = "example"
  cpu_hot_add_enabled = false
  # (and so on, for all of the other environment-specific variables
  # you need to override)
}

Каждый отдельный модуль Terraform является отдельным каталогом, поэтому вы можете сформировать структуру каталогов примерно так, например:

- environments/
  - environment1/
    - environment.tf
  - environment2/
    - environment.tf
- modules
  - environment
    - variables.tf
    - main.tf
    - (etc)

Чтобы создать новую среду, вы должны сделать новый подкаталог environments и запишите туда новый environment.tf, затем переключитесь в этот каталог и запустите Terraform:

cd environments/environment1
terraform init
terraform apply

Terraform отслеживает объекты для каждой конфигурации отдельно в отдельном состоянии снимок, поэтому, имея отдельный каталог конфигурации для каждого, вы можете работать с каждой средой отдельно, не привлекая конфигурацию от других. Однако каждый из них будет использовать один и тот же модуль «среда», и поэтому все они будут построены из одного определения, и вы можете обновить этот общий модуль «среда» в любое время, когда захотите внести изменения, которые должны применяться ко всем ваше окружение.

...