【问题标题】:Terraform deletes the previous instance when you re-run it to create newTerraform 会在您重新运行它以创建新实例时删除之前的实例
【发布时间】:2020-03-05 19:07:22
【问题描述】:

我是 terraform 新手,使用的是 v0.12.20 版本。我注意到,一旦我重新运行它以创建新资源,它就会删除现有资源。例如,如果我在某个 env 中创建 10 个 vm 并想创建 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


【问题讨论】:

  • 您好!您能否编辑您的问题以包含来自terraform plan 的输出,您在其中看到了意外的计划更改?希望通过看到我或其他人可以解释 Terraform 正在做什么的原因以及您可能会如何影响 Terraform 制定不同的计划。
  • @MartinAtkins 非常感谢您的回复。
  • 我在上面添加了 terraform plan 的输出。我创建了一个名为 test01 的虚拟机并重新运行脚本以创建一个名为 testm01 的新虚拟机,但正如您所见,该脚本替换了旧的虚拟机并为其分配了新的 IP 地址。我宁愿期望它用新的 IP 地址创建一个新的虚拟机。如何实现?
  • 不幸的是,这里的问题似乎并非所有信息。我不确定teststestm 来自哪里。你在改变var.environment_test 的值吗?您能否在问题中包含更多配置,以便我可以看到使 tests 变为 testm 的更改?
  • 我正在更改 var.environment_test 的值。最初我使用 environment_test 值作为测试运行脚本,后来我将 environment_test 的值修改为 testm。我原以为会创建一个新的 vm testm01 而不会删除前一个,但实际上前一个被删除并形成了新的。我们用少量虚拟机构建了一个环境,并在需要时不断创建新的虚拟机。例如,如果我在一个 env 中创建 50 个 vm,几天后我想再创建几个,我应该用新的 vm 名称和 IP 修改 var,它不应该删除以前的,而只是创建新的。

标签: terraform vsphere


【解决方案1】:

不幸的是,Terraform 不是为这种你只想创建新事物然后将它们留给其他地方管理的用例而设计的。相反,Terraform 的模型是管理长期存在的对象,您可能需要随着时间的推移在 Terraform 中对它们进行更改。

在内部,Terraform 将配置中声明的每个资源实例与供应商远程 API 中的远程对象相关联。在您的情况下,每个 vsphere_virtual_machine.test_vm 实例(Terraform 调用 vsphere_virtual_machine.small_vm[0]vsphere_virtual_machine.small_vm[1] 等,具体取决于您的 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"](有两个 CPU)
  • vsphere_virtual_machine.test_vms["testm01"](带一个 CPU)

请注意,Terraform 正在使用地图中的键来跟踪此资源的每个单独实例。如果您想在不影响其他虚拟机的情况下添加新的虚拟机,您可以在 var.virtual_machines 的值中添加一个新条目,保留所有现有元素。每次您向该地图添加新元素并再次运行 terraform apply 时,Terraform 将计划创建一个新的虚拟机实例。同样,如果您删除或编辑该地图中的条目,则 Terraform 将计划销毁或更新/替换相应的虚拟机。


特别是对于环境的问题,而不是单个虚拟机,请注意,通常最好为每个环境创建单独的 Terraform 配置,这样您就可以单独更新每个环境,而不会冒险更新到一个人无意中改变了另一个人。

通常的方法是使用您已经编写为shared module 的配置,然后对于您要创建的每个新环境,您可以编写一个小的新配置,其中只包含对该模块的一次调用使用适用于该特定环境的设置:

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 在单独的state 快照中分别跟踪每个配置的对象,因此通过为每个配置目录分别创建一个单独的配置目录,您可以在每个环境中单独工作,而无需涉及其他环境的任何配置。但是,它们中的每一个都将共享相同的“环境”模块,因此它们都将根据相同的定义构建,并且您可以随时更新该共享的“环境”模块,以便进行更改以应用于所有你的环境。

【讨论】:

  • 感谢您的回复,但问题是 terraform 在检测到更改时正在删除资源。我想创建一个有 100 个虚拟机的环境,我提到的脚本就是用来实现的。例如,如果我想将其扩展到 102 个 vm 并重新运行脚本并进行更改,它将替换以前的实例,然后创建 2 个新实例。我不希望我的其他虚拟机被删除或替换。事实上,我不想使用 terraform 删除任何虚拟机,我们可以手动处理。我只想用它来不断扩展环境。
猜你喜欢
  • 2019-09-12
  • 1970-01-01
  • 2019-10-14
  • 1970-01-01
  • 2018-03-13
  • 2013-12-18
  • 1970-01-01
  • 1970-01-01
  • 2021-11-25
相关资源
最近更新 更多