使用 Terraform.io 管理您的基础设施即代码






4.94/5 (7投票s)
如何使用 Terraform.io 在多个云平台上管理资源。一次编写,多处部署!
引言
当我开始利用云来托管应用程序和解决方案时,我总是每次只使用一个云提供商。我之前在的一些公司偏好使用 AWS,有些偏好 Azure,其他的要么是私有主机或 Google 等。我个人偏好 Azure,并且一般来说,我建议初次使用云时使用托管服务 - 对于现有的 .NET 开发人员来说,学习曲线较低,工具集成性强,非常强大,而且初始成本很低。
我以前从未遇到过(直到现在)需要设计一个能够同时在多个提供商上运行且所有组件都可互操作的解决方案 - 这是一个有趣的挑战。我的方法通常是尽可能使用“平台即服务”,但在当前项目中,事情却朝着不同的方向发展。我们目标是拥有单一代码库,并且碰巧有经验丰富的 DevOps 团队可用,因此“基础设施即服务”已成为我设计“一次编写,随处运行”系统的有趣选择。这个概念很简单……我们需要一个“启动模板”,可以针对 *任何* 主要云提供商执行,并让这个模板启动我们所需的网络/虚拟网络/计算机/容器集群等,然后根据需要自动扩展,所有这一切都使用单一代码库。
本文是该系列的第一篇,我将概述所使用的方法和技术 - 希望它能对他人有所帮助!

