Skip to content

1. 什么是预置器provisioner?

借助Terraform provisioner,我们可以使用单个命令在云环境中对基础设施配置进行编码并构建虚拟服务器 (和其他资源)。在构建的虚拟服务器上,设置各种中间件等准备应用程序执行环境(所谓的provisioning过 程)。

可以使用provisioner在远程或本地机器上指定特定的操作,以便为服务器或其他基础设施对象服务。 Terraform帮我们创建好instance以后,这个时候我们只是有一个空的instance,很多情况下,我们需要做一些初 始化的操作,例如安装必要的软件包,创建用户,修改配置文件等。

https://developer.hashicorp.com/terraform/language/resources/provisioners/syntax

1.2 provisioner的三种功能

rovisioner提供了三种功能:

Remote-exec Provisioner:在远端资源创建后调用该资源延的脚本。

File Provisioner:复制文件或目录到远程。

Local-exec Provisioner:在资源创建后调用本地可执行文件。

远程执行命令

命令说明

provisioner "remote-exec" {} 在服务器上远程执行命令

yaml
#创建ECS实例
resource "alicloud_instance" "instance" {
  # cn-shanghai
  #count = 2 # 创建2台ECS实例,默认不写count,则创建1台ECS实例
  availability_zone    = "cn-shanghai-b"
  security_groups      = ["${alicloud_security_group.nsg1.id}"]
  instance_type        = "ecs.e-c1m1.large" #若实例规格下线,请使用目前售卖中的实例规格
  system_disk_category = "cloud_essd"       #若磁盘规格下线,请使用目前售卖中的实例规格
  system_disk_size     = 40                 # 系统盘大小,单位为GB
  image_id             = local.image_id
  #instance_name              = "lyc-test-${count.index+1}"
  instance_name              = "lyc-test"
  vswitch_id                 = alicloud_vswitch.vsw_1.id
  internet_max_bandwidth_out = 1 # 出网带宽,单位为Mbps
  password                   = local.password

  # data_disks {
  #   name = "data_disk1"
  #   description = "data_disk1" # 数据盘描述
  #   size = 20 # 数据盘大小,单位为GB
  #   category = "cloud_essd" # 数据盘类型
  # }
  //provisioner
  provisioner "remote-exec" {
    inline = [
      "mkdir /root/test",
      "echo 'Hello, World!' > /root/test/hello.txt",
    ]
    connection {
      type     = "ssh"
      user     = "root"
      password = local.password
      host     = self.public_ip
    }
  }
}
#创建ECS实例
resource "alicloud_instance" "instance" {
  # cn-shanghai
  #count = 2 # 创建2台ECS实例,默认不写count,则创建1台ECS实例
  availability_zone    = "cn-shanghai-b"
  security_groups      = ["${alicloud_security_group.nsg1.id}"]
  instance_type        = "ecs.e-c1m1.large" #若实例规格下线,请使用目前售卖中的实例规格
  system_disk_category = "cloud_essd"       #若磁盘规格下线,请使用目前售卖中的实例规格
  system_disk_size     = 40                 # 系统盘大小,单位为GB
  image_id             = local.image_id
  #instance_name              = "lyc-test-${count.index+1}"
  instance_name              = "lyc-test"
  vswitch_id                 = alicloud_vswitch.vsw_1.id
  internet_max_bandwidth_out = 1 # 出网带宽,单位为Mbps
  password                   = local.password

  # data_disks {
  #   name = "data_disk1"
  #   description = "data_disk1" # 数据盘描述
  #   size = 20 # 数据盘大小,单位为GB
  #   category = "cloud_essd" # 数据盘类型
  # }
  //provisioner
  provisioner "remote-exec" {
    inline = [
      "mkdir /root/test",
      "echo 'Hello, World!' > /root/test/hello.txt",
    ]
    connection {
      type     = "ssh"
      user     = "root"
      password = local.password
      host     = self.public_ip
    }
  }
}

阿里上面获取元数据

bash
curl http://100.100.100.200/latest/meta-data/
curl http://100.100.100.200/latest/meta-data/

这里有个问题,就是如果命令执行失败,在执行会把原先的机器删除,在次创建新的实例来运行命令

复制本地文件到远程命令file

yaml
//复制本地文件到远程
  provisioner "file" {
    source      = "local_file.txt"
    destination = "/root/test/local_file.txt"
  }
  //连接远程,connection 也可以放到provisioner中
  connection {
    type     = "ssh"
    user     = "root"
    password = local.password
    host     = self.public_ip
  }
//复制本地文件到远程
  provisioner "file" {
    source      = "local_file.txt"
    destination = "/root/test/local_file.txt"
  }
  //连接远程,connection 也可以放到provisioner中
  connection {
    type     = "ssh"
    user     = "root"
    password = local.password
    host     = self.public_ip
  }

本地

执行命令local-exec

yaml
 //本地执行命令
  provisioner "local-exec" {
    command = "echo ${self.public_ip} > output.txt"
  }
}
 //本地执行命令
  provisioner "local-exec" {
    command = "echo ${self.public_ip} > output.txt"
  }
}

💡 说明

file provisioners、remote-exec provisioners需要connection 与服务器进行连接才可工作,local-exec不需要。

local-exec(本地执行)是被推荐的,他的优势是可以比较事物是否发生变化和达到期望状态的方式,以便我 们可以检测到变化(声明式模型就是这样维护的)。

rmote-exec(远程执行)是不被推荐的。官方不建议使用远程执行脚本,Terraform无法感知你执行了什么,不知道命令是否实际执行成功,状态是如何。因此如果是初始化操作,建议使用user_data用户数据执行,provisioners只在临时需要或必要时使用。

我们资源创建成功,但是provisioner没有执行成功,此时Terraform会认为我们的资源是“不安全”的,Terraform 会将这个资源标记为tainted,当我们下次执行apply的时候,Terraform不会重新尝试provisioner,它会直接将标记为tainted的资源删掉,然后重新创建,通过这样的方式来触发provisioner进行再次尝试。因此是十分危险和不推荐的。

povisioner只能作为最后手段使用。最佳实践是,使用配置管理工具Ansible。由管理配置工具接管服务器,并做所有事情,管理工具有更多的可见性,可控性,管理。