Terraform在您重新运行以创建新实例时会删除上一个实例



我是terraform的新手,使用的是版本v0.12.20。我注意到它正在删除现有的资源,只要我重新运行它来创建一个新的资源。例如,如果我在某个env中创建了10个vm,并想创建5个新的vm,那么它应该简单地创建检测更改的新vm,而不是删除以前的vm并创建新的vm。

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 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并不是为这种只想创建新事物,然后让它们在其他地方管理的用例而设计的。相反,Terraform的模型是对长寿命对象的管理,随着时间的推移,您可能需要对这些对象进行更改,这些对象仍处于Terraform中。

在内部,Terraform将配置中声明的每个资源实例与供应商的远程API中的远程对象相关联。在您的情况下,每个vsphere_virtual_machine.test_vm实例(Terraform根据您的count值调用vsphere_virtual_machine.small_vm[0]vsphere_virtual_machine.small_vm[1]等(都与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配置,这样您就可以分别更新每个环境,而不会冒着对一个环境进行意外更改的风险。

常见的方法是使用您已经编写的配置作为共享模块,然后对于您想要创建的每个新环境,您可以编写一个小的新配置,该配置只包含对该模块的单个调用,并具有适用于该特定环境的设置:

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在一个单独的状态快照中分别跟踪每个配置的对象,因此通过为每个配置都有一个独立的配置目录,您可以在不涉及其他配置的情况下单独处理每个环境。然而,它们中的每一个都将共享相同的"环境"模块,因此它们都将根据相同的定义构建,并且您可以在任何时候更新共享的"环境"模块,以进行应适用于所有环境的更改。

相关内容

最新更新