Terraform
管理基础设施
在之前的教程中,您使用 Terraform 在 AWS 上创建了一个 EC2 实例。
在本教程中,您将学习 Terraform 如何实现配置更改。您将添加输入变量和输出值,以使您的配置更加动态和灵活,并使用模块来引用可重用的基础设施集合。
先决条件
要遵循本教程,您需要
- 已安装 Terraform CLI(1.2.0+)。
- 已安装 AWS CLI。
- 一个 AWS 账户和 关联的凭证,允许您在
us-west-2区域中创建资源,包括一个 EC2 实例、VPC 和安全组。 - 来自 创建基础设施 教程的配置和基础设施。
完成上一教程后,您将拥有一个名为 learn-terraform-get-started-aws 的目录,其中包含名为 main.tf 的配置文件。
main.tf
provider "aws" {
region = "us-west-2"
}
data "aws_ami" "ubuntu" {
most_recent = true
filter {
name = "name"
values = ["ubuntu/images/hvm-ssd-gp3/ubuntu-noble-24.04-amd64-server-*"]
}
owners = ["099720109477"] # Canonical
}
resource "aws_instance" "app_server" {
ami = data.aws_ami.ubuntu.id
instance_type = "t2.micro"
tags = {
Name = "learn-terraform"
}
}
您在之前的教程中创建的配置也包含一个 terraform.tf 文件,该文件配置 Terraform。
terraform.tf
terraform {
required_providers {
aws = {
source = "hashicorp/aws"
version = "~> 5.92"
}
}
required_version = ">= 1.2"
}
变量和输出
输入变量允许您参数化 Terraform 配置的行为。您还可以定义输出值以暴露您创建的资源的数据。 变量和输出还允许您通过提供配置和检索工作区基础设施数据的一致接口,将您的 Terraform 工作区与其他自动化工具集成。
输入变量
在您的 learn-terraform-aws-get-started 目录中创建一个名为 variables.tf 的新文件,并包含以下配置。
variables.tf
variable "instance_name" {
description = "Value of the EC2 instance's Name tag."
type = string
default = "learn-terraform"
}
variable "instance_type" {
description = "The EC2 instance's type."
type = string
default = "t2.micro"
}
这些输入变量允许您在每次更新 EC2 实例的名称和类型时,而无需修改配置文件。 两个变量都设置了 Terraform 在您未指定值时使用的默认值。 我们建议您将工作区变量和输出定义放在各自的文件中,variables.tf 和 outputs.tf,以便更容易地维护您的 Terraform 配置。
更新 main.tf 中的实例配置,以引用这些变量而不是硬编码参数值。
main.tf
resource "aws_instance" "app_server" {
ami = data.aws_ami.ubuntu.id
- instance_type = "t2.micro"
+ instance_type = var.instance_type
tags = {
- Name = "learn-terraform"
+ Name = var.instance_name
}
}
您可以通过多种方式设置 Terraform 变量的值,包括环境变量、命令行参数和存储在磁盘上的文件。
运行 Terraform 计划而不应用它,以查看如果您将 EC2 实例类型从 t2.micro 更改为 t2.large 使用命令行变量会发生什么。
$ terraform plan -var instance_type=t2.large
data.aws_ami.ubuntu: Reading...
data.aws_ami.ubuntu: Read complete after 1s [id=ami-0026a04369a3093cc]
aws_instance.app_server: Refreshing state... [id=i-0c636e158c30e48f9]
Terraform used the selected providers to generate the following execution plan. Resource actions are indicated with the following symbols:
~ update in-place
Terraform will perform the following actions:
# aws_instance.app_server will be updated in-place
~ resource "aws_instance" "app_server" {
id = "i-0c636e158c30e48f9"
~ instance_type = "t2.micro" -> "t2.large"
~ public_dns = "ec2-34-216-162-36.us-west-2.compute.amazonaws.com" -> (known after apply)
~ public_ip = "34.216.162.36" -> (known after apply)
tags = {
"Name" = "learn-terraform"
}
# (36 unchanged attributes hidden)
# (8 unchanged blocks hidden)
}
Plan: 0 to add, 1 to change, 0 to destroy.
─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
Note: You didn't use the -out option to save this plan, so Terraform can't guarantee to take exactly these actions if you run "terraform apply"
now.
如果您应用此计划,Terraform 将使用新的实例类型更新您的 EC2 实例。 Terraform 需要替换实例才能实现此更改,因此 AWS 提供程序还指示 IP 地址和主机名也会更改,但将它们标记为 (应用后可知),因为 AWS 在您应用更改以重新创建实例之前不会分配新的值。
输出值
输出值允许您访问 Terraform 配置中的属性并使用其值与其他自动化工具或工作流程。
创建一个名为 outputs.tf 的新文件,并包含以下配置。
outputs.tf
output "instance_hostname" {
description = "Private DNS name of the EC2 instance."
value = aws_instance.app_server.private_dns
}
此输出值从您的 Terraform 工作区公开您的 EC2 实例的主机名。
应用您的配置。 由于您创建的两个变量的默认值与它们所取代的硬编码值相同,因此 Terraform 会检测到唯一的更改是您添加的输出值。 使用 yes 回应确认提示,以将输出值添加到您的工作区。
$ terraform apply
data.aws_ami.ubuntu: Reading...
data.aws_ami.ubuntu: Read complete after 1s [id=ami-0026a04369a3093cc]
aws_instance.app_server: Refreshing state... [id=i-0c636e158c30e48f9]
Changes to Outputs:
+ instance_hostname = "ip-172-31-35-26.us-west-2.compute.internal"
You can apply this plan to save these new output values to the Terraform state, without changing any real infrastructure.
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
Apply complete! Resources: 0 added, 0 changed, 0 destroyed.
Outputs:
instance_hostname = "ip-172-31-35-26.us-west-2.compute.internal"
Terraform 在您运行计划或应用时会打印出您的输出值,并将其存储在您工作区的状态文件中。
使用 `terraform output 命令查看你的输出值。
$ terraform output
instance_hostname = "ip-172-31-35-26.us-west-2.compute.internal"
模块
模块是可重用的配置集合。 使用模块可以一致地管理包含多个资源和数据源的复杂基础设施部署。 类似于提供程序,你可以从 Terraform Registry 获取模块。 你也可以创建自己的模块并在你的组织内部共享它们。
模块块
将一个模块块添加到你的配置中 `main.tf` 中,以创建一个 VPC 和与你的 EC2 实例相关的网络资源。
main.tf
module "vpc" {
source = "terraform-aws-modules/vpc/aws"
version = "5.19.0"
name = "example-vpc"
cidr = "10.0.0.0/16"
azs = ["us-west-2a", "us-west-2b", "us-west-2c"]
private_subnets = ["10.0.1.0/24", "10.0.2.0/24"]
public_subnets = ["10.0.101.0/24"]
enable_dns_hostnames = true
}
由于 Terraform 会自动解析配置中的依赖关系,因此你可以按照你喜欢的任何顺序组织配置块。 作为最佳实践,我们建议你组织配置,以便于你和你的团队维护。
此配置定义了一个名为 `example-vpc` 的 VPC,具有两个公有子网和两个私有子网。 查看 AWS VPC 模块页面 在 Terraform Registry 上,其中包含有关如何使用该模块创建网络资源(包括子网、安全组、弹性 IP 地址和 NAT 网关)的文档和示例。
通过更新资源块以匹配下面的内容,将你的 EC2 实例迁移到新的 VPC 中。
main.tf
resource "aws_instance" "app_server" {
ami = data.aws_ami.ubuntu.id
instance_type = var.instance_type
vpc_security_group_ids = [module.vpc.default_security_group_id]
subnet_id = module.vpc.private_subnets[0]
tags = {
Name = var.instance_name
}
}
此更改会将你的 EC2 实例配置到你的新 VPC 之一的公有子网中,并使用其默认安全组。
每当你向配置添加新的模块时,都需要通过重新初始化工作区来安装它。
通过运行 `terraform init` 来安装 VPC 模块。
$ terraform init
Initializing the backend...
Initializing modules...
Downloading registry.terraform.io/terraform-aws-modules/vpc/aws 5.19.0 for vpc...
- vpc in .terraform/modules/vpc
Initializing provider plugins...
- Reusing previous version of hashicorp/aws from the dependency lock file
- Using previously-installed hashicorp/aws v5.98.0
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 在工作区目录中的 `.terraform.lock.hcl` 文件中跟踪配置中使用的提供程序的当前版本。
规划和应用更改
应用更新的配置以创建新的 VPC 并将你的 EC2 实例迁移到其中。 由于 AWS 不支持将现有的 EC2 实例移动到新的 VPC,因此 Terraform 将计划替换你的现有实例,而不是就地更新它。 通过对确认提示回复 `yes` 来批准 Terraform 的计划,以添加 VPC 并替换你的 EC2 实例。
$ terraform apply
data.aws_ami.ubuntu: Reading...
data.aws_ami.ubuntu: Read complete after 1s [id=ami-0026a04369a3093cc]
aws_instance.app_server: Refreshing state... [id=i-0c636e158c30e48f9]
Terraform used the selected providers to generate the following execution plan. Resource actions are indicated with the following symbols:
+ create
-/+ destroy and then create replacement
Terraform will perform the following actions:
# aws_instance.app_server must be replaced
-/+ resource "aws_instance" "app_server" {
~ arn = "arn:aws:ec2:us-west-2:949008909725:instance/i-0c636e158c30e48f9" -> (known after apply)
~ associate_public_ip_address = true -> (known after apply)
~ availability_zone = "us-west-2b" -> (known after apply)
##...
}
}
Plan: 16 to add, 0 to change, 1 to destroy.
Changes to Outputs:
~ instance_hostname = "ip-172-31-35-26.us-west-2.compute.internal" -> (known after apply)
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.app_server: Destroying... [id=i-0fbb487cbdf2eb6ed]
aws_instance.app_server: Still destroying... [id=i-0fbb487cbdf2eb6ed, 00m10s elapsed]
aws_instance.app_server: Still destroying... [id=i-0fbb487cbdf2eb6ed, 00m20s elapsed]
aws_instance.app_server: Still destroying... [id=i-0fbb487cbdf2eb6ed, 00m30s elapsed]
aws_instance.app_server: Destruction complete after 31s
module.vpc.aws_vpc.this[0]: Creating...
module.vpc.aws_vpc.this[0]: Still creating... [00m10s elapsed]
module.vpc.aws_vpc.this[0]: Creation complete after 12s [id=vpc-01e157ec1af2d7314]
module.vpc.aws_subnet.private[0]: Creating...
module.vpc.aws_route_table.private[1]: Creating...
module.vpc.aws_subnet.private[1]: Creating...
module.vpc.aws_route_table.private[0]: Creating...
module.vpc.aws_default_route_table.default[0]: Creating...
module.vpc.aws_subnet.public[0]: Creating...
module.vpc.aws_internet_gateway.this[0]: Creating...
module.vpc.aws_default_security_group.this[0]: Creating...
## ...
module.vpc.aws_route_table_association.private[1]: Creation complete after 0s [id=rtbassoc-0ea0b41646a73659c]
module.vpc.aws_route_table_association.private[0]: Creation complete after 0s [id=rtbassoc-0d51502051aa2c2af]
module.vpc.aws_default_network_acl.this[0]: Creation complete after 2s [id=acl-03b6b08c6f6a81e4a]
module.vpc.aws_route.public_internet_gateway[0]: Creation complete after 1s [id=r-rtb-0cde73c077eadf7e61080289494]
module.vpc.aws_default_security_group.this[0]: Creation complete after 3s [id=sg-04f350f66843618db]
module.vpc.aws_subnet.public[0]: Creation complete after 3s [id=subnet-02a7d5d6b6e8742d6]
module.vpc.aws_route_table_association.public[0]: Creating...
module.vpc.aws_route_table_association.public[0]: Creation complete after 1s [id=rtbassoc-03db7fad8ba24eca0]
aws_instance.app_server: Still creating... [00m10s elapsed]
aws_instance.app_server: Creation complete after 13s [id=i-0226232d8b6e9eea6]
Apply complete! Resources: 16 added, 0 changed, 1 destroyed.
Outputs:
instance_hostname = "ip-10-0-1-75.us-west-2.compute.internal"
Terraform 创建由你添加的 `vpc` 模块定义的 VPC 和相关资源。
请注意,Terraform 基于资源依赖关系按顺序执行所需的操作。 首先,它销毁了你的旧 EC2 实例,然后它创建了你的 VPC 和大多数相关资源,最后创建了你的新 EC2 实例。 当 Terraform 创建执行计划时,它会构造依赖关系图以确定操作的正确顺序。 当你应用你的计划时,Terraform 会以依赖顺序创建、更新和销毁你的资源,并在可能的情况下并行执行。
使用 `terraform state list` 命令打印出你的工作空间的资源列表。
$ terraform state list
data.aws_ami.ubuntu
aws_instance.app_server
module.vpc.aws_default_network_acl.this[0]
module.vpc.aws_default_route_table.default[0]
module.vpc.aws_default_security_group.this[0]
module.vpc.aws_internet_gateway.this[0]
module.vpc.aws_route.public_internet_gateway[0]
module.vpc.aws_route_table.private[0]
module.vpc.aws_route_table.private[1]
module.vpc.aws_route_table.public[0]
module.vpc.aws_route_table_association.private[0]
module.vpc.aws_route_table_association.private[1]
module.vpc.aws_route_table_association.public[0]
module.vpc.aws_subnet.private[0]
module.vpc.aws_subnet.private[1]
module.vpc.aws_subnet.public[0]
module.vpc.aws_vpc.this[0]
请注意,在 VPC 模块中配置的资源以 `module.vpc` 开头。 就像资源和数据源地址一样,模块地址在你的配置中必须是唯一的。 你可以通过指定相同的 `source` 和 `version` 参数来多次使用同一个模块,同时为每个模块块赋予一个唯一的名称。
继续阅读下一个教程,学习如何使用 Terraform 销毁基础设施。
交互式终端
你可以在我们的交互式终端中学习如何在不设置云账户的情况下创建你的基础设施。 要跟随本教程中的练习,请使用实验室环境中的 跳过 按钮跳至第二个挑战。
启动终端
本教程包括一个免费的交互式命令行实验室,您可以跟随实际云基础设施进行操作。