65.9K
CodeProject 正在变化。 阅读更多。
Home

使用 Terraform 在 Azure 上远程设置/安装虚拟机

starIconstarIconstarIconstarIconstarIcon

5.00/5 (3投票s)

2017年8月31日

CPOL

8分钟阅读

viewsIcon

16289

downloadIcon

110

使用 Terraform 在 Azure 上进行预配

引言

微软最近宣布加大了对 Terraform 与 Azure 集成的投入(2017 年 8 月)。这是微软向异构/多云领域扩张的延续,他们正竭尽全力帮助开发者在云上取得成功。过去,Azure 只适用于微软开发者——现在则不然。雷德蒙德团队以及全球各地的办公室都在大力推广开源和“面向所有人的 Azure”的机会。从长远来看,开放性对每个人都有好处。您可以在官方 MS Terraform 页面上阅读有关 Terraform 与 Azure 集成的更多详细信息。

总之,回到当前的任务——推进这个以 DevOps/基础设施为中心的系列。在本系列的第一个文章中,我介绍了“Terraform”,并对其进行了介绍,说明了它是什么以及如何使用它。第二篇文章讨论了如何使用变量、插值和资源计数来创建资源的多个副本,以避免代码/配置重复。本文假定您已阅读了前两篇文章,并提供了关于在虚拟机部署后,使用您的特定设置来设置或“配置”远程虚拟机的说明。

背景

当您在 Azure、AWS 或其他地方创建虚拟机实例时,您无法确定它是否完全是最新的并已打补丁等。即使它已经更新,您可能仍需要配置(“供应”)一套非常符合您需求并支持您要构建的基础设施的解决方案。本文向您展示了一些使用 Terraform 在 Azure 上配置虚拟机的不同选项。

Provisioners (配置器)

当我们创建虚拟机时,我们需要在它们上面运行一些东西——这可能用于设置、更新、安全加固等。“Provisioners”(配置器)是我们用来声明我们希望针对特定资源进行配置的内容。这可能包括上传文件、安装软件或运行一些自定义脚本。

代码放置

您可能还记得本系列早期文章中,我们将基础设施定义为“.TF”配置文件中的基本构建块。构建块代表我们基础设施的不同部分,例如虚拟网络、公共 IP 地址、虚拟网络接口卡、磁盘等。配置器通常放置在它们所针对的资源块内部。所以,在这种情况下,我们将在我们的虚拟机创建块内创建一个配置器块

