赞
踩
Terraform 是一种部署技术,任何想要通过基础设施即代码(Infrastructure as Code,IaC)方法来置备和管理基础设施的人,都可以使用这种技术。基础设施指的主要是基于云的基础设施,不过从技术上讲,任何能够通过应用程序编程接口(Application Programming Interface,API)进行控制的东西都可以算作基础设施。基础设施即代码是通过机器可读的定义文件来管理和置备基础设施的过程的。我们使用IaC来自动完成原本要由人手动完成的过程。所谓置备,指的是基础设施部署,而不是配置管理,后者主要处理应用程序交付,特别是在虚拟机(Virtual Machine,VM)上交付。Ansible、Puppet、SaltStack 和 Chef 等配置管理(Configuration Management,CM)工具已经存在多年,非常流行。Terraform 并没有取代这些工具,至少不会完全取代,因为基础设施置备和配置管理在本质上是不同的问题。即使如此,Terraform 也会提供原本只有 CM 工具会提供的一些功能,许多公司在采用了 Terraform 之后,发现自己并不需要 CM 工具。HashiCorp Terraform 是一种基础架构即代码工具,可让您在可读的配置文件中定义云和本地资源,您可以对其进行版本控制、重用和共享。然后,您可以使用一致的工作流程在整个生命周期内配置和管理所有基础架构。Terraform 可以管理计算、存储和网络资源等低级组件,以及 DNS 条目和 SaaS 功能等高级组件。Terraform 的基本原则是,它允许编写人类可读的配置代码来定义 IaC。借助配置代码,你可以把可重复的、短暂的、一致的环境部署到公有云、私有云和混合云上的供应商,即 我们借助 Terraform 可以把基础设施部署到任何云或者混合云中如下图所示:
- 基础设施即代码:使用高级配置语法描述基础设施。这允许您对数据中心的蓝图进行版本控制和处理,就像您处理任何其他代码一样。此外,基础设施可以共享和重用。
- 执行计划:Terraform 有一个“计划”步骤,它会生成一个执行计划。执行计划显示了当您调用 apply 时 Terraform 将执行的操作。这可以让您在 Terraform 操作基础设施时避免任何意外。
- 资源图:Terraform 构建所有资源的图,并并行创建和修改任何非依赖资源。因此,Terraform 尽可能高效地构建基础设施,并且运营商可以深入了解其基础设施中的依赖关系。
- 变更自动化:可以将复杂的变更集应用到您的基础设施,只需最少的人工交互。通过前面提到的执行计划和资源图,您可以准确地知道 Terraform 将改变什么以及以什么顺序进行更改,从而避免了许多可能的人为错误。
工作流
近来有大量关于 Terraform 的宣传,但这种宣传有理有据吗?Terraform 并不是唯一的 IaC 技术,还有其他许多工具也能完成同样的工作。软件部署是利润颇丰的市场领域,Terraform 为什么能够在这个领域与 Amazon、Microsoft 和 Google 等公司的技术竞争呢?有6个关键特征让 Terraform 与众不同,给它带来了竞争优势。
- 置备工具:部署基础设施,而不只是应用程序。
- 易于使用:适合我们这些不是天才的人使用。
- 免费且开源:谁不喜欢免费的东西呢?
- 声明式:说出你想要的结果,而不是说出如何实现这个结果。
- 云无关:使用相同的工具部署到任意云。
- 表达能力强且可扩展:不受语言的限制。
下面我们列出 Terraform 与其他 IaC 工具的对比
名称 | 关键特征 | |||||
置备工具 | 易于使用 | 免费、开源 | 声明式 | 云无关 | 表达能力强、可扩展 | |
Ansible | √ | × | × | √ | × | × |
Chef | √ | √ | × | × | × | × |
Puppet | √ | √ | × | × | × | × |
SaltStack | √ | × | × | × | × | × |
Terraform | × | × | × | × | × | × |
Pulumi | × | √ | × | √ | × | × |
AWS CloudFormation | × | × | √ | × | √ | √ |
GCP Deployment Manager | × | × | √ | × | √ | √ |
Azure Resource Manager | × | √ | √ | √ | √ | √ |
从技术上讲,Pulumi 最接近 Terraform,唯一的区别在于它不是声明式的。Pulumi 团队认为这是 Pulumi 相较于 Terraform 的优势,但 Terraform 也有一个云开发工具包(Cloud Development Kit,CDK),允许实现相同的功能。Terraform 的设计受到了 AWS CloudFormation 的启发,并且与 GCP Deployment Manager 和 Azure Resource Manager 有很相近的地方。那些技术虽然也不错,但都不是与具体云无关的技术,也都不是开源的。它们只能用于特定的云供应商,并且一般不如 Terraform 简洁和灵活。Ansible、Chef、Puppet 和 SaltStack 都是配置管理工具,而不是基础设施置备工具。它们解决的问题类别与 Terraform 有些区别,不过也存在重叠的地方。
Terraform 是一种基础设施置备工具,而不是配置管理工具。置备工具部署和管理基础设施,而配置管理工具(如 Ansible、Puppet、SaltStack 和 Chef)将软件部署到现有服务器上。一些配置管理工具也能够执行一定程度的基础设施置备,但不如 Terraform,因为它们并不是为这类任务设计的。配置管理工具和置备工具之间的区别主要在于理念。配置管理工具常用于管理可变基础设施,而 Terraform 和其他置备工具常用于管理不可变基础设施。可变基础设施意味着在现有服务器上执行软件更新。不可变基础设施则不关心现有服务器,它把基础设施视为用后即可丢弃的商品。这两种范式之间的区别可归结为复用思想与用后丢弃思想的区别。
即使是非程序员,也可以快速、轻松地学会 Terraform 的基础知识。大部分开发者很容易上手并且经过一些简单联系就将具备中级 Terraform 用户必备的技能。细想一下,这简直让人难以置信。当然,要精通 Terraform 就是另外一回事了,不过对于大部分技能都是如此。Terraform 之所以如此易用,主要原因在于其代码是用一种称作 HashiCorp Configuration Language(HCL)的领域特定的配置语言编写的。HashiCorp 开发了这种语言,用来替代更加冗长的 JSON 和 XML 等配置语言。HCL 试图在人类可读性和机器可读性之间达到一种平衡,并受到了这个领域中一些早期尝试(如 libucl 和 Nginx 配置)的影响。HCL 与 JSON 完全兼容,这意味着HCL能够完全转换为 JSON,反之亦然。这就使得与 Terraform 之外的系统进行互操作或者动态生成配置代码变得十分简单。
Terraform 的引擎称作 Terraform core,这是一款免费且开源的软件,通过 Mozilla Public License v2.0 提供。该许可规定,任何人都可以出于个人目的和商业目的使用、分发或修改软件。免费这一点很好,因为这意味着你在使用 Terraform 时不必担心会承担额外的费用。另外,它使得产品及其工作方式对用户来说变得透明。Terraform 没有提供高级版本,但提供了商业解决方案和企业解决方案(Terraform Cloudv和 Terraform Enterprise),可成规模运行 Terraform。
声明式编程指的是表达计算逻辑(做什么),但不描述控制流(怎么做)。你不必编写一步步执行的指令,只要描述自己想要的结果即可。数据库查询语言(SQL)、函数式编程语言(Haskell、Clojure)、配置语言(XML、JSON)和大部分 IaC 工具(Ansible、Chef、Puppet)都是声明式编程语言的示例。声明式编程语言是与命令式(或过程式)编程相对的。命令式语言使用条件分支、循环和表达式来控制系统流程、保存状态和执行命令。几乎所有传统编程语言(如 Python、Java、C 等)都是命令式编程语言。注意 声明式编程关注的是结果,而不是过程。命令式编程关注的是过程,而不是结果。
云平台无关指的是能够使用一组相同的工具和工作流,无缝运行在任意云平台上。Terraform 是云无关的,使用 Terraform 把基础设施部署到 AWS 与部署到 GCP、Azure 甚至私有数据中心一样简单,如下图所示。云平台性无关很重要,因为这意味着你不会被局限于特定的云供应商,也不需要在每次改变云供应商时学习一种全新的技术。
Terraform 通过提供程序(provider)与不同的云集成。提供程序是 Terraform 插件,用于与外部 API 进行交互。每个云供应商都会维护自己的 Terraform 提供程序,使 Terraform 能够管理该云中的资源。提供程序是使用 Go 语言编写的,并作为二进制文件分发到 Terraform 注册表上。它们负责进行身份验证、发出 API 请求以及处理超时和错误。在这个注册表中,有数百个已经发布的提供程序,它们协同起来,使你能够管理数千种不同的资源。
与其他声明式 IaC 工具相比,Terraform 的表达能力强,且高度可扩展。通过使用条件语句、for表达式、指令、模板文件、动态块、变量和许多内置函数,我们可以轻松地编写代码来实现自己的目的。下面的表格中我们从技术的角度对比了 Terraform 和 AWS CloudFormation(催生 Terraform 的技术)。
名称 | 语言特性 | 其他特性 | |||||
本身提供的函数 | 条件语句 | for循环 | 类型 | 支持插件 | 模块化 | 等待条件 | |
Terraform | 115个 | 是 | 是 | 字符串、数字、列表、映射、布尔值、对象、复杂类型 | 是 | 是 | 否 |
AWS | 11个 | 是 | 否 | 字符串、数字、列表 | 有限程度 | 是 | 是 |
这是 Terraform 配置语言的文档。它与Terraform CLI、 Terraform Cloud和 Terraform Enterprise的用户相关。Terraform 的语言是它的主要用户界面。您用 Terraform 语言编写的配置文件告诉 Terraform 要安装哪些插件、要创建哪些基础设施以及要获取哪些数据。Terraform 语言还允许您定义资源之间的依赖关系,并从单个配置块创建多个类似的资源。Terraform 语言的语法仅包含几个基本元素:
- 资源"aws_vpc" "main" {
- cidr_block = var.base_cidr_block }
- <BLOCK TYPE> "<BLOCK LABEL>" "<BLOCK LABEL>" {
- # Block body <IDENTIFIER> = <EXPRESSION> # Argument }
以下示例描述了 Amazon Web Services 的简单网络拓扑,只是为了让您了解 Terraform 语言的整体结构和语法。可以使用其他提供商定义的资源类型为其他虚拟网络服务创建类似的配置,并且实际的网络配置通常会包含此处未显示的其他元素。
- terraform {
- required_providers {
- aws = {
- source = "hashicorp/aws"
- version = "~> 1.0.4"
- }
- }
- }
-
- variable "aws_region" {}
-
- variable "base_cidr_block" {
- description = "A /16 CIDR range definition, such as 10.1.0.0/16, that the VPC will use"
- default = "10.1.0.0/16"
- }
-
- variable "availability_zones" {
- description = "A list of availability zones in which to create subnets"
- type = list(string)
- }
-
- provider "aws" {
- region = var.aws_region
- }
-
- resource "aws_vpc" "main" {
- # Referencing the base_cidr_block variable allows the network address
- # to be changed without modifying the configuration.
- cidr_block = var.base_cidr_block
- }
-
- resource "aws_subnet" "az" {
- # Create one subnet for each given availability zone.
- count = length(var.availability_zones)
-
- # For each subnet, use one of the specified availability zones.
- availability_zone = var.availability_zones[count.index]
-
- # By referencing the aws_vpc.main object, Terraform knows that the subnet
- # must be created only after the VPC is created.
- vpc_id = aws_vpc.main.id
-
- # Built-in functions and operators can be used for simple transformations of
- # values, such as computing a subnet address. Here we create a /20 prefix for
- # each subnet, using consecutive addresses for each availability zone,
- # such as 10.1.16.0/20 .
- cidr_block = cidrsubnet(aws_vpc.main.cidr_block, 4, count.index+1)
- }
Terraform Cloud 和 Terraform Enterprise 安装提供程序作为每次运行的一部分。Terraform CLI 在 初始化工作目录时查找并安装提供程序。它可以从 Terraform 注册表自动下载提供程序,或从本地镜像或缓存加载它们。如果您使用的是持久工作目录,则无论何时更改配置的提供者都必须重新初始化。为了节省时间和带宽,Terraform CLI 支持可选的插件缓存。您可以使用CLI 配置文件plugin_cache_dir中的设置 启用缓存。
Terraform 工作目录通常包含:
- 描述 Terraform 应管理的资源的 Terraform 配置。这种配置预计会随着时间而改变。
- 一个隐藏.terraform目录,Terraform 使用它来管理缓存的提供程序插件和模块,记录 当前处于活动状态的工作区,并记录最后一次已知的后端配置,以防下次运行时需要迁移状态。此目录由 Terraform 自动管理,并在初始化期间创建。
- 状态数据,如果配置使用默认local后端。这是由 Terraform 在terraform.tfstate文件(如果目录仅使用默认工作区)或terraform.tfstate.d目录(如果目录使用多个工作区)中管理的。
- 运行terraform init命令以初始化包含 Terraform 配置的工作目录。初始化后,您将能够执行其他命令,例如terraform plan和terraform apply。
- 如果您尝试运行依赖于初始化而不首先初始化的命令,该命令将失败并显示错误并说明您需要运行 init。
- 初始化执行多个任务来准备目录,包括访问已配置后端中的状态、下载和安装提供程序插件以及下载模块。在某些情况下(通常是从一个后端更改为另一个后端时),它可能会要求用户提供指导或确认。
- Terraform 配置的某些类型的更改可能需要重新初始化,然后才能继续正常操作。这包括对提供者要求、模块源或版本约束以及后端配置的更改。
- 您可以通过再次运行来重新初始化目录terraform init。事实上,您可以随时重新初始化;init 命令是幂等的,如果不需要更改,它将不起作用。
- 如果需要重新初始化,任何依赖于初始化的命令都将失败并显示错误并告诉您。
Terraform 注册表是一个交互式资源,用于发现与 Terraform 一起使用的各种集成(提供程序)和配置包(模块)。Registry 包括由 HashiCorp、第三方供应商和我们的 Terraform 社区开发的解决方案。我们对 Registry 的目标是提供插件来管理任何基础设施 API、预制模块以快速配置通用基础设施组件,以及如何编写优质 Terraform 代码的示例。
注册表为模块和提供程序提供了许多不同的类别,以帮助导航大量可用选项。选择提供商或模块卡以了解更多信息,将结果过滤到特定层,或使用注册表顶部的搜索字段来查找您要查找的内容。
任何有兴趣发布提供程序或模块的人都可以创建一个帐户并使用 GitHub 帐户登录到 Terraform Registry。单击“登录”按钮,然后按照登录提示进行操作。授权使用 GitHub 帐户并登录后,您可以直接从您管理的存储库之一发布提供程序和模块。
Terraform 云开发工具包 (CDKTF) 允许您使用熟悉的编程语言来定义和配置基础设施。这使您无需学习 HashiCorp 配置语言 (HCL) 即可访问整个 Terraform 生态系统,并允许您利用现有工具链的强大功能进行测试、依赖管理等。我们目前支持 TypeScript、Python、Java、C# 和 Go(实验性)。
适用于 Terraform 的 CDK 利用AWS 云开发工具包中的概念和库将您的代码转换为 Terraform 的基础设施配置文件。在高层次上,您将:
- 创建应用程序:使用内置或自定义模板以您选择的语言构建项目。
- 定义基础架构:使用您选择的语言来定义您想要在一个或多个提供商上配置的基础架构。CDKTF 自动从 Terraform 提供程序和模块中提取模式,为您的应用程序生成必要的类。
- 部署:使用cdktfCLI 命令为基础设施配置 Terraform,或将您的代码合成为其他人可以直接与 Terraform 一起使用的 JSON 配置文件。
CDKTF 提供了许多好处,但它并不是每个项目的正确选择。在以下情况下,您应该考虑使用 CDKTF:
- 您有强烈的偏好或需要使用过程语言来定义基础设施。
- 您需要创建抽象来帮助管理复杂性。例如,您想要创建构造来对由多种资源和便利方法组成的可重用基础架构模式进行建模。
- 您可以轻松进行自己的故障排除,并且不需要商业支持。
Terraform 的一种经典用例——在AWS上部署一个虚拟机(EC2实例)。我们将使用 Terraform 的 AWS 提供程序来代表我们发出 API 调用和部署 EC2 实例。完成部署后,我们将让 Terraform 销毁该实例,避免服务器一直运行,造成越来越多的费用。这个场景有一个先决条件——你必须安装了 Terraform 0.15.X,并具有 AWS 的访问凭据。部署项目的步骤如下所示。
(1)编写 Terraform 配置文件。
(2)配置 AWS 提供程序。
(3)使用 terraform init 初始化 Terraform。
(4)使用 terraform apply 部署 EC2 实例。
(5)使用 terraform destroy 进行清理。
如下图所示演示了 Hello Terraform! 部署的工作流程:
使用 Terraform 在 AWS 上部署一个 EC2 实例的架构,如下图是Hello Terraform! 的部署流程:
Terraform 通过读取配置文件来部署基础设施。要告诉 Terraform 部署一个 EC2 实例,需要使用代码来声明该 EC2 实例。为此,先要创建一个新文件,将其命名为 main.tf,并添加代码清单中的内容。.tf 扩展名表示这是一个 Terraform 配置文件。Terraform 在运行时,将读取工作目录中所有具有 .tf 扩展名的文件,并把它们连接起来。
main.tf
- resource "aws_instance" "helloworld" { ⇽--- 声明一个名为“HelloWorld”的aws_instance资源
- ami = "ami-09dd2e08d601bff67" ⇽--- EC2实例的特性
- instance_type = "t2.micro"
- tags = {
- Name = "HelloWorld"
- }
- }
注意 此 Amazon 机器映像(Amazon Machine Image,AMI)仅对 us-west-2 地区有效。
我们希望 Terraform 置备一个 t2.micro AWS EC2 实例,使其具有 Ubuntu AMI 和一个名称标签。对比下面给出的等效的 CloudFormation 代码,可以看到 Terraform 代码要清晰得多,也简洁得多。
- {
- "Resources": {
- "Example": {
- "Type": "AWS::EC2::Instance",
- "Properties": {
- "ImageId": "ami-09dd2e08d601bff67",
- "InstanceType": "t2.micro",
- "Tags": [
- {
- "Key": "Name",
- "Value": "HelloWorld"
- }
- ]
- }
- }
- }
- }
这个 EC2 代码块是 Terraform 资源的一个示例。在 Terraform 中,资源是最重要的元素,因为它们置备虚拟机、负载均衡器、NAT 网关等基础设施。资源被声明为 HCL 对象,具有 resource 类型和两个标签。第一个标签指定了要创建的资源的类型,第二个标签是资源的名称。名称并没有特别的意义,只用来在给定模块作用域内引用该资源。类型与名称合起来构成资源标识符,每个资源的标识符都是唯一的。如下图所示显示了 Terraform 资源块的语法:
每个资源都有输入和输出。输入称作实参,输出称作特性。实参通过资源进行传递,也可作为资源特性使用。另外,资源还有计算特性,但只有在创建了资源后才能使用它们。计算特性包含计算得到的关于管理资源的信息。如下图所示显示了 aws_instance 资源的实参、特性和计算特性的示例:
接下来,我们需要配置 AWS 提供程序。AWS 提供程序负责理解 API 交互、发出经过身份验证的请求,以及为 Terraform 提供资源。下面通过添加一个 provider 块来配置 AWS 提供程序。
main.tf
- provider "aws" { ⇽--- 声明AWS提供程序
- ![箭头08{5%}](/api/storage/getbykey/original?key=22031b3595d646878b29) region = "us-west-2" ⇽--- 配置部署地区
- }
-
- resource "aws_instance" "helloworld" {
- ami = "ami-09dd2e08d601bff67"
- instance_type = "t2.micro"
- tags = {
- Name = "HelloWorld"
- }
- }
注意:在置备基础设施之前,需要先获得 AWS 凭据。凭据可以存储到凭据文件中或者环境变量中。与资源不同,提供程序只有一个标签 Name。这是该提供程序在 Terraform 注册表中发布时使用的正式名称(如 “aws” 代表 AWS,“google” 代表 GCP,“azurerm” 代表 Azure)。提供程序块的语法如下图所示:
注意:Terraform 注册表是一个全球商店,用来分享版本化提供程序的二进制文件。当 Terraform 初始化时,会从该注册表自动查找和下载任何必要的提供程序。提供程序没有输出,只有输入。通过传递输入(或配置实参)给 provider 块,可以配置提供程序。配置实参包括服务端点 URL、地区、提供程序版本、通过 API 身份验证所需的任何凭据等。如下图所示演示了其注入过程:
当发出 API 调用时,配置的提供程序如何把凭据注入 aws_instance 中。通常,你不会想要把凭据信息作为纯文本传递给提供程序,特别是以后要把这些代码签入版本控制系统的时候更是如此。因此,许多提供程序允许从环境变量或者共享凭据文件中读取凭据。
在让 Terraform 部署 EC2 实例之前,我们首先必须初始化工作空间。尽管我们已经声明了 AWS 提供程序,但是 Terraform 仍然需要从 Terraform 注册表下载和安装二进制文件。至少需要为所有工作空间执行一次初始化。运行 terraform init 命令可以初始化 Terraform。运行该命令将看到如下输出。
- $ terraform init
-
- Initializing the backend...
- Initializing provider plugins...
- - Finding latest version of hashicorp/aws...
- - Installing hashicorp/aws v3.28.0... ⇽--- Terraform获取AWS提供程序的最新版本
- - Installed hashicorp/aws v3.28.0 (signed by HashiCorp)
- Terraform has created a lock file .terraform.lock.hcl to record the
- provider selections it made above. Include this file in your version
- control repository so that Terraform can guarantee to make the same
- selections by default when you run "terraform init" in the future.
-
- _Terraform has been successfully initialized! ⇽--- 我们真正关心的只有这条信息
- __
- You may now begin working with Terraform. Try running "terraform plan" to
- see any changes that are required for your infrastructure. All Terraform
- commands should now work.
-
- If you ever set or change modules or backend configuration for Terraform,
- rerun this command to reinitialize your working directory. If you forget,
- other commands will detect it and remind you to do so if necessary.
注意:如果还没有安装 Terraform,需要先进行安装,然后才能运行此命令。
现在,我们就准备好使用 Terraform 部署 EC2 实例了。这需要执行下面的 terraform apply 命令。警告:完成此操作后会启用 EC2 和 CloudWatch Logs,这可能会导致对你的 AWS 账户收费。
- $ terraform apply
-
- An execution plan has been generated and is shown below.
- Resource actions are indicated with the following symbols:
- + create
-
- Terraform will perform the following actions:
-
- # aws_instance.helloworld will be created
- + resource "aws_instance" "helloworld" {
- + ami = "ami-09dd2e08d601bff67" ⇽--- ami特性
- + arn = (known after apply)
- + associate_public_ip_address = (known after apply)
- + availability_zone = (known after apply)
- + cpu_core_count = (known after apply)
- + cpu_threads_per_core = (known after apply)
- + get_password_data = false
- + host_id = (known after apply)
- * + id = (known after apply)
- + instance_state = (known after apply)
- * + instance_type = "t2.micro" ⇽--- instance_type特性
- + ipv6_address_count = (known after apply)
- + ipv6_addresses = (known after apply)
- + key_name = (known after apply)
- + network_interface_id = (known after apply)
- + outpost_arn = (known after apply)
- + password_data = (known after apply)
- + placement_group = (known after apply)
- + primary_network_interface_id = (known after apply)
- + private_dns = (known after apply)
- + private_ip = (known after apply)
- + public_dns = (known after apply)
- + public_ip = (known after apply)
- + security_groups = (known after apply)
- + source_dest_check = true
- + subnet_id = (known after apply)
- + tags = { ⇽--- Tags特性
- + "Name" = "HelloWorld"
- }
- + tenancy = (known after apply)
- + volume_tags = (known after apply)
- + vpc_security_group_ids = (known after apply)
-
- + ebs_block_device {
- + delete_on_termination = (known after apply)
- + device_name = (known after apply)
- + encrypted = (known after apply)
- + iops = (known after apply)
- + kms_key_id = (known after apply)
- + snapshot_id = (known after apply)
- + volume_id = (known after apply)
- + volume_size = (known after apply)
- + volume_type = (known after apply)
- }
-
- + ephemeral_block_device {
- + device_name = (known after apply)
- + no_device = (known after apply)
- + virtual_name = (known after apply)
- }
-
- + metadata_options {
- + http_endpoint = (known after apply)
- + http_put_response_hop_limit = (known after apply)
- + http_tokens = (known after apply)
- }
-
- + network_interface {
- + delete_on_termination = (known after apply)
- + device_index = (known after apply)
- + network_interface_id = (known after apply)
- }
-
- + root_block_device {
- + delete_on_termination = (known after apply)
- + device_name = (known after apply)
- + encrypted = (known after apply)
- + iops = (known after apply)
- + kms_key_id = (known after apply)
- + volume_id = (known after apply)
- + volume_size = (known after apply)
- + volume_type = (known after apply)
- }
- }
- *Plan: 1 to add, 0 to change, 0 to destroy. ⇽--- 操作的摘要
-
- Do you want to perform these actions?
- Terraform will perform the actions described above.
- Only 'yes' will be accepted to approve.
-
- Enter a value: ⇽--- 手动批准步骤
提示:如果收到错误 “No Valid Credentials Sources Found”,说明 Terraform 无法通过 AWS 的身份验证。CLI 输出称为执行计划,描述了 Terraform 计划执行哪些操作来得到人们期望的状态。在继续操作前,作为一种健全性检查,检查执行计划是一个好主意。除非在拼写时出错,否则这里不应有什么奇怪的地方。检查完执行计划后,通过在命令行输入 yes 批准执行。一两分钟后(置备 EC2 实例大概需要这么长时间),apply 即成功完成。下面是一些示例输出。
- aws_instance.helloworld: Creating...
- aws_instance.helloworld: Still creating... [10s elapsed]
- aws_instance.helloworld: Still creating... [20s elapsed]
- aws_instance.helloworld: Creation complete after 25s [id=i-070098fcf77d93c54]
-
- Apply complete! Resources: 1 added, 0 changed, 0 destroyed.
要验证资源已被创建,你可以在 AWS 的 EC2 控制台找到它,注意,此实例位于 us-west-2 地区,因为我们在提供程序中就是这么设置的。如下图所示我们可以在控制台找到实例:
资源的状态信息存储在一个名为 terraform.tfstate 的文件中。不要被扩展名 .tfstate 误导,它其实就是一个 JSON 文件。使用 terraform show 命令可以从状态文件输出人类可读的输出,这使得列举 Terraform 管理的资源的信息非常方便。下面是一条 terraform show 命令的执行结果:
- $ terraform show
- # aws_instance.helloworld:
- resource "aws_instance" "helloworld" {
- ami = "ami-09dd2e08d601bff67"
- arn =
- ➥"arn:aws:ec2:us-west-2:215974853022:instance/i-070098fcf77d93c54"
- associate_public_ip_address = true
- availability_zone = "us-west-2a"
- cpu_core_count = 1
- cpu_threads_per_core = 1
- disable_api_termination = false
- ebs_optimized = false
- get_password_data = false
- hibernation = false
- id = "i-070098fcf77d93c54" ⇽--- id是一个重要的计算特性
- instance_state = "running"
- instance_type = "t2.micro"
- ipv6_address_count = 0
- ipv6_addresses = []
- monitoring = false
- primary_network_interface_id = "eni-031d47704eb23eaf0"
- private_dns =
- ➥"ip-172-31-25-172.us-west-2.compute.internal"
- private_ip = "172.31.25.172"
- public_dns =
- ➥"ec2-52-24-28-182.us-west-2.compute.amazonaws.com"
- public_ip = "52.24.28.182"
- secondary_private_ips = []
- security_groups = [
- "default",
- ]
- source_dest_check = true
- subnet_id = "subnet-0d78ac285558cff78"
- tags = {
- "Name" = "HelloWorld"
- }
- tenancy = "default"
- vpc_security_group_ids = [
- "sg-0d8222ef7623a02a5",
- ]
-
- credit_specification {
- cpu_credits = "standard"
- }
-
- enclave_options {
- enabled = false
- }
-
- metadata_options {
- http_endpoint = "enabled"
- http_put_response_hop_limit = 1
- http_tokens = "optional"
- }
-
- root_block_device {
- delete_on_termination = true
- device_name = "/dev/sda1"
- encrypted = false
- iops = 100
- tags = {}
- throughput = 0
- volume_id = "vol-06b149cdd5722d6bc"
- volume_size = 8
- volume_type = "gp2"
- }
- }
这里的特性远多于我们一开始在资源块中设置的特性,这是因为aws_instance中的特性大部分是可选特性或计算特性。通过设置可选实参,你可以自定义 aws_instance。如果你想知道有哪些可选实参,可以参阅 AWS 的提供程序文档。
当不再使用基础设施时,应该销毁它们,因为在云中运行基础设施是要收费的。Terraform 提供了一个特殊命令 —— terraform destroy,用于销毁全部资源。当运行此命令时,Terraform 将会给出提示,要求你手动确认销毁操作:
- $ terraform destroy
- aws_instance.helloworld: Refreshing state... [id=i-070098fcf77d93c54]
-
- Terraform used the selected providers to generate the following execution plan.
- Resource actions are indicated with the following symbols:
- - destroy
-
- Terraform will perform the following actions:
-
- # aws_instance.helloworld will be destroyed
- - resource "aws_instance" "helloworld" {
- - ami = "ami-09dd2e08d601bff67" -> null
- - arn = "arn:aws:ec2:us-west-2:215974853022:
- ➥instance/i-070098fcf77d93c54" -> null
- - associate_public_ip_address = true -> null
- - availability_zone = "us-west-2a" -> null
- - cpu_core_count = 1 -> null
- - cpu_threads_per_core = 1 -> null
- - disable_api_termination = false -> null
- - ebs_optimized = false -> null
- - get_password_data = false -> null
- - hibernation = false -> null
- - id = "i-070098fcf77d93c54" -> null
- - instance_state = "running" -> null
- - instance_type = "t2.micro" -> null
- - ipv6_address_count = 0 -> null
- - ipv6_addresses = [] -> null
- - monitoring = false -> null
- - primary_network_interface_id = "eni-031d47704eb23eaf0" -> null
- - private_dns =
- ➥"ip-172-31-25-172.us-west-2.compute.internal" -> null
- - private_ip = "172.31.25.172" -> null
- - public_dns =
- ➥"ec2-52-24-28-182.us-west-2.compute.amazonaws.com" -> null
- - public_ip = "52.24.28.182" -> null
- - secondary_private_ips = [] -> null
- - security_groups = [
- - "default",
- ] -> null
- - source_dest_check = true -> null
- - subnet_id = "subnet-0d78ac285558cff78" -> null
- - tags = {
- - "Name" = "HelloWorld"
- } -> null
- - tenancy = "default" -> null
- - vpc_security_group_ids = [
- - "sg-0d8222ef7623a02a5",
- ] -> null
-
- - credit_specification {
- - cpu_credits = "standard" -> null
- }
-
- - enclave_options {
- - enabled = false -> null
- }
-
- - metadata_options {
- - http_endpoint = "enabled" -> null
- - http_put_response_hop_limit = 1 -> null
- - http_tokens = "optional" -> null
- }
-
- - root_block_device {
- - delete_on_termination = true -> null
- - device_name = "/dev/sda1" -> null
- - encrypted = false -> null
- - iops = 100 -> null
- - tags = {} -> null
- - throughput = 0 -> null
- - volume_id = "vol-06b149cdd5722d6bc" -> null
- - volume_size = 8 -> null
- - volume_type = "gp2" -> null
- }
- }
-
- *Plan: 0 to add, 0 to change, 1 to destroy. ⇽--- Terraform计划采取的操作的摘要
-
- Do you really want to destroy all resources?
- Terraform will destroy all your managed infrastructure, as shown above.
- There is no undo. Only 'yes' will be accepted to confirm.
-
- Enter a value:
警告:不要手动编辑或删除 terraform.tfstate 文件,这一点很重要,否则 Terraform 将无法跟踪其管理的资源。销毁计划与前面的执行计划类似,只不过它用于删除操作。注意:terraform destroy 执行的操作相当于你删除了所有配置代码,然后运行 terraform apply。通过在命令行输入 yes,确认自己希望应用销毁计划。等待几分钟,让 Terraform 进行处理,然后你将收到 Terraform 已经销毁了所有资源的通知。如下图所示:
- aws_instance.helloworld: Destroying… [id=i-070098fcf77d93c54]
- aws_instance.helloworld: Still destroying...
- ➥[id=i-070098fcf77d93c54, 10s elapsed]
- aws_instance.helloworld: Still destroying...
- ➥[id=i-070098fcf77d93c54, 20s elapsed]
- aws_instance.helloworld: Still destroying...
- ➥[id=i-070098fcf77d93c54, 30s elapsed]
- aws_instance.helloworld: Destruction complete after 31s
-
- Destroy complete! Resources: 1 destroyed.
通过刷新 AWS 控制台,或者运行 terraform show 命令并确认它没有返回任何东西,验证资源确实被销毁了。
我们都喜欢经典的 Hello World! 示例,并认为它是一个不错的入门项目,但这本身并不能系统地展示了整个技术。Terraform 不仅可以从静态配置代码置备资源,还能够基于外部查询和数据查找的结果动态置备资源。我们讨论一下数据源,该元素允许在运行时获取数据并执行计算。下面我们添加一个数据源来动态查找 Ubuntu AMI 的最新值。我们将把输出值传入 aws_instance,这样就不必在 EC2 实例的资源配置中静态设置AMI了,如下图所示:
因为我们已经配置了 AWS 提供程序,并使用 terraform init 初始化了 Terraform,所以可以跳过之前的一些步骤。在这里,我们将执行下面的步骤。
(1)修改Terraform配置来添加数据源。
(2)使用terraform apply进行重新部署。
(3)使用terraform destroy进行清理。
部署流程如下图所示:
我们需要添加从外部数据源读取数据的代码,以便能够查询最新发布到 AWS 的 Ubuntu AMI。编辑 main.tf,如下所示:
main.tf
- provider "aws" {
- region = "us-west-2"
-
- data "aws_ami" "ubuntu" { ⇽--- 声明一个名为“ubuntu”的aws_ami数据源
- most_recent = true
-
- filter { ⇽--- 设置一个过滤器,以选择名称与这个正则表达式匹配的所有AMI
- name = "name"
- values = ["ubuntu/images/hvm-ssd/ubuntu-focal-20.04-amd64-server-*"]
- }
-
- owners = ["099720109477"] ⇽--- 规范的Ubuntu AWS账户ID
- }
- resource "aws_instance" "helloworld" {
- ami = data.aws_ami.ubuntu.id ⇽--- 将资源链接起来
- instance_type = "t2.micro"
- tags = {
- Name = "HelloWorld"
- }
- }
与资源一样,要声明数据源,需要创建一个 HCL 对象,其类型为 data,且具有两个标签。第一个标签指定数据源的类型,第二个标签是数据源的名称。类型和名称合起来构成了数据源的标识符,标识符在一个模块内必须保持唯一。数据源的语法如下图所示:
数据源代码块的内容称为“查询约束实参”。它们的行为与资源的实参的行为相同。查询约束实参用于指定从哪个(哪些)资源获取数据。数据源是不受管理的资源,Terraform 能够从它们读取数据,但不能直接控制它们。
接下来,我们应用修改,让 Terraform 部署一个使用 Ubuntu 数据源的输出值作为 AMI 的 EC2 实例。这需要运行 terraform apply。CLI8 输出如下所示:
- $ terraform apply
-
- Terraform used the selected providers to generate the following execution
- plan.
- Resource actions are indicated with the following symbols:
- + create
-
- Terraform will perform the following actions:
-
- # aws_instance.helloworld will be created
- + resource "aws_instance" "helloworld" {
- + ami = "ami-0928f4202481dfdf6" ⇽--- 使用数据源的输出进行设置
- + arn = (known after apply)
- + associate_public_ip_address = (known after apply)
- + availability_zone = (known after apply)
- + cpu_core_count = (known after apply)
- + cpu_threads_per_core = (known after apply)
- + get_password_data = false
- + host_id = (known after apply)
- + id = (known after apply)
- + instance_state = (known after apply)
- + instance_type = "t2.micro"
- // skip some logs
- }
-
- Plan: 1 to add, 0 to change, 0 to destroy.
-
- Do you want to perform these actions?
- Terraform will perform the actions described above.
- Only 'yes' will be accepted to approve.
-
- Enter a value:
在命令行输入 yes 来应用修改。等待几分钟后,输出将如下所示:
- aws_instance.helloworld: Creating...
- aws_instance.helloworld: Still creating... [10s elapsed]
- aws_instance.helloworld: Creation complete after 19s [id=i-0c0a6a024bb4ba669]
-
- Apply complete! Resources: 1 added, 0 changed, 0 destroyed.
与之前一样,可通过访问 AWS 控制台或者调用 terraform show 来验证修改。
运行 terraform destroy,销毁上一步创建的基础设施。注意,这里仍然需要手动确认。
- $ terraform destroy
- aws_instance.helloworld: Refreshing state... [id=i-0c0a6a024bb4ba669]
-
- Terraform used the selected providers to generate the following execution
- plan.
- Resource actions are indicated with the following symbols:
- - destroy
-
- Terraform will perform the following actions:
-
- # aws_instance.helloworld will be destroyed
- - resource "aws_instance" "helloworld" {
- - ami = "ami-0928f4202481dfdf6" -> null
- - arn = "arn:aws:ec2:us-west-2:215974853022
- ➥:instance/i-0c0a6a024bb4ba669" -> null
- - associate_public_ip_address = true -> null
- // skip some logs
- }
-
- Plan: 0 to add, 0 to change, 1 to destroy.
- Do you really want to destroy all resources?
- Terraform will destroy all your managed infrastructure, as shown above.
- There is no undo. Only 'yes' will be accepted to confirm.
-
- Enter a value:
手动确认并等待几分钟后,EC2 实例将成功销毁。
- aws_instance.helloworld: Destroying... [id=i-0c0a6a024bb4ba669]
- aws_instance.helloworld: Still destroying...
- ➥[id=i-0c0a6a024bb4ba669, 10s elapsed]
- aws_instance.helloworld: Still destroying...
- ➥[id=i-0c0a6a024bb4ba669, 20s elapsed]
- aws_instance.helloworld: Still destroying...
- ➥[id=i-0c0a6a024bb4ba669, 30s elapsed]
- aws_instance.helloworld: Destruction complete after 30s
-
- Destroy complete! Resources: 1 destroyed.
上面我们讨论了什么是 Terraform,相对于其他 IaC 工具它具有哪些优缺点,还讲述了如何执行两个实际部署的案例。第一个部署是 Terraform 的 Hello World! 示例,第二个部署是我个人偏爱的部署,因为它使用数据源演示了 Terraform 的动态能力。另外在这里给大家推荐一本《Terraform 实战》,深入学习 terraform。
本书基于实际项目,揭示如何使用 Terraform 自动扩展和管理基础架构。本书重点介绍了 Terraform 0.12 的语法、基础知识和高级设计(如零停机时间部署和创建 Terraform 提供程序)。本书主要内容包括如何使用 Terraform,如何管理 Terraform 资源的生命周期,如何编程,如何在 AWS 云中部署多层的 Web 应用程序,如何实现无服务器的部署,如何通过 Terraform 部署服务器,如何实现零停机部署,如何测试、重构,如何扩展 Terraform,如何通过 Terraform 自动部署,如何实现安全管理。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。