Terraform 学习总结—— Terraform 实战
Posted 科技D人生
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Terraform 学习总结—— Terraform 实战相关的知识,希望对你有一定的参考价值。
前言
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 工具。Terraform 的基本原则是,它允许编写人类可读的配置代码来定义 IaC。借助配置代码,你可以把可重复的、短暂的、一致的环境部署到公有云、私有云和混合云上的供应商,即 我们借助 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 的一种经典用例——在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 通过读取配置文件来部署基础设施。要告诉 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 提供程序。AWS 提供程序负责理解 API 交互、发出经过身份验证的请求,以及为 Terraform 提供资源。下面通过添加一个 provider 块来配置 AWS 提供程序。
main.tf
provider "aws" ⇽--- 声明AWS提供程序
![箭头085%](/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
在让 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,需要先进行安装,然后才能运行此命令。
部署 EC2 实例
现在,我们就准备好使用 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 的提供程序文档。
销毁 EC2 实例
当不再使用基础设施时,应该销毁它们,因为在云中运行基础设施是要收费的。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进行清理。
部署流程如下图所示:
修改 Terraform 配置
我们需要添加从外部数据源读取数据的代码,以便能够查询最新发布到 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 自动部署,如何实现安全管理。
创作挑战赛 新人创作奖励来咯,坚持创作打卡瓜分现金大奖以上是关于Terraform 学习总结—— Terraform 实战的主要内容,如果未能解决你的问题,请参考以下文章
Terraform 学习总结—— Terraform 常用命令再总结
Terraform 学习总结—— Terraform 常用命令再总结
Terraform 学习总结—— Terraform 常用命令再总结
Terraform 学习总结(10)—— 阿里云平台 Terraform 代码开发技巧总结