背景
平台即服务(PaaS)很棒 - 大部分情况下,它能完全满足您的需求。最大的好处是您只需开始编码,无需担心管理支撑一切的底层机器/网络/服务。总的来说,通过鼠标几次点击和一些设置,平台就会自动扩展并处理那些可能非常麻烦的细节。但有时,事情会超出常规,您需要亲自动手。我发现这种情况通常发生在所使用的技术(尚)不作为 PaaS 的元素可用时。
我目前的项目涉及处理大量数据,每天摄取 1.5TB 以上的新数据,并从中每天生成 3500 万条新的数据库记录。已经有一个非常有经验的运营团队到位,他们乐于管理所需领域的专业基础设施。然而,我希望确保尽可能地自动化,跨不同的云托管平台,并让那些聪明的运维人员处理真正的问题,而不是被日常琐事所困扰。
对于这个项目,我们自己管理整个基础设施。这意味着我们需要创建、管理、按需向上/向外扩展和向下缩减服务。这实际上并不像想象中那么令人生畏,有非常有用的技术可以提供帮助。自动化有三个方面,第一是虚拟机(称为节点)的管理,第二是控制各种服务运行其中的容器,第三是容器本身。
我将使用的主要技术是用于容器的 Docker,用于管理容器的 Kubernetes,以及用于管理虚拟机的 Terraform *(感谢爱尔兰微软的团队向我推荐了这个!)*。由于底层代码将编排一切,而且我希望它是真正的跨平台,因此我将为此使用 .NET Core。这篇初步的文章将讨论 Terraform,它在其中的作用以及如何在 Azure 上使用它。该系列后续的文章将探讨其他部分以及一切如何协同工作。
纯粹的云...
我们要做的第一件事是使用 Azure 门户创建一个虚拟机及其所有支持的*东西*……我们这样做只是为了看看有多容易,然后我们将退一步看看我们需要做什么来实现自动化。为此,我将设置一个小型 Linux 虚拟机,并一直采用默认设置,这里没什么特别的……
需要注意的是,Azure 上那个简单的“选择和点击”实际做了什么……当然,它创建了虚拟机,但它也部署了支持的资源组、存储区域、网络设置、子网、公共 IP 等。
在了解了使用标准工具的便捷性以及 Azure 如何为我们自动化繁重的工作之后,现在让我们来看看 Terraform 方法以及它如何帮助我们在不同云供应商之间复制这种易用性。
那么,为什么选择 Terraform?
当然,您可以使用 .NET 代码、Powershell 等来实现这种机器自动化,但对于这个用例来说,这不是重点。就我而言,*在这个项目中*,我需要在多个云主机(Azure、AWS、Alibaba、Google 和一些私有云)上部署相同的基础设施。有人可能会认为可以自己从头开始构建一些类,它们只抽象托管服务(如 Azure 上的 DocumentDB、AWS 上的 DynamoDB 等)的基本需求,但是,这会偏离项目的核心要求,即在 DevOps 人员可以管理的每个云主机上使用相同的基本基础设施和技术。
Terraform 提供的优势很简单——它充当中介,协调多个云主机上虚拟机和其他虚拟资源的管理,从而使开发人员不必在尝试管理云基础设施时管理不同且不断变化的 API。Terraform 是一个开源库(用 GO 语言编写),我们通过命令行界面(CLI)和配置文件与之交互。配置文件采用简单的 JSON 格式,并且“编码”在 JSON 有效负载中的指令可以针对每个主机提供商。Terraform 会根据需要将有效负载中的指令翻译成管理虚拟资源的指令,使用每个云主机的特定 API。Terraform 基本上已经为我们完成了繁重的工作,它是开源的,并且维护良好,所以我真的不需要担心它,并且在这种情况下,如果可以避免,我不想重新发明轮子。
入门
为了开始,我们首先将远程设置和创建一个基础机器。这将演示基本功能。在下一篇文章中,我们将对此进行扩展,并展示如何使用 Docker 中的 .NET Core 来帮助进一步自动化和编排。
您需要下载两样东西才能开始。
- Terraform CLI - 您可以从 Terraform.io 网站下载
- 一个 Powershell 脚本,用于启动与 Azure 的身份验证。我已将此脚本的副本(以及示例脚本文件)附加到文章中供下载,您也可以从原始的 github 仓库 获取*(非常感谢 Eugene Chuvyrov 编写脚本!)*。
Terraform CLI 是一个独立的控制台应用程序,您可以从命令行运行它。它接受输入参数来确定要执行的操作。我建议下载文件,创建一个文件夹并将其解压缩到其中。在我这里,我有一个通用的“Data”文件夹,我在其中创建了一个“Terraform”文件夹并解压到那里。我还将您可以在本文顶部下载的 Powershell 脚本放在了同一个文件夹中。
Azure 身份验证
我们需要做的第一件事是在 Azure 中创建一些身份验证。实际上,这不是为我们自己创建,而是为一种特定的内部 API/应用程序用途,称为“服务主体”。简而言之,这样做可以确保您不会使用您的主要凭据来执行命令,万一失去控制,也只会影响该服务主体。所以它有点像一个非常受限的账户(*此处有更多关于服务主体的信息*)。
为了开始,我们需要登录以创建这些服务主体凭据,为此,我们将使用本文附加的 Powershell 脚本。
注意脚本后面的 'setup
' 参数。运行此命令后,系统会要求您接受或拒绝数据收集,然后登录到您的 Azure 帐户。
登录后,系统会提示您输入应用程序的名称和密码。完成后,您将收到一个 JSON 响应,其中包含 Terraform 设置配置所需的设置。
Terraform Azure 配置
创建服务主体并从脚本中获取所需的设置后,我们就可以开始构建 Terraform 将用于创建资源的配置文件了。如果我们回顾一下之前通过 Azure 门户点击/构建自动创建的内容,我们可以大致了解需要在配置中放入什么。
Terraform 配置文件包含多个部分。在顶部,我们声明“Provider
”。在这种情况下,它是云主机。在这里,我们提供了运行上述 Powershell 设置脚本时从 JSON 收到的详细信息……
Terraform 配置文件具有“*.tf”扩展名。我创建的第一个文件就叫“sample.tf”。它是一个简单的文本文件,格式非常基础。
提供商详细信息
在文件顶部,我提供了提供商的详细信息以及 JSON 中的详细信息
# Core init
provider "azurerm" {
client_id = "your client id in here"
client_secret = "your secret in here"
subscription_id = "your subscription value in here"
tenant_id = "your tenant id in here"
}
(显然,您需要在指示的地方输入自己的值!)
注意提供商名称“azurerm
”。其他提供商的格式不同。您可以在 Terraform 文档网站上的提供商文档页面上找到完整的详细信息。
从上到下,我们开始构建我们希望 Terraform 创建的一系列细节。如果您已经拥有同名/同类型的资源,它们将被跳过,否则将被创建。您也可以强制覆盖/重新构建虚拟资源(有关详细信息,请查看文档)。
资源组
所有 Azure 事物的顶级占位符是“资源组”。
# Setup resource group
resource "azurerm_resource_group" "Alias_RG" {
name = "Terraform-resource-group"
location = "North Europe"
}
让我们谈谈一些设置。在第一行,我们有一个注释,用“#
”表示。接下来,我们有第一个标记,它告诉 Terraform 接下来的类型是什么,在这种情况下是“resource
”。由于上一节已告知 Terraform 接下来的内容是 Azure 配置,因此它会理解该资源与此相关,并且声明的类型是“azurerm_resource_group
”。紧随其后,我们可以给该项一个别名,在这种情况下,我将其命名为“Alias_RG
”(表示资源组)。“name
”为要创建的资源提供了一个标题,“location
”指的是云提供商上应创建资源的区域。我几乎可以肯定所有的云提供商都有位置区域的概念。
网络设置
接下来,我们有一组设置,用于创建我们要构建的虚拟机所需的网络资源。与之前一样,如果您愿意,可以使用预先存在的资源,我在这里只是从头开始创建一切,以展示详细的构建过程。大多数单独的值都是不言自明的。
# Create virtual network within the group
resource "azurerm_virtual_network" "Alias_VN" {
name = "Terraform-virtual-network"
resource_group_name = "${azurerm_resource_group.Alias_RG.name}"
address_space = [
"10.0.0.0/16"]
location = "North Europe"
}
上面需要注意的一个关键项是,我们如何使用模板来引用我们为资源组创建的别名。我已在此处用粗体突出显示。
每个别名都以应引用的类型为前缀
# Create subnet
resource "azurerm_subnet" "Alias_SubNet" {
name = "subn"
resource_group_name = "${azurerm_resource_group.Alias_RG.name}"
virtual_network_name = "${azurerm_virtual_network.Alias_VN.name}"
address_prefix = "10.0.2.0/24"
}
在别名之后,是应引用的对象成员/属性
# create public IP
resource "azurerm_public_ip" "Alias_PubIP" {
name = "TestPublicIP"
location = "North Europe"
resource_group_name = "${azurerm_resource_group.Alias_RG.name}"
public_ip_address_allocation = "dynamic"
tags {
environment = "TerraformDemo"
}
}
我们还可以在脚本中使用变量(与声明别名相同)。这在我们要声明一些顶级命名约定并让其级联到我们的模板时非常有用。
# create network interface
resource "azurerm_network_interface" "Alias_NIC" {
name = "tfnetinterface"
location = "North Europe"
resource_group_name = "${azurerm_resource_group.Alias_RG.name}"
ip_configuration {
name = "testconfiguration1"
subnet_id = "${azurerm_subnet.Alias_SubNet.id}"
private_ip_address_allocation = "static"
private_ip_address = "10.0.2.5"
public_ip_address_id = "${azurerm_public_ip.Alias_PubIP.id}"
}
}
存储帐户设置
在网络设置之后,我们然后设置用于存储数据和虚拟机本身的配置。
# create storage account
resource "azurerm_storage_account" "Alias_StorageAccount" {
name = "tfstorage1234xx"
resource_group_name = "${azurerm_resource_group.Alias_RG.name}"
location = "North Europe"
account_type = "Standard_LRS"
tags {
environment = "staging"
}
}
我们始终使用相同的别名命名约定。另外,值得注意的是,我们可以引入依赖关系的概念……
# create storage container
resource "azurerm_storage_container" "Alias_Storage" {
name = "vhd"
resource_group_name = "${azurerm_resource_group.Alias_RG.name}"
storage_account_name = "${azurerm_storage_account.Alias_StorageAccount.name}"
container_access_type = "private"
depends_on = ["azurerm_storage_account.Alias_StorageAccount"]
}
虚拟机设置
最后,我们开始设置虚拟机本身的配置。与之前一样,在这里我们将此新项链接到资源组、网络、存储帐户等的其他别名。在操作系统配置文件中,我们还提供了机器应设置的初始用户名/密码凭据。稍后我们可以使用这些来测试一切是否正常。还要注意我发送的“存储映像引用”,在这里我们可以非常具体地满足我们的需求,并使用我们自己的映像以及云主机预定义的映像。
# create virtual machine
resource "azurerm_virtual_machine" "Alias_UBuntuVM" {
name = "terraformvm"
location = "North Europe"
resource_group_name = "${azurerm_resource_group.Alias_RG.name}"
network_interface_ids = ["${azurerm_network_interface.Alias_NIC.id}"]
vm_size = "Standard_A0"
storage_image_reference {
publisher = "Canonical"
offer = "UbuntuServer"
sku = "14.04.2-LTS"
version = "latest"
}
storage_os_disk {
name = "myosdisk"
vhd_uri =
"${azurerm_storage_account.Alias_StorageAccount.
primary_blob_endpoint}$
{azurerm_storage_container.Alias_Storage.name}/somevmdisk.vhd"
caching = "ReadWrite"
create_option = "FromImage"
}
os_profile {
computer_name = "hostname"
admin_username = "testadmin"
admin_password = "Password1234!"
}
os_profile_linux_config {
disable_password_authentication = false
}
tags {
environment = "staging"
}
}
Terraform 执行
我们的配置已完成,下一步是让 Terraform 设置一切,并在远程应用配置。这是一个两步过程,首先我们“计划”作业,然后我们“应用”它。
在命令行中,输入 terraform.exe sample.tf
(或您为配置文件命名的任何名称)。
片刻之后,Terraform 将预先计划好作业,输出应类似于以下内容:
最后的主要步骤(祈祷顺利!)是“应用”该计划到云主机。“apply”操作会处理计划好的作业,连接到指定的云主机,并使用配置计划来执行和创建/修改所需的资源。完成后,输出应类似于以下内容:
那么,它奏效了吗?……让我们看看 Azure 门户……
看起来不错!……好了,这就是 Terraform 的基本知识。本系列后续文章将探讨如何使用 .NET Core API 进行自动化和利用 Terraform,然后进一步使用 Docker 和 Kubernetes 扩展编排。一如既往,如果您喜欢这篇文章,请在顶部给它投票!
历史
- 2017年5月29日 - 版本 1