# outer virtual machine resource definition
resource "azurerm_virtual_machine" "workerNode" {
	name = "workerNode"
    location = "North Europe"
    resource_group_name = "${azurerm_resource_group.Alias_RG.name}"  
	network_interface_ids = [network_interface_ids = ???]
    vm_size = "Standard_A0"
    ... etc
# inner definition of a provisioner
    provisioner XXX TYPE XXX {
       XXX specific settings XXX
}

生命周期事件

Terraform 提供了一些特定的模板,我们可以在配置时使用,这些模板将在配置生命周期的特定时间触发——包括创建、销毁和失败时配置。默认情况下,配置设置为假定创建事件,但是,您可以标记配置器,使其仅在附加到的资源被销毁时发生。以下是显示默认、带创建事件的默认(相同)和销毁事件的三个示例。请注意,需要使用“when”关键字来告知 Terraform 何时在此块中执行配置。

provisioner XXX TYPE XXX {
    XXX specific settings XXX
}
provisioner XXX TYPE XXX {
   when = "create"}

provisioner XXX TYPE XXX {
   when = "destroy"}

现在,有一个注意事项——在撰写本文时(2017 年 8 月),此特定设置似乎存在一些不稳定性——所以如果它对您来说不稳定,请在 Github 上提交一个工单!

因此——我们知道基本结构是什么样的,但我们如何才能实际配置东西到我们的远程服务器上呢?……要做到这一点,我们可以使用三种类型的配置代码块之一(这会是我上面提到的“XXX type XXX”占位符!)

文件配置

文件上传是使用FILE 配置设置完成的。我们可以使用 FILE 上传单个文件和整个文件夹的内容。Terraform 根据您提供给“source”(源)的内容来判断它是一个文件还是文件夹,然后将该文件或文件夹复制到远程系统的“destination”(目标)位置。唯一需要小心的是,您要上传到一个存在且您具有写入权限的位置。

 # Copies all files and folders in apps/app1 to D:/IIS/webapp1
   provisioner "file" {
   source "apps/app1/"
   destination = "D:/IIS/webapp1"
}

# Copies a single file "MyTextFile.txt" to the d:\data folder on remote machine
   provisioner "file" {
   source "c:\data\MyTextFile.txt"
   destination = "d:\data\files\MyTextFile.txt"
}

您还可以让 Terraform 创建一个文件,然后传递该文件的内容——这在您创建动态文件内容时非常有用。这里的诀窍是我们不使用“source”属性,而是使用“content”属性。

#  Copies the string in "content"into c:\data\someNotes.txt
   provisioner "file" {
   content "my notes: ${var.Notes}"
   destination = "d:\data\someNotes.txt"
}

EXEC 配置

除了将文件和内容上传到我们新建的远程机器之外,我们很可能还需要运行脚本和其他形式的设置。这可以通过使用 EXEC 命令来实现。其中有两个,一个用于远程主机,一个用于执行 Terraform 正在运行的本地主机。正如您可能预期的那样,它们是 remote-execlocal-exec

remote-exec 配置器的类型采用以下格式

provisioner "remote-exec" {
    inline = [   # argument declaration
      "RunApp.exe someParam"   # 1 or more elements to execute inline
    ]
  }

Remote-exec 有三种不同的参数类型

  1. inline - 这是一个命令字符串列表。它们按提供的顺序执行。此选项不能与 script 或 scripts 合并。
  2. script - 这是指向本地脚本的路径(相对或绝对),该脚本将被复制到远程资源然后执行。此选项不能与 inline 或 scripts 合并。
  3. scripts - 这是指向本地脚本的路径列表(相对或绝对),这些脚本将被复制到远程资源然后执行。它们按提供的顺序执行。此选项不能与 inlinescript 合并。

Local-exec 的构造如下

provisioner "local-exec" {
    command = [
      "RunApp.exe someParam"
    ]
  }

因此,当 remote-exec 有一个“inline”参数元素时,local-exec 使用“command”。给定的命令可以作为当前工作目录的相对路径或绝对路径提供。它在一个 shell 中进行评估,并且可以使用环境变量或 Terraform 变量。

连接

当我们运行配置器时,它是在创建或销毁资源的上下文中进行的。默认情况下,配置器应该使用它们已经建立的与机器的连接,然而,有时您需要帮助。要与资源关联,需要在其上下文中声明连接——以下示例演示了这一点

	provisioner "remote-exec" {
    inline = [
	 "sudo mkdir /tmp/staging"
	 ]
	  connection {
         type = "ssh"
	     user = "testadmin"
	     host = "<<some public IP or server host name>>"
	     private_key = "${file("id_rsa")}"
	  }	 
	}

您会注意到,在连接中,我定义了用户和私钥,但没有密码。这是我的选择——如果您愿意,您可以配置远程主机使用密码。我拒绝了这一点,并更倾向于使用密钥安全连接。所示的特定插值解析以加载我放在与 terraform.exe 相同文件夹中的名为 'id_rsa' 的本地文件。

超时

有时,在使用 Terraform 时,您会发现脚本因超时而失败。一个常见问题是云提供商(AWS、Google、Azure)花费了很长时间来启动您请求的资源,或者对脚本响应缓慢。为了帮助解决这个问题,我们可以定义和延长 exec 配置器的“timeout”周期——我们只需要提供延迟多长时间(以秒为单位),然后放弃并重试……。

	provisioner "remote-exec" {
    inline = [
	 "sudo mkdir /tmp/staging"
	 ]
	  connection {
         type = "ssh"
	     user = "testadmin"
	     host = "<<some public IP or server host name>>"
	     private_key = "${file("id_rsa")}"
	     timeout = "300s"
	  }	 
	}

Null Resources (空资源)

值得一提的是一个叫做“null resource”的有用东西。这是一种资源类型,您可以用来包装更通用的配置器,您希望在整个基础设施上执行,而不仅仅是特定机器。这里有一个示例,当集群的任何实例发生变化时,它会在您正在运行 Terraform 的本地机器上回显一条消息。

resource "null_resource" "cluster" {
  # Changes to any instance of the cluster requires re-provisioning
  triggers {
    cluster_instance_ids = "${join(",", aws_instance.cluster.*.id)}"
  }

trigger”(触发器)是启动依赖图更改的事件。

当事情出错时……

尽管我们尽了最大努力,但有时事情就是不如我们所愿。从 Terraform 的角度来看,这可能意味着云提供商未能完成一个请求,某个请求因某种原因失败等。我们优雅地处理这些问题的方式是使用“on_failure”(失败时)标签。在下面的示例中,假设我们在命名变量时犯了一个错字,将“NotesN”写成了“Notes”。我们没有一个名为“NotesN”的变量,即使有,它指向本地文件,假设它不存在,此配置器也会失败。在这种情况下,Terraform 的默认行为是“Taint”(污点)正在创建的资源(即将其标记为未完成/需要重建),并报告错误。如果您将“on_failure”设置为“fail”,则会发生污点(这是默认设置);如果另一方面,这不成问题,您可以将“on_failure”设置为“continue”,配置将继续进行。

#  Copies the string in "content"into c:\data\someNotes.txt
   provisioner "file" {
   content "my notes: ${var.NotesN}"
   destination = "d:\data\someNotes.txt"
   on_failure="continue" # or on_failure="fail"
   ...
}

总结

好的,以上是配置器的基础知识。如果您需要更详细的信息和更多选项,您可以在Terraform 配置器文档页面上阅读所有内容。

我附上了一个示例脚本,这样您就无需手动输入即可进行测试——只需记住填写您自己的详细信息!……祝您编码愉快,如果文章有用,别忘了投票!

在下一篇文章中,我们将探讨下一步,使用 Terraform 在我们创建的虚拟机网络中设置 Kubernetes/Docker 集群。

最后,如果您想获得更广泛的视角,这个关于 Terraform 在 Azure 上的视频值得一看。

历史

  • 2017 年 8 月 31 日:版本 1
© . All rights reserved.