1.2.1. Terraform初步体验

1.2.1.1. 安装

首先我们先安装Terraform。对于Ubuntu用户:

sudo apt-get update && sudo apt-get install -y wget curl gnupg software-properties-common
wget -O- https://apt.releases.hashicorp.com/gpg | \
gpg --dearmor | \
sudo tee /usr/share/keyrings/hashicorp-archive-keyring.gpg
echo "deb [signed-by=/usr/share/keyrings/hashicorp-archive-keyring.gpg] \
https://apt.releases.hashicorp.com $(lsb_release -cs) main" | \
sudo tee /etc/apt/sources.list.d/hashicorp.list
sudo apt update
sudo apt-get install terraform

对于CentOS用户:

sudo yum install -y yum-utils
sudo yum-config-manager --add-repo https://rpm.releases.hashicorp.com/RHEL/hashicorp.repo
sudo yum -y install terraform

对于Mac用户:

brew tap hashicorp/tap
brew install hashicorp/tap/terraform

对于Windows用户,官方推荐的包管理器是choco,可以去https://chocolatey.org/ 下载安装好chocolatey后,以管理员身份启动powershell,然后:

choco install terraform

如果只想纯手动安装,那么可以前往Terraform官网下载对应操作系统的可执行文件(Terraform是用go编写的,只有一个可执行文件),解压缩到指定的位置后,配置一下环境变量的PATH,使其包含Terraform所在的目录即可。

1.2.1.2. 验证

terraform version
Terraform v1.7.3

terraform -h
Usage: terraform [global options] <subcommand> [args]

The available commands for execution are listed below.
The primary workflow commands are given first, followed by
less common or more advanced commands.

Main commands:
  init          Prepare your working directory for other commands
  validate      Check whether the configuration is valid
  plan          Show changes required by the current configuration
  apply         Create or update infrastructure
  destroy       Destroy previously-created infrastructure

All other commands:
  console       Try Terraform expressions at an interactive command prompt
  fmt           Reformat your configuration in the standard style
  force-unlock  Release a stuck lock on the current workspace
  get           Install or upgrade remote Terraform modules
  graph         Generate a Graphviz graph of the steps in an operation
  import        Associate existing infrastructure with a Terraform resource
  login         Obtain and save credentials for a remote host
  logout        Remove locally-stored credentials for a remote host
  metadata      Metadata related commands
  output        Show output values from your root module
  providers     Show the providers required for this configuration
  refresh       Update the state to match remote systems
  show          Show the current state or a saved plan
  state         Advanced state management
  taint         Mark a resource instance as not fully functional
  test          Execute integration tests for Terraform modules
  untaint       Remove the 'tainted' state from a resource instance
  version       Show the current Terraform version
  workspace     Workspace management

Global options (use these before the subcommand, if any):
  -chdir=DIR    Switch to a different working directory before executing the
                given subcommand.
  -help         Show this help output, or the help for a specified subcommand.
  -version      An alias for the "version" subcommand.

1.2.1.3. 一个简单的例子

为了给读者一个安全的体验环境,避免付费,我们下面使用 LocalStack 搭配 Terraform 来创建一批虚拟的云资源。

启动 LocalStack 最简单的方法是使用 Docker

docker run \
  --rm -it \
  -p 4566:4566 \
  -p 4510-4559:4510-4559 \
  localstack/localstack

看到如下输出时说明 LocalStack 已经准备好了:

...

LocalStack version: 3.1.1.dev
LocalStack build date: 2024-02-14
LocalStack build git hash: ebaf4ea8d

2024-02-15T01:56:11.845  INFO --- [-functhread4] hypercorn.error            : Running on https://0.0.0.0:4566 (CTRL + C to quit)
2024-02-15T01:56:11.845  INFO --- [-functhread4] hypercorn.error            : Running on https://0.0.0.0:4566 (CTRL + C to quit)
2024-02-15T01:56:12.103  INFO --- [  MainThread] localstack.utils.bootstrap : Execution of "start_runtime_components" took 1804.56ms
Ready.

然后我们创建一个干净的空文件夹,在里面创建一个 main.tf 文件(.tf 就是 TerraformTerraform 代码大部分是 .tf 文件,语法是 HCL,当然目前也支持 JSON 格式的 Terraform 代码,但我们暂时只以 .tf 为例):

terraform {
  required_providers {
    aws = {
      source = "hashicorp/aws"
      version = "~>5.0"
    }
  }
}

provider "aws" {
  access_key                  = "test"
  secret_key                  = "test"
  region                      = "us-east-1"
  s3_use_path_style           = false
  skip_credentials_validation = true
  skip_metadata_api_check     = true
  skip_requesting_account_id  = true

  endpoints {
    apigateway     = "http://localhost:4566"
    apigatewayv2   = "http://localhost:4566"
    cloudformation = "http://localhost:4566"
    cloudwatch     = "http://localhost:4566"
    dynamodb       = "http://localhost:4566"
    ec2            = "http://localhost:4566"
    es             = "http://localhost:4566"
    elasticache    = "http://localhost:4566"
    firehose       = "http://localhost:4566"
    iam            = "http://localhost:4566"
    kinesis        = "http://localhost:4566"
    lambda         = "http://localhost:4566"
    rds            = "http://localhost:4566"
    redshift       = "http://localhost:4566"
    route53        = "http://localhost:4566"
    s3             = "http://s3.localhost.localstack.cloud:4566"
    secretsmanager = "http://localhost:4566"
    ses            = "http://localhost:4566"
    sns            = "http://localhost:4566"
    sqs            = "http://localhost:4566"
    ssm            = "http://localhost:4566"
    stepfunctions  = "http://localhost:4566"
    sts            = "http://localhost:4566"
  }
}

data "aws_ami" "ubuntu" {
  most_recent = true

  filter {
    name   = "name"
    values = ["ubuntu/images/hvm-ssd/ubuntu-trusty-14.04-amd64-server-20170727"]
  }

  filter {
    name   = "virtualization-type"
    values = ["hvm"]
  }

  owners = ["099720109477"] # Canonical
}

resource "aws_instance" "web" {
  ami           = data.aws_ami.ubuntu.id
  instance_type = "t3.micro"

  tags = {
    Name = "HelloWorld"
  }
}

resource "aws_eip_association" "eip_assoc" {
  instance_id   = aws_instance.web.id
  allocation_id = aws_eip.example.id
}

resource "aws_eip" "example" {
  domain = "vpc"
}

output "instance_id" {
  value = aws_instance.web.id
}

这里要注意代码中的这一段:

provider "aws" {
  access_key                  = "test"
  secret_key                  = "test"
  region                      = "us-east-1"
  s3_use_path_style           = false
  skip_credentials_validation = true
  skip_metadata_api_check     = true
  skip_requesting_account_id  = true

  endpoints {
    apigateway     = "http://localhost:4566"
    apigatewayv2   = "http://localhost:4566"
    cloudformation = "http://localhost:4566"
    cloudwatch     = "http://localhost:4566"
    dynamodb       = "http://localhost:4566"
    ec2            = "http://localhost:4566"
    es             = "http://localhost:4566"
    elasticache    = "http://localhost:4566"
    firehose       = "http://localhost:4566"
    iam            = "http://localhost:4566"
    kinesis        = "http://localhost:4566"
    lambda         = "http://localhost:4566"
    rds            = "http://localhost:4566"
    redshift       = "http://localhost:4566"
    route53        = "http://localhost:4566"
    s3             = "http://s3.localhost.localstack.cloud:4566"
    secretsmanager = "http://localhost:4566"
    ses            = "http://localhost:4566"
    sns            = "http://localhost:4566"
    sqs            = "http://localhost:4566"
    ssm            = "http://localhost:4566"
    stepfunctions  = "http://localhost:4566"
    sts            = "http://localhost:4566"
  }
}

因为我们使用的是通过 LocalStack 模拟的虚拟 AWS 服务,所以在这里我们需要在 endpoints 中把各个服务 API 的端点设置为 LocalStack 暴露的本地端点。原本的 access_keysecret_key 应该是通过 AWS IAM 获取的,在这里我们就可以用假的 Key 来替代。

这段代码比较简单,头部的terraform这一段声明了这段代码所需要的Terraform版本以及 AWS 插件版本,后面的 provider 段则是给出了调用 AWS API所需要的 key 和 region 等信息。

真正定义云端基础设施的代码就是后面的部分,分为三部分,dataresourceoutput

data 代表利用 AWS 插件定义的 data 模型对 AWS 进行查询,例如我们在代码中利用 data 查询名为 "ubuntu/images/hvm-ssd/ubuntu-trusty-14.04-amd64-server-20170727 的 AWS 虚拟机镜像 ID,这样我们就不需要人工在界面上去查询相关 ID,再硬编码到代码中。请注意,由于我们使用的是 LocalStack 虚拟的 AWS,所以这里的 nameowners 都是参照了 LocalStack 自带的模拟数据 构造的。

resource 代表我们需要在云端创建的资源,在例子里我们创建了三个资源,分别是主机、弹性公网 ip,以及主机和公网 ip 的绑定。

我们在定义主机时给定了主机的尺寸名称等关键信息,最后,我们声明了一个output,名字是eip,它的值就是我们创建的弹性公网ip的值。

运行这段代码很简单,让我们在代码所在的路径下进入命令行,执行:

$ terraform init

这时Terraform会进行初始化操作,通过官方插件仓库下载对应操作系统的UCloud插件。如果一切都正常,读者应该会看到:

Initializing the backend...

Initializing provider plugins...
- Finding latest version of hashicorp/aws...
- Installing hashicorp/aws v5.36.0...
- Installed hashicorp/aws v5.36.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.

╷
│ Warning: Provider development overrides are in effect
│
│ The following provider development overrides are set in the CLI configuration:
│  - azure/modtm in C:\Users\hezijie\go\bin
│  - lonegunmanb/aztfteam in C:\Users\hezijie\go\bin
│
│ Skip terraform init when using provider development overrides. It is not necessary and may error unexpectedly.
╵

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.
PS C:\project\mySpike> terraform terraform apply -auto-approve
Terraform has no command named "terraform".

To see all of Terraform's top-level commands, run:
  terraform -help

然后我们可以预览一下代码即将产生的变更:

$ data.aws_ami.ubuntu: Reading...
data.aws_ami.ubuntu: Read complete after 0s [id=ami-1e749f67]

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_eip.example will be created
  + resource "aws_eip" "example" {
      + allocation_id        = (known after apply)
      + association_id       = (known after apply)
      + carrier_ip           = (known after apply)
      + customer_owned_ip    = (known after apply)
      + domain               = "vpc"
      + id                   = (known after apply)
      + instance             = (known after apply)
      + network_border_group = (known after apply)
      + network_interface    = (known after apply)
      + private_dns          = (known after apply)
      + private_ip           = (known after apply)
      + public_dns           = (known after apply)
      + public_ip            = (known after apply)
      + public_ipv4_pool     = (known after apply)
      + tags_all             = (known after apply)
      + vpc                  = (known after apply)
    }

  # aws_eip_association.eip_assoc will be created
  + resource "aws_eip_association" "eip_assoc" {
      + allocation_id        = (known after apply)
      + id                   = (known after apply)
      + instance_id          = (known after apply)
      + network_interface_id = (known after apply)
      + private_ip_address   = (known after apply)
      + public_ip            = (known after apply)
    }

  # aws_instance.web will be created
  + resource "aws_instance" "web" {
      + ami                                  = "ami-1e749f67"
      + 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)
      + disable_api_stop                     = (known after apply)
      + disable_api_termination              = (known after apply)
      + ebs_optimized                        = (known after apply)
      + get_password_data                    = false
      + host_id                              = (known after apply)
      + host_resource_group_arn              = (known after apply)
      + iam_instance_profile                 = (known after apply)
      + id                                   = (known after apply)
      + instance_initiated_shutdown_behavior = (known after apply)
      + instance_lifecycle                   = (known after apply)
      + instance_state                       = (known after apply)
      + instance_type                        = "t3.micro"
      + ipv6_address_count                   = (known after apply)
      + ipv6_addresses                       = (known after apply)
      + key_name                             = (known after apply)
      + monitoring                           = (known after apply)
      + outpost_arn                          = (known after apply)
      + password_data                        = (known after apply)
      + placement_group                      = (known after apply)
      + placement_partition_number           = (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)
      + secondary_private_ips                = (known after apply)
      + security_groups                      = (known after apply)
      + source_dest_check                    = true
      + spot_instance_request_id             = (known after apply)
      + subnet_id                            = (known after apply)
      + tags                                 = {
          + "Name" = "HelloWorld"
        }
      + tags_all                             = {
          + "Name" = "HelloWorld"
        }
      + tenancy                              = (known after apply)
      + user_data                            = (known after apply)
      + user_data_base64                     = (known after apply)
      + user_data_replace_on_change          = false
      + vpc_security_group_ids               = (known after apply)
    }

Plan: 3 to add, 0 to change, 0 to destroy.

Changes to Outputs:
  + instance_id = (known after apply)
╷
│ Warning: AWS account ID not found for provider
│
│   with provider["registry.terraform.io/hashicorp/aws"],
│   on main.tf line 1, in provider "aws":1: provider "aws" {
│
│ See https://registry.terraform.io/providers/hashicorp/aws/latest/docs#skip_requesting_account_id for implications.
╵

────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── 

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.

这段输出告诉我们,代码即将创建 3 个新资源,修改 0 个资源,删除 0 个资源。资源的属性少部分是我们在代码中直接给出的,或是通过 data 查询的,所以在plan命令的结果中可以看到它们的值;更多的属性只有在资源真正被创建以后我们才能看到,所以会显示 (known after apply)

然后我们运行一下:

$ terraform apply

data.aws_ami.ubuntu: Reading...
data.aws_ami.ubuntu: Read complete after 0s [id=ami-1e749f67]

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_eip.example will be created
  + resource "aws_eip" "example" {
      + allocation_id        = (known after apply)
      + association_id       = (known after apply)
      + carrier_ip           = (known after apply)
      + customer_owned_ip    = (known after apply)
      + domain               = "vpc"
      + id                   = (known after apply)
      + instance             = (known after apply)
      + network_border_group = (known after apply)
      + network_interface    = (known after apply)
      + private_dns          = (known after apply)
      + private_ip           = (known after apply)
      + public_dns           = (known after apply)
      + public_ip            = (known after apply)
      + public_ipv4_pool     = (known after apply)
      + tags_all             = (known after apply)
      + vpc                  = (known after apply)
    }

  # aws_eip_association.eip_assoc will be created
  + resource "aws_eip_association" "eip_assoc" {
      + allocation_id        = (known after apply)
      + id                   = (known after apply)
      + instance_id          = (known after apply)
      + network_interface_id = (known after apply)
      + private_ip_address   = (known after apply)
      + public_ip            = (known after apply)
    }

  # aws_instance.web will be created
  + resource "aws_instance" "web" {
      + ami                                  = "ami-1e749f67"
      + 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)
      + disable_api_stop                     = (known after apply)
      + disable_api_termination              = (known after apply)
      + ebs_optimized                        = (known after apply)
      + get_password_data                    = false
      + host_id                              = (known after apply)
      + host_resource_group_arn              = (known after apply)
      + iam_instance_profile                 = (known after apply)
      + id                                   = (known after apply)
      + instance_initiated_shutdown_behavior = (known after apply)
      + instance_lifecycle                   = (known after apply)
      + instance_state                       = (known after apply)
      + instance_type                        = "t3.micro"
      + ipv6_address_count                   = (known after apply)
      + ipv6_addresses                       = (known after apply)
      + key_name                             = (known after apply)
      + monitoring                           = (known after apply)
      + outpost_arn                          = (known after apply)
      + password_data                        = (known after apply)
      + placement_group                      = (known after apply)
      + placement_partition_number           = (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)
      + secondary_private_ips                = (known after apply)
      + security_groups                      = (known after apply)
      + source_dest_check                    = true
      + spot_instance_request_id             = (known after apply)
      + subnet_id                            = (known after apply)
      + tags                                 = {
          + "Name" = "HelloWorld"
        }
      + tags_all                             = {
          + "Name" = "HelloWorld"
        }
      + tenancy                              = (known after apply)
      + user_data                            = (known after apply)
      + user_data_base64                     = (known after apply)
      + user_data_replace_on_change          = false
      + vpc_security_group_ids               = (known after apply)
    }

Plan: 3 to add, 0 to change, 0 to destroy.

Changes to Outputs:
  + instance_id = (known after apply)
╷
│ Warning: AWS account ID not found for provider
│
│   with provider["registry.terraform.io/hashicorp/aws"],
│   on main.tf line 1, in provider "aws":1: provider "aws" {
│
│ See https://registry.terraform.io/providers/hashicorp/aws/latest/docs#skip_requesting_account_id for implications.
╵

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

当我们运行 terraform apply 时,Terraform 会首先重新计算一下变更计划,并且像刚才执行 plan 命令那样把变更计划打印给我们,要求我们人工确认。让我们输入 yes,然后回车:

aws_eip.example: Creating...
aws_instance.web: Creating...
aws_eip.example: Creation complete after 0s [id=eipalloc-c71b7984]
aws_instance.web: Still creating... [10s elapsed]
aws_instance.web: Creation complete after 10s [id=i-c22230eecf2b8a950]
aws_eip_association.eip_assoc: Creating...
aws_eip_association.eip_assoc: Creation complete after 0s [id=eipassoc-194f62e6]
╷
│ Warning: AWS account ID not found for provider
│
│   with provider["registry.terraform.io/hashicorp/aws"],
│   on main.tf line 1, in provider "aws":1: provider "aws" {
│
│ See https://registry.terraform.io/providers/hashicorp/aws/latest/docs#skip_requesting_account_id for implications.
╵

Apply complete! Resources: 3 added, 0 changed, 0 destroyed.

Outputs:

instance_id = "i-c22230eecf2b8a950"

可以看到,Terraform 成功地创建了我们定义的资源,并且把我们定义的输出给打印了出来。

1.2.1.4. 清理

完成这个体验后,不要忘记清理我们的云端资源。我们可以通过调用 destroy 命令来轻松完成清理:

$ terraform destroy
data.aws_ami.ubuntu: Reading...
aws_eip.example: Refreshing state... [id=eipalloc-c71b7984]
data.aws_ami.ubuntu: Read complete after 0s [id=ami-1e749f67]
aws_instance.web: Refreshing state... [id=i-c22230eecf2b8a950]
aws_eip_association.eip_assoc: Refreshing state... [id=eipassoc-194f62e6]

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_eip.example will be destroyed
  - resource "aws_eip" "example" {
      - allocation_id     = "eipalloc-c71b7984" -> null
      - association_id    = "eipassoc-194f62e6" -> null
      - domain            = "vpc" -> null
      - id                = "eipalloc-c71b7984" -> null
      - instance          = "i-c22230eecf2b8a950" -> null
      - network_interface = "eni-b689ba2c" -> null
      - private_dns       = "ip-10-73-124-108.ec2.internal" -> null
      - private_ip        = "10.73.124.108" -> null
      - public_dns        = "ec2-127-127-89-71.compute-1.amazonaws.com" -> null
      - public_ip         = "127.127.89.71" -> null
      - tags              = {} -> null
      - tags_all          = {} -> null
      - vpc               = true -> null
    }

  # aws_eip_association.eip_assoc will be destroyed
  - resource "aws_eip_association" "eip_assoc" {
      - allocation_id        = "eipalloc-c71b7984" -> null
      - id                   = "eipassoc-194f62e6" -> null
      - instance_id          = "i-c22230eecf2b8a950" -> null
      - network_interface_id = "eni-b689ba2c" -> null
      - private_ip_address   = "10.73.124.108" -> null
      - public_ip            = "127.127.89.71" -> null
    }

  # aws_instance.web will be destroyed
  - resource "aws_instance" "web" {
      - ami                                  = "ami-1e749f67" -> null
      - arn                                  = "arn:aws:ec2:us-east-1::instance/i-c22230eecf2b8a950" -> null
      - associate_public_ip_address          = true -> null
      - availability_zone                    = "us-east-1a" -> null
      - disable_api_stop                     = false -> null
      - disable_api_termination              = false -> null
      - ebs_optimized                        = false -> null
      - get_password_data                    = false -> null
      - hibernation                          = false -> null
      - id                                   = "i-c22230eecf2b8a950" -> null
      - instance_initiated_shutdown_behavior = "stop" -> null
      - instance_state                       = "running" -> null
      - instance_type                        = "t3.micro" -> null
      - ipv6_address_count                   = 0 -> null
      - ipv6_addresses                       = [] -> null
      - monitoring                           = false -> null
      - placement_partition_number           = 0 -> null
      - primary_network_interface_id         = "eni-b689ba2c" -> null
      - private_dns                          = "ip-10-73-124-108.ec2.internal" -> null
      - private_ip                           = "10.73.124.108" -> null
      - public_dns                           = "ec2-127-127-89-71.compute-1.amazonaws.com" -> null
      - public_ip                            = "127.127.89.71" -> null
      - secondary_private_ips                = [] -> null
      - security_groups                      = [] -> null
      - source_dest_check                    = true -> null
      - subnet_id                            = "subnet-73eb6c90" -> null
      - tags                                 = {
          - "Name" = "HelloWorld"
        } -> null
      - tags_all                             = {
          - "Name" = "HelloWorld"
        } -> null
      - tenancy                              = "default" -> null
      - user_data_replace_on_change          = false -> null
      - vpc_security_group_ids               = [] -> null

      - root_block_device {
          - delete_on_termination = true -> null
          - device_name           = "/dev/sda1" -> null
          - encrypted             = false -> null
          - iops                  = 0 -> null
          - tags                  = {} -> null
          - throughput            = 0 -> null
          - volume_id             = "vol-b56e5dc7" -> null
          - volume_size           = 8 -> null
          - volume_type           = "gp2" -> null
        }
    }

Plan: 0 to add, 0 to change, 3 to destroy.

Changes to Outputs:
  - instance_id = "i-c22230eecf2b8a950" -> null
╷
│ Warning: AWS account ID not found for provider
│
│   with provider["registry.terraform.io/hashicorp/aws"],
│   on main.tf line 1, in provider "aws":1: provider "aws" {
│
│ See https://registry.terraform.io/providers/hashicorp/aws/latest/docs#skip_requesting_account_id for implications.
╵

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: yes

可以看到,Terraform 列出了它即将清理的资源信息,并且要求我们人工确认同意继续执行清理操作。我们输入 yes,然后回车:

aws_eip_association.eip_assoc: Destroying... [id=eipassoc-194f62e6]
aws_eip_association.eip_assoc: Destruction complete after 0s
aws_eip.example: Destroying... [id=eipalloc-c71b7984]
aws_instance.web: Destroying... [id=i-c22230eecf2b8a950]
aws_eip.example: Destruction complete after 0s
aws_instance.web: Still destroying... [id=i-c22230eecf2b8a950, 10s elapsed]
aws_instance.web: Destruction complete after 10s

Destroy complete! Resources: 3 destroyed.

很快的,刚才创建的资源就全部被删除了。(在本例中因为我们使用了 LocalStack,所以我们并没有真的创建 AWS 资源,所以不执行 destroy 也没什么问题,但是在试验后清理实验资源是一个很好的习惯)

Terraform 与以往诸如 Ansible 等配置管理工具比较大的不同在于,它是根据代码计算出的目标状态与当前状态的差异来计算变更计划的,有兴趣的读者可以在执行 terraform apply 以后,直接再执行一次 terraform apply,看看会发生什么,就能明白他们之间的差异。

实际上这段代码在 apply 以后,直接再次 apply,得到的计划会是什么也不做:

$ terraform plan

data.aws_ami.ubuntu: Reading...
aws_eip.example: Refreshing state... [id=eipalloc-c12bcca1]
data.aws_ami.ubuntu: Read complete after 0s [id=ami-1e749f67]
aws_instance.web: Refreshing state... [id=i-0159574cc2f7986ad]
aws_eip_association.eip_assoc: Refreshing state... [id=eipassoc-7f849867]

No changes. Your infrastructure matches the configuration.

因为当前云端的资源状态已经完全符合代码所描述的期望状态了,所以 Terraform 什么也不会做。好了,这就是我们对 Terraform 的一个初步体验。

results matching ""

    No results matching ""