基础设施自动化实践——用Terraform自动化构建云上环境

本文涉及的产品
云服务器 ECS,每月免费额度200元 3个月
云服务器ECS,u1 2核4GB 1个月
公网NAT网关,每月750个小时 15CU
简介: 前言在我们平时的开发工作中,经常会遇到需要搭建各式各样的云上环境,比如项目开发环境、测试环境、部署环境、客户展示环境等。而有时这些搭建过程并不轻松,如可能涉及很多台ECS,可能对镜像或配置有特殊要求,可能涉及较为复杂的网络配置等。而对于这些较为复杂多变的环境部署要求,传统的人工手动配置方法,不仅耗时费力,还有一个很大的问题就是难以追踪管理、难以复用。比如负责搭建环境的架构师如果休假了,团队其他人很

前言

在我们平时的开发工作中,经常会遇到需要搭建各式各样的云上环境,比如项目开发环境、测试环境、部署环境、客户展示环境等。而有时这些搭建过程并不轻松,如可能涉及很多台ECS,可能对镜像或配置有特殊要求,可能涉及较为复杂的网络配置等。而对于这些较为复杂多变的环境部署要求,传统的人工手动配置方法,不仅耗时费力,还有一个很大的问题就是难以追踪管理、难以复用。比如负责搭建环境的架构师如果休假了,团队其他人很难搞清楚如何快速更改当前环境的配置或快速搭建另一套类似的环境。

在软件开发领域,一个很重要的原则就是我们应该尽可能地将一切流程代码化、Git化,以便共享与复用,并实现自动化。在CI/CD领域,我们已经体验到了尽可能地将编译、部署、测试、发布等流程脚本化、自动化。而在基础设施环境搭建方面,也有同样的思路。我们可以借助于Terraform,轻松地实现基础设施即代码(Infrastructure as code)。

背景

关于Terraform本身的使用说明或介绍教程已有很多,本文将不再赘述Terraform本身涉及的基础概念、语法、命令或用法,而是以具体、真实的案例为例,向大家介绍一下如何利用Terraform完成云上环境构建自动化。该案例的背景是我们希望在阿里云上构建一个demo环境,用于部署我们的风险管理产品,并希望能模拟客户环境中遭遇的恶意行为,产生真实的风险分数变化告警。为了简化并清晰起见,我这里简单地将要求列举如下:

  1. 云上同一个区域内申请4台ECS,规格尽可能小,且采用较旧的debian镜像(节约成本;该版本漏洞较多,便于模拟客户环境产生告警)。
  2. 要求能访问公网。
  3. 每个实例上希望能自动化定时运行恶意脚本(脚本中包含一些高风险请求,用于模拟客户环境正在遭受恶意行为)。

初始化

阿里云提供了两种方式运行Terraform,一种是通过CloudShell,另一种是通过本地CLI。为了便于与Git交互,建议在本地安装Terraform,并为项目创建Git仓库。每次试图变更环境之前,可以先从Git拉取最新的Terraform文件,本地比较、修改后,执行Terraform变更到远程云上环境;环境变更执行成功后,记得及时提交变更文件到远程Git。

在本地CLI执行Terraform的有效命令之前,还需要配置阿里云上所需的ak/sk等信息,以便连接云上provider,示例如下:

export ALICLOUD_ACCESS_KEY="ak"
export ALICLOUD_SECRET_KEY="sk"
export ALICLOUD_REGION="cn-hangzhou"

创建ECS实例

我们先从创建ECS示例开始,这其实是我们平时使用云环境中最经常遇到的场景。我们可以直接使用alicloud_instance(https://registry.terraform.io/providers/aliyun/alicloud/latest/docs/resources/instance)来创建,也可以使ecs_instance的module(https://registry.terraform.io/modules/alibaba/ecs-instance/alicloud/latest)来创建。module是对resource的封装,使用起来更简单快捷。因为我们一次需要创建多个实例,这里采用module的形式,配置代码如下:

variable "profile" {
  default = "default"
}

variable "region" {
  default = "cn-hangzhou"
}

variable "zone_id" {
  default = "cn-hangzhou-h"
}

variable "instances_number" {
  default = 4
}

provider "alicloud" {
  region  = var.region
  profile = var.profile
}

resource "alicloud_vpc" "vpc" {
  vpc_name   = "vpc_tf_demo"
  cidr_block = "172.16.0.0/12"
}

resource "alicloud_vswitch" "vsw" {
  vpc_id     = alicloud_vpc.vpc.id
  cidr_block = "172.16.0.0/21"
  zone_id    = var.zone_id
}

resource "alicloud_security_group" "default" {
  name   = "default"
  vpc_id = alicloud_vpc.vpc.id
}

resource "alicloud_security_group_rule" "allow_all_tcp" {
  type              = "ingress"
  ip_protocol       = "tcp"
  nic_type          = "intranet"
  policy            = "accept"
  port_range        = "1/65535"
  priority          = 1
  security_group_id = alicloud_security_group.default.id
  cidr_ip           = "0.0.0.0/0"
}

data "alicloud_images" "debian" {
  most_recent = false
  name_regex  = "^debian_8_09_64_20G_alibase_20170824.vhd"
}

module "tf-instances" {
  source                      = "alibaba/ecs-instance/alicloud"
  region                      = var.region
  number_of_instances         = var.instances_number
  vswitch_id                  = alicloud_vswitch.vsw.id
  group_ids                   = [alicloud_security_group.default.id]
  private_ips                 = ["172.16.0.9", "172.16.0.10", "172.16.0.11", "172.16.0.12"]
  image_id                    = data.alicloud_images.debian.ids.0
  instance_type               = "ecs.s6-c1m1.small"
  associate_public_ip_address = false
  instance_name               = "demo_instance_"
  host_name                   = "tf-demo"
  internet_charge_type        = "PayByTraffic"
  password                    = "password123"
  system_disk_category        = "cloud_ssd"
  system_disk_size            = 20

  tags = {
    Created     = "Terraform"
    env         = "pre"
    application = "online-shop"
  }
}


这里我们简单定义了一些variable变量,指定了alicloud provider,定义了vpc/vswitch/security_group/security_group_rule等资源;指定了镜像为某个较旧版本的debian;并利用ecs-instance的module创建了4个ECS实例,其中分别指定了private_ip/instance_name/disk/tags等信息。

init

编写完成terraform文件之后,需要执行Terraform init来初始化该Terraform文件所在的目录,并下载必要的插件。执行成功后的提示信息如下:

shanyao:terraform-ata shanyao$ terraform init
Initializing modules...

Initializing the backend...

Initializing provider plugins...
- Reusing previous version of hashicorp/alicloud from the dependency lock file
- Using previously-installed hashicorp/alicloud v1.186.0

╷
│ Warning: Additional provider information from registry
│ 
│ The remote registry returned warnings for registry.terraform.io/hashicorp/alicloud:
│ - For users on Terraform 0.13 or greater, this provider has moved to aliyun/alicloud. Please update your source in required_providers.
╵

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.

大家可以看到,在init执行的过程中,下载了provider/modules等插件,并完成了目录的初始化。初始化完成后的工程目录如下:

可见,在.teraform文件下,新增了modules/providers等插件。

plan

完成init之后,可以通过terraform plan来查看本次变更所涉及的远程资源变更,执行后的结果如下:

shanyao:terraform-ata shanyao$ terraform plan
data.alicloud_images.debian: Reading...
data.alicloud_images.debian: Read complete after 2s [id=4116092999]

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:

  # alicloud_security_group.default will be created
  + resource "alicloud_security_group" "default" {
      + id                  = (known after apply)
      + inner_access        = (known after apply)
      + inner_access_policy = (known after apply)
      + name                = "default"
      + security_group_type = "normal"
      + vpc_id              = (known after apply)
    }

  # alicloud_security_group_rule.allow_all_tcp will be created
  + resource "alicloud_security_group_rule" "allow_all_tcp" {
      + cidr_ip           = "0.0.0.0/0"
      + id                = (known after apply)
      + ip_protocol       = "tcp"
      + nic_type          = "intranet"
      + policy            = "accept"
      + port_range        = "1/65535"
      + prefix_list_id    = (known after apply)
      + priority          = 1
      + security_group_id = (known after apply)
      + type              = "ingress"
    }

  # alicloud_vpc.vpc will be created
  + resource "alicloud_vpc" "vpc" {
      + cidr_block            = "172.16.0.0/12"
      + id                    = (known after apply)
      + ipv6_cidr_block       = (known after apply)
      + name                  = (known after apply)
      + resource_group_id     = (known after apply)
      + route_table_id        = (known after apply)
      + router_id             = (known after apply)
      + router_table_id       = (known after apply)
      + secondary_cidr_blocks = (known after apply)
      + status                = (known after apply)
      + vpc_name              = "vpc_tf_demo"
    }

  # alicloud_vswitch.vsw will be created
  + resource "alicloud_vswitch" "vsw" {
      + availability_zone = (known after apply)
      + cidr_block        = "172.16.0.0/21"
      + id                = (known after apply)
      + name              = (known after apply)
      + status            = (known after apply)
      + vpc_id            = (known after apply)
      + vswitch_name      = (known after apply)
      + zone_id           = "cn-hangzhou-h"
    }

  # module.tf-instances.alicloud_instance.this[0] will be created
  + resource "alicloud_instance" "this" {
      + availability_zone                  = (known after apply)
      + credit_specification               = (known after apply)
      + deletion_protection                = false
      + deployment_set_group_no            = (known after apply)
      + description                        = "An ECS instance came from terraform-alicloud-modules/ecs-instance"
      + dry_run                            = false
      + host_name                          = "tf-demo001"
      + id                                 = (known after apply)
      + image_id                           = "debian_8_09_64_20G_alibase_20170824.vhd"
      + instance_charge_type               = "PostPaid"
      + instance_name                      = "demo_instance_001"
      + instance_type                      = "ecs.s6-c1m1.small"
      + internet_max_bandwidth_in          = (known after apply)
      + internet_max_bandwidth_out         = 0
      + key_name                           = (known after apply)
      + maintenance_action                 = (known after apply)
      + operator_type                      = "upgrade"
      + private_ip                         = "172.16.0.9"
      + public_ip                          = (known after apply)
      + role_name                          = (known after apply)
      + secondary_private_ip_address_count = (known after apply)
      + secondary_private_ips              = (known after apply)
      + security_enhancement_strategy      = "Active"
      + security_groups                    = (known after apply)
      + spot_strategy                      = "NoSpot"
      + status                             = "Running"
      + stopped_mode                       = (known after apply)
      + subnet_id                          = (known after apply)
      + system_disk_category               = "cloud_ssd"
      + system_disk_performance_level      = (known after apply)
      + system_disk_size                   = 20
      + tags                               = {
          + "Created"     = "Terraform"
          + "Name"        = "demo_instance_001"
          + "application" = "online-shop"
          + "env"         = "pre"
        }
      + volume_tags                        = {
          + "Name" = "demo_instance_001"
        }
      + vswitch_id                         = (known after apply)
    }

  # module.tf-instances.alicloud_instance.this[1] will be created
  + resource "alicloud_instance" "this" {
      + availability_zone                  = (known after apply)
      + credit_specification               = (known after apply)
      + deletion_protection                = false
      + deployment_set_group_no            = (known after apply)
      + description                        = "An ECS instance came from terraform-alicloud-modules/ecs-instance"
      + dry_run                            = false
      + host_name                          = "tf-demo002"
      + id                                 = (known after apply)
      + image_id                           = "debian_8_09_64_20G_alibase_20170824.vhd"
      + instance_charge_type               = "PostPaid"
      + instance_name                      = "demo_instance_002"
      + instance_type                      = "ecs.s6-c1m1.small"
      + internet_max_bandwidth_in          = (known after apply)
      + internet_max_bandwidth_out         = 0
      + key_name                           = (known after apply)
      + maintenance_action                 = (known after apply)
      + operator_type                      = "upgrade"
      + private_ip                         = "172.16.0.10"
      + public_ip                          = (known after apply)
      + role_name                          = (known after apply)
      + secondary_private_ip_address_count = (known after apply)
      + secondary_private_ips              = (known after apply)
      + security_enhancement_strategy      = "Active"
      + security_groups                    = (known after apply)
      + spot_strategy                      = "NoSpot"
      + status                             = "Running"
      + stopped_mode                       = (known after apply)
      + subnet_id                          = (known after apply)
      + system_disk_category               = "cloud_ssd"
      + system_disk_performance_level      = (known after apply)
      + system_disk_size                   = 20
      + tags                               = {
          + "Created"     = "Terraform"
          + "Name"        = "demo_instance_002"
          + "application" = "online-shop"
          + "env"         = "pre"
        }
      + volume_tags                        = {
          + "Name" = "demo_instance_002"
        }
      + vswitch_id                         = (known after apply)
    }

  # module.tf-instances.alicloud_instance.this[2] will be created
  + resource "alicloud_instance" "this" {
      + availability_zone                  = (known after apply)
      + credit_specification               = (known after apply)
      + deletion_protection                = false
      + deployment_set_group_no            = (known after apply)
      + description                        = "An ECS instance came from terraform-alicloud-modules/ecs-instance"
      + dry_run                            = false
      + host_name                          = "tf-demo003"
      + id                                 = (known after apply)
      + image_id                           = "debian_8_09_64_20G_alibase_20170824.vhd"
      + instance_charge_type               = "PostPaid"
      + instance_name                      = "demo_instance_003"
      + instance_type                      = "ecs.s6-c1m1.small"
      + internet_max_bandwidth_in          = (known after apply)
      + internet_max_bandwidth_out         = 0
      + key_name                           = (known after apply)
      + maintenance_action                 = (known after apply)
      + operator_type                      = "upgrade"
      + private_ip                         = "172.16.0.11"
      + public_ip                          = (known after apply)
      + role_name                          = (known after apply)
      + secondary_private_ip_address_count = (known after apply)
      + secondary_private_ips              = (known after apply)
      + security_enhancement_strategy      = "Active"
      + security_groups                    = (known after apply)
      + spot_strategy                      = "NoSpot"
      + status                             = "Running"
      + stopped_mode                       = (known after apply)
      + subnet_id                          = (known after apply)
      + system_disk_category               = "cloud_ssd"
      + system_disk_performance_level      = (known after apply)
      + system_disk_size                   = 20
      + tags                               = {
          + "Created"     = "Terraform"
          + "Name"        = "demo_instance_003"
          + "application" = "online-shop"
          + "env"         = "pre"
        }
      + volume_tags                        = {
          + "Name" = "demo_instance_003"
        }
      + vswitch_id                         = (known after apply)
    }

  # module.tf-instances.alicloud_instance.this[3] will be created
  + resource "alicloud_instance" "this" {
      + availability_zone                  = (known after apply)
      + credit_specification               = (known after apply)
      + deletion_protection                = false
      + deployment_set_group_no            = (known after apply)
      + description                        = "An ECS instance came from terraform-alicloud-modules/ecs-instance"
      + dry_run                            = false
      + host_name                          = "tf-demo004"
      + id                                 = (known after apply)
      + image_id                           = "debian_8_09_64_20G_alibase_20170824.vhd"
      + instance_charge_type               = "PostPaid"
      + instance_name                      = "demo_instance_004"
      + instance_type                      = "ecs.s6-c1m1.small"
      + internet_max_bandwidth_in          = (known after apply)
      + internet_max_bandwidth_out         = 0
      + key_name                           = (known after apply)
      + maintenance_action                 = (known after apply)
      + operator_type                      = "upgrade"
      + private_ip                         = "172.16.0.12"
      + public_ip                          = (known after apply)
      + role_name                          = (known after apply)
      + secondary_private_ip_address_count = (known after apply)
      + secondary_private_ips              = (known after apply)
      + security_enhancement_strategy      = "Active"
      + security_groups                    = (known after apply)
      + spot_strategy                      = "NoSpot"
      + status                             = "Running"
      + stopped_mode                       = (known after apply)
      + subnet_id                          = (known after apply)
      + system_disk_category               = "cloud_ssd"
      + system_disk_performance_level      = (known after apply)
      + system_disk_size                   = 20
      + tags                               = {
          + "Created"     = "Terraform"
          + "Name"        = "demo_instance_004"
          + "application" = "online-shop"
          + "env"         = "pre"
        }
      + volume_tags                        = {
          + "Name" = "demo_instance_004"
        }
      + vswitch_id                         = (known after apply)
    }

Plan: 8 to add, 0 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.

可见,该命令提示我们,该执行计划将会创建security_group/security_group_rule/vpc/vswitch,以及4个alicloud_instance实例;总计涉及8个资源的add(4个网络相关资源和4个ECS实例)。

apply

执行计划查看后,若符合预期,则可以通过terraform apply命令执行变更到云上,执行后的示例结果如下:

shanyao:terraform-ata shanyao$ terraform apply
data.alicloud_images.debian: Reading...
data.alicloud_images.debian: Read complete after 2s [id=4116092999]

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:

  # alicloud_security_group.default will be created
  + resource "alicloud_security_group" "default" {
      + id                  = (known after apply)
      + inner_access        = (known after apply)
      + inner_access_policy = (known after apply)
      + name                = "default"
      + security_group_type = "normal"
      + vpc_id              = (known after apply)
    }

  # alicloud_security_group_rule.allow_all_tcp will be created
  + resource "alicloud_security_group_rule" "allow_all_tcp" {
      + cidr_ip           = "0.0.0.0/0"
      + id                = (known after apply)
      + ip_protocol       = "tcp"
      + nic_type          = "intranet"
      + policy            = "accept"
      + port_range        = "1/65535"
      + prefix_list_id    = (known after apply)
      + priority          = 1
      + security_group_id = (known after apply)
      + type              = "ingress"
    }

  # alicloud_vpc.vpc will be created
  + resource "alicloud_vpc" "vpc" {
      + cidr_block            = "172.16.0.0/12"
      + id                    = (known after apply)
      + ipv6_cidr_block       = (known after apply)
      + name                  = (known after apply)
      + resource_group_id     = (known after apply)
      + route_table_id        = (known after apply)
      + router_id             = (known after apply)
      + router_table_id       = (known after apply)
      + secondary_cidr_blocks = (known after apply)
      + status                = (known after apply)
      + vpc_name              = "vpc_tf_demo"
    }

  # alicloud_vswitch.vsw will be created
  + resource "alicloud_vswitch" "vsw" {
      + availability_zone = (known after apply)
      + cidr_block        = "172.16.0.0/21"
      + id                = (known after apply)
      + name              = (known after apply)
      + status            = (known after apply)
      + vpc_id            = (known after apply)
      + vswitch_name      = (known after apply)
      + zone_id           = "cn-hangzhou-h"
    }

  # module.tf-instances.alicloud_instance.this[0] will be created
  + resource "alicloud_instance" "this" {
      + availability_zone                  = (known after apply)
      + credit_specification               = (known after apply)
      + deletion_protection                = false
      + deployment_set_group_no            = (known after apply)
      + description                        = "An ECS instance came from terraform-alicloud-modules/ecs-instance"
      + dry_run                            = false
      + host_name                          = "tf-demo001"
      + id                                 = (known after apply)
      + image_id                           = "debian_8_09_64_20G_alibase_20170824.vhd"
      + instance_charge_type               = "PostPaid"
      + instance_name                      = "demo_instance_001"
      + instance_type                      = "ecs.s6-c1m1.small"
      + internet_max_bandwidth_in          = (known after apply)
      + internet_max_bandwidth_out         = 0
      + key_name                           = (known after apply)
      + maintenance_action                 = (known after apply)
      + operator_type                      = "upgrade"
      + private_ip                         = "172.16.0.9"
      + public_ip                          = (known after apply)
      + role_name                          = (known after apply)
      + secondary_private_ip_address_count = (known after apply)
      + secondary_private_ips              = (known after apply)
      + security_enhancement_strategy      = "Active"
      + security_groups                    = (known after apply)
      + spot_strategy                      = "NoSpot"
      + status                             = "Running"
      + stopped_mode                       = (known after apply)
      + subnet_id                          = (known after apply)
      + system_disk_category               = "cloud_ssd"
      + system_disk_performance_level      = (known after apply)
      + system_disk_size                   = 20
      + tags                               = {
          + "Created"     = "Terraform"
          + "Name"        = "demo_instance_001"
          + "application" = "online-shop"
          + "env"         = "pre"
        }
      + volume_tags                        = {
          + "Name" = "demo_instance_001"
        }
      + vswitch_id                         = (known after apply)
    }

  # module.tf-instances.alicloud_instance.this[1] will be created
  + resource "alicloud_instance" "this" {
      + availability_zone                  = (known after apply)
      + credit_specification               = (known after apply)
      + deletion_protection                = false
      + deployment_set_group_no            = (known after apply)
      + description                        = "An ECS instance came from terraform-alicloud-modules/ecs-instance"
      + dry_run                            = false
      + host_name                          = "tf-demo002"
      + id                                 = (known after apply)
      + image_id                           = "debian_8_09_64_20G_alibase_20170824.vhd"
      + instance_charge_type               = "PostPaid"
      + instance_name                      = "demo_instance_002"
      + instance_type                      = "ecs.s6-c1m1.small"
      + internet_max_bandwidth_in          = (known after apply)
      + internet_max_bandwidth_out         = 0
      + key_name                           = (known after apply)
      + maintenance_action                 = (known after apply)
      + operator_type                      = "upgrade"
      + private_ip                         = "172.16.0.10"
      + public_ip                          = (known after apply)
      + role_name                          = (known after apply)
      + secondary_private_ip_address_count = (known after apply)
      + secondary_private_ips              = (known after apply)
      + security_enhancement_strategy      = "Active"
      + security_groups                    = (known after apply)
      + spot_strategy                      = "NoSpot"
      + status                             = "Running"
      + stopped_mode                       = (known after apply)
      + subnet_id                          = (known after apply)
      + system_disk_category               = "cloud_ssd"
      + system_disk_performance_level      = (known after apply)
      + system_disk_size                   = 20
      + tags                               = {
          + "Created"     = "Terraform"
          + "Name"        = "demo_instance_002"
          + "application" = "online-shop"
          + "env"         = "pre"
        }
      + volume_tags                        = {
          + "Name" = "demo_instance_002"
        }
      + vswitch_id                         = (known after apply)
    }

  # module.tf-instances.alicloud_instance.this[2] will be created
  + resource "alicloud_instance" "this" {
      + availability_zone                  = (known after apply)
      + credit_specification               = (known after apply)
      + deletion_protection                = false
      + deployment_set_group_no            = (known after apply)
      + description                        = "An ECS instance came from terraform-alicloud-modules/ecs-instance"
      + dry_run                            = false
      + host_name                          = "tf-demo003"
      + id                                 = (known after apply)
      + image_id                           = "debian_8_09_64_20G_alibase_20170824.vhd"
      + instance_charge_type               = "PostPaid"
      + instance_name                      = "demo_instance_003"
      + instance_type                      = "ecs.s6-c1m1.small"
      + internet_max_bandwidth_in          = (known after apply)
      + internet_max_bandwidth_out         = 0
      + key_name                           = (known after apply)
      + maintenance_action                 = (known after apply)
      + operator_type                      = "upgrade"
      + private_ip                         = "172.16.0.11"
      + public_ip                          = (known after apply)
      + role_name                          = (known after apply)
      + secondary_private_ip_address_count = (known after apply)
      + secondary_private_ips              = (known after apply)
      + security_enhancement_strategy      = "Active"
      + security_groups                    = (known after apply)
      + spot_strategy                      = "NoSpot"
      + status                             = "Running"
      + stopped_mode                       = (known after apply)
      + subnet_id                          = (known after apply)
      + system_disk_category               = "cloud_ssd"
      + system_disk_performance_level      = (known after apply)
      + system_disk_size                   = 20
      + tags                               = {
          + "Created"     = "Terraform"
          + "Name"        = "demo_instance_003"
          + "application" = "online-shop"
          + "env"         = "pre"
        }
      + volume_tags                        = {
          + "Name" = "demo_instance_003"
        }
      + vswitch_id                         = (known after apply)
    }

  # module.tf-instances.alicloud_instance.this[3] will be created
  + resource "alicloud_instance" "this" {
      + availability_zone                  = (known after apply)
      + credit_specification               = (known after apply)
      + deletion_protection                = false
      + deployment_set_group_no            = (known after apply)
      + description                        = "An ECS instance came from terraform-alicloud-modules/ecs-instance"
      + dry_run                            = false
      + host_name                          = "tf-demo004"
      + id                                 = (known after apply)
      + image_id                           = "debian_8_09_64_20G_alibase_20170824.vhd"
      + instance_charge_type               = "PostPaid"
      + instance_name                      = "demo_instance_004"
      + instance_type                      = "ecs.s6-c1m1.small"
      + internet_max_bandwidth_in          = (known after apply)
      + internet_max_bandwidth_out         = 0
      + key_name                           = (known after apply)
      + maintenance_action                 = (known after apply)
      + operator_type                      = "upgrade"
      + private_ip                         = "172.16.0.12"
      + public_ip                          = (known after apply)
      + role_name                          = (known after apply)
      + secondary_private_ip_address_count = (known after apply)
      + secondary_private_ips              = (known after apply)
      + security_enhancement_strategy      = "Active"
      + security_groups                    = (known after apply)
      + spot_strategy                      = "NoSpot"
      + status                             = "Running"
      + stopped_mode                       = (known after apply)
      + subnet_id                          = (known after apply)
      + system_disk_category               = "cloud_ssd"
      + system_disk_performance_level      = (known after apply)
      + system_disk_size                   = 20
      + tags                               = {
          + "Created"     = "Terraform"
          + "Name"        = "demo_instance_004"
          + "application" = "online-shop"
          + "env"         = "pre"
        }
      + volume_tags                        = {
          + "Name" = "demo_instance_004"
        }
      + vswitch_id                         = (known after apply)
    }

Plan: 8 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

alicloud_vpc.vpc: Creating...
alicloud_vpc.vpc: Creation complete after 6s [id=vpc-bp13jb0hn8gvlk7frvngl]
alicloud_security_group.default: Creating...
alicloud_vswitch.vsw: Creating...
alicloud_security_group.default: Creation complete after 1s [id=sg-bp14g56wck3nk5qlvlyl]
alicloud_security_group_rule.allow_all_tcp: Creating...
alicloud_security_group_rule.allow_all_tcp: Creation complete after 1s [id=sg-bp14g56wck3nk5qlvlyl:ingress:tcp:1/65535:intranet:0.0.0.0/0:accept:1]
alicloud_vswitch.vsw: Creation complete after 6s [id=vsw-bp14yu7pmsg7zkkvry3vb]
module.tf-instances.alicloud_instance.this[2]: Creating...
module.tf-instances.alicloud_instance.this[1]: Creating...
module.tf-instances.alicloud_instance.this[3]: Creating...
module.tf-instances.alicloud_instance.this[0]: Creating...
module.tf-instances.alicloud_instance.this[2]: Still creating... [10s elapsed]
module.tf-instances.alicloud_instance.this[1]: Still creating... [10s elapsed]
module.tf-instances.alicloud_instance.this[3]: Still creating... [10s elapsed]
module.tf-instances.alicloud_instance.this[0]: Still creating... [10s elapsed]
module.tf-instances.alicloud_instance.this[0]: Still creating... [20s elapsed]
module.tf-instances.alicloud_instance.this[1]: Still creating... [20s elapsed]
module.tf-instances.alicloud_instance.this[3]: Still creating... [20s elapsed]
module.tf-instances.alicloud_instance.this[2]: Still creating... [20s elapsed]
module.tf-instances.alicloud_instance.this[0]: Creation complete after 22s [id=i-bp15veru9k3viz048x9f]
module.tf-instances.alicloud_instance.this[1]: Creation complete after 22s [id=i-bp1dtiqbjgjinghn478b]
module.tf-instances.alicloud_instance.this[3]: Creation complete after 22s [id=i-bp1b3a43b6c7sfffcbsp]
module.tf-instances.alicloud_instance.this[2]: Creation complete after 22s [id=i-bp176ekeo6unrcunczdx]

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

该命令将再次提示我们即将进行的远程资源变更,若确认无误,需输入“yes”,则正式执行。最终提示我们执行完成,有8个资源已经added。

我们可以登录阿里云ECS控制台,查看确认新创建的实例信息,如下:

如果创建后的ECS实例不符合预期,也可以直接修改terraform文件,再通过plan、apply来执行变更。比如,我们可以新增或修改密码,plan提示如下:

shanyao:terraform-ata shanyao$ terraform plan
data.alicloud_images.debian: Reading...
alicloud_vpc.vpc: Refreshing state... [id=vpc-bp13jb0hn8gvlk7frvngl]
alicloud_security_group.default: Refreshing state... [id=sg-bp14g56wck3nk5qlvlyl]
alicloud_vswitch.vsw: Refreshing state... [id=vsw-bp14yu7pmsg7zkkvry3vb]
alicloud_security_group_rule.allow_all_tcp: Refreshing state... [id=sg-bp14g56wck3nk5qlvlyl:ingress:tcp:1/65535:intranet:0.0.0.0/0:accept:1]
data.alicloud_images.debian: Read complete after 2s [id=4116092999]
module.tf-instances.alicloud_instance.this[3]: Refreshing state... [id=i-bp1b3a43b6c7sfffcbsp]
module.tf-instances.alicloud_instance.this[1]: Refreshing state... [id=i-bp1dtiqbjgjinghn478b]
module.tf-instances.alicloud_instance.this[2]: Refreshing state... [id=i-bp176ekeo6unrcunczdx]
module.tf-instances.alicloud_instance.this[0]: Refreshing state... [id=i-bp15veru9k3viz048x9f]

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:

  # module.tf-instances.alicloud_instance.this[0] will be updated in-place
  ~ resource "alicloud_instance" "this" {
        id                                 = "i-bp15veru9k3viz048x9f"
      ~ password                           = (sensitive value)
        tags                               = {
            "Created"     = "Terraform"
            "Name"        = "demo_instance_001"
            "application" = "online-shop"
            "env"         = "pre"
        }
        # (30 unchanged attributes hidden)
    }

  # module.tf-instances.alicloud_instance.this[1] will be updated in-place
  ~ resource "alicloud_instance" "this" {
        id                                 = "i-bp1dtiqbjgjinghn478b"
      ~ password                           = (sensitive value)
        tags                               = {
            "Created"     = "Terraform"
            "Name"        = "demo_instance_002"
            "application" = "online-shop"
            "env"         = "pre"
        }
        # (30 unchanged attributes hidden)
    }

  # module.tf-instances.alicloud_instance.this[2] will be updated in-place
  ~ resource "alicloud_instance" "this" {
        id                                 = "i-bp176ekeo6unrcunczdx"
      ~ password                           = (sensitive value)
        tags                               = {
            "Created"     = "Terraform"
            "Name"        = "demo_instance_003"
            "application" = "online-shop"
            "env"         = "pre"
        }
        # (30 unchanged attributes hidden)
    }

  # module.tf-instances.alicloud_instance.this[3] will be updated in-place
  ~ resource "alicloud_instance" "this" {
        id                                 = "i-bp1b3a43b6c7sfffcbsp"
      ~ password                           = (sensitive value)
        tags                               = {
            "Created"     = "Terraform"
            "Name"        = "demo_instance_004"
            "application" = "online-shop"
            "env"         = "pre"
        }
        # (30 unchanged attributes hidden)
    }

Plan: 0 to add, 4 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.

其中有4个change。

destroy

而如果依然不符合预期,或者你想释放或销毁所有terraform创建的资源。可通过terraform destory命令摧毁一切。为了确保销毁过程符合预期,你可以通过terraform plan -destroy命令来查看销毁的执行计划,如下:

shanyao:terraform-ata shanyao$ terraform plan -destroy
alicloud_vpc.vpc: Refreshing state... [id=vpc-bp13jb0hn8gvlk7frvngl]
data.alicloud_images.debian: Reading...
alicloud_security_group.default: Refreshing state... [id=sg-bp14g56wck3nk5qlvlyl]
alicloud_vswitch.vsw: Refreshing state... [id=vsw-bp14yu7pmsg7zkkvry3vb]
alicloud_security_group_rule.allow_all_tcp: Refreshing state... [id=sg-bp14g56wck3nk5qlvlyl:ingress:tcp:1/65535:intranet:0.0.0.0/0:accept:1]
data.alicloud_images.debian: Read complete after 2s [id=4116092999]
module.tf-instances.alicloud_instance.this[3]: Refreshing state... [id=i-bp1b3a43b6c7sfffcbsp]
module.tf-instances.alicloud_instance.this[2]: Refreshing state... [id=i-bp176ekeo6unrcunczdx]
module.tf-instances.alicloud_instance.this[1]: Refreshing state... [id=i-bp1dtiqbjgjinghn478b]
module.tf-instances.alicloud_instance.this[0]: Refreshing state... [id=i-bp15veru9k3viz048x9f]

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:

  # alicloud_security_group.default will be destroyed
  - resource "alicloud_security_group" "default" {
      - id                  = "sg-bp14g56wck3nk5qlvlyl" -> null
      - inner_access        = true -> null
      - inner_access_policy = "Accept" -> null
      - name                = "default" -> null
      - security_group_type = "normal" -> null
      - tags                = {} -> null
      - vpc_id              = "vpc-bp13jb0hn8gvlk7frvngl" -> null
    }

  # alicloud_security_group_rule.allow_all_tcp will be destroyed
  - resource "alicloud_security_group_rule" "allow_all_tcp" {
      - cidr_ip           = "0.0.0.0/0" -> null
      - id                = "sg-bp14g56wck3nk5qlvlyl:ingress:tcp:1/65535:intranet:0.0.0.0/0:accept:1" -> null
      - ip_protocol       = "tcp" -> null
      - nic_type          = "intranet" -> null
      - policy            = "accept" -> null
      - port_range        = "1/65535" -> null
      - priority          = 1 -> null
      - security_group_id = "sg-bp14g56wck3nk5qlvlyl" -> null
      - type              = "ingress" -> null
    }

  # alicloud_vpc.vpc will be destroyed
  - resource "alicloud_vpc" "vpc" {
      - cidr_block            = "172.16.0.0/12" -> null
      - id                    = "vpc-bp13jb0hn8gvlk7frvngl" -> null
      - name                  = "vpc_tf_demo" -> null
      - resource_group_id     = "rg-acfm3gyq7fqm2qa" -> null
      - route_table_id        = "vtb-bp1kbkehf4cm5wb1y6t8n" -> null
      - router_id             = "vrt-bp1pqrbr6ylqioadjdkee" -> null
      - router_table_id       = "vtb-bp1kbkehf4cm5wb1y6t8n" -> null
      - secondary_cidr_blocks = [] -> null
      - status                = "Available" -> null
      - user_cidrs            = [] -> null
      - vpc_name              = "vpc_tf_demo" -> null
    }

  # alicloud_vswitch.vsw will be destroyed
  - resource "alicloud_vswitch" "vsw" {
      - availability_zone = "cn-hangzhou-h" -> null
      - cidr_block        = "172.16.0.0/21" -> null
      - id                = "vsw-bp14yu7pmsg7zkkvry3vb" -> null
      - status            = "Available" -> null
      - tags              = {} -> null
      - vpc_id            = "vpc-bp13jb0hn8gvlk7frvngl" -> null
      - zone_id           = "cn-hangzhou-h" -> null
    }

  # module.tf-instances.alicloud_instance.this[0] will be destroyed
  - resource "alicloud_instance" "this" {
      - availability_zone                  = "cn-hangzhou-h" -> null
      - deletion_protection                = false -> null
      - description                        = "An ECS instance came from terraform-alicloud-modules/ecs-instance" -> null
      - dry_run                            = false -> null
      - host_name                          = "tf-demo001" -> null
      - id                                 = "i-bp15veru9k3viz048x9f" -> null
      - image_id                           = "debian_8_09_64_20G_alibase_20170824.vhd" -> null
      - instance_charge_type               = "PostPaid" -> null
      - instance_name                      = "demo_instance_001" -> null
      - instance_type                      = "ecs.s6-c1m1.small" -> null
      - internet_charge_type               = "PayByTraffic" -> null
      - internet_max_bandwidth_in          = -1 -> null
      - internet_max_bandwidth_out         = 0 -> null
      - maintenance_action                 = "AutoRecover" -> null
      - maintenance_notify                 = false -> null
      - operator_type                      = "upgrade" -> null
      - password                           = (sensitive value)
      - private_ip                         = "172.16.0.9" -> null
      - secondary_private_ip_address_count = 0 -> null
      - secondary_private_ips              = [] -> null
      - security_enhancement_strategy      = "Active" -> null
      - security_groups                    = [
          - "sg-bp14g56wck3nk5qlvlyl",
        ] -> null
      - spot_price_limit                   = 0 -> null
      - spot_strategy                      = "NoSpot" -> null
      - status                             = "Running" -> null
      - stopped_mode                       = "Not-applicable" -> null
      - subnet_id                          = "vsw-bp14yu7pmsg7zkkvry3vb" -> null
      - system_disk_category               = "cloud_ssd" -> null
      - system_disk_encrypted              = false -> null
      - system_disk_size                   = 20 -> null
      - tags                               = {
          - "Created"     = "Terraform"
          - "Name"        = "demo_instance_001"
          - "application" = "online-shop"
          - "env"         = "pre"
        } -> null
      - volume_tags                        = {
          - "Name" = "demo_instance_001"
        } -> null
      - vswitch_id                         = "vsw-bp14yu7pmsg7zkkvry3vb" -> null
    }

  # module.tf-instances.alicloud_instance.this[1] will be destroyed
  - resource "alicloud_instance" "this" {
      - availability_zone                  = "cn-hangzhou-h" -> null
      - deletion_protection                = false -> null
      - description                        = "An ECS instance came from terraform-alicloud-modules/ecs-instance" -> null
      - dry_run                            = false -> null
      - host_name                          = "tf-demo002" -> null
      - id                                 = "i-bp1dtiqbjgjinghn478b" -> null
      - image_id                           = "debian_8_09_64_20G_alibase_20170824.vhd" -> null
      - instance_charge_type               = "PostPaid" -> null
      - instance_name                      = "demo_instance_002" -> null
      - instance_type                      = "ecs.s6-c1m1.small" -> null
      - internet_charge_type               = "PayByTraffic" -> null
      - internet_max_bandwidth_in          = -1 -> null
      - internet_max_bandwidth_out         = 0 -> null
      - maintenance_action                 = "AutoRecover" -> null
      - maintenance_notify                 = false -> null
      - operator_type                      = "upgrade" -> null
      - password                           = (sensitive value)
      - private_ip                         = "172.16.0.10" -> null
      - secondary_private_ip_address_count = 0 -> null
      - secondary_private_ips              = [] -> null
      - security_enhancement_strategy      = "Active" -> null
      - security_groups                    = [
          - "sg-bp14g56wck3nk5qlvlyl",
        ] -> null
      - spot_price_limit                   = 0 -> null
      - spot_strategy                      = "NoSpot" -> null
      - status                             = "Running" -> null
      - stopped_mode                       = "Not-applicable" -> null
      - subnet_id                          = "vsw-bp14yu7pmsg7zkkvry3vb" -> null
      - system_disk_category               = "cloud_ssd" -> null
      - system_disk_encrypted              = false -> null
      - system_disk_size                   = 20 -> null
      - tags                               = {
          - "Created"     = "Terraform"
          - "Name"        = "demo_instance_002"
          - "application" = "online-shop"
          - "env"         = "pre"
        } -> null
      - volume_tags                        = {
          - "Name" = "demo_instance_002"
        } -> null
      - vswitch_id                         = "vsw-bp14yu7pmsg7zkkvry3vb" -> null
    }

  # module.tf-instances.alicloud_instance.this[2] will be destroyed
  - resource "alicloud_instance" "this" {
      - availability_zone                  = "cn-hangzhou-h" -> null
      - deletion_protection                = false -> null
      - description                        = "An ECS instance came from terraform-alicloud-modules/ecs-instance" -> null
      - dry_run                            = false -> null
      - host_name                          = "tf-demo003" -> null
      - id                                 = "i-bp176ekeo6unrcunczdx" -> null
      - image_id                           = "debian_8_09_64_20G_alibase_20170824.vhd" -> null
      - instance_charge_type               = "PostPaid" -> null
      - instance_name                      = "demo_instance_003" -> null
      - instance_type                      = "ecs.s6-c1m1.small" -> null
      - internet_charge_type               = "PayByTraffic" -> null
      - internet_max_bandwidth_in          = -1 -> null
      - internet_max_bandwidth_out         = 0 -> null
      - maintenance_action                 = "AutoRecover" -> null
      - maintenance_notify                 = false -> null
      - operator_type                      = "upgrade" -> null
      - password                           = (sensitive value)
      - private_ip                         = "172.16.0.11" -> null
      - secondary_private_ip_address_count = 0 -> null
      - secondary_private_ips              = [] -> null
      - security_enhancement_strategy      = "Active" -> null
      - security_groups                    = [
          - "sg-bp14g56wck3nk5qlvlyl",
        ] -> null
      - spot_price_limit                   = 0 -> null
      - spot_strategy                      = "NoSpot" -> null
      - status                             = "Running" -> null
      - stopped_mode                       = "Not-applicable" -> null
      - subnet_id                          = "vsw-bp14yu7pmsg7zkkvry3vb" -> null
      - system_disk_category               = "cloud_ssd" -> null
      - system_disk_encrypted              = false -> null
      - system_disk_size                   = 20 -> null
      - tags                               = {
          - "Created"     = "Terraform"
          - "Name"        = "demo_instance_003"
          - "application" = "online-shop"
          - "env"         = "pre"
        } -> null
      - volume_tags                        = {
          - "Name" = "demo_instance_003"
        } -> null
      - vswitch_id                         = "vsw-bp14yu7pmsg7zkkvry3vb" -> null
    }

  # module.tf-instances.alicloud_instance.this[3] will be destroyed
  - resource "alicloud_instance" "this" {
      - availability_zone                  = "cn-hangzhou-h" -> null
      - deletion_protection                = false -> null
      - description                        = "An ECS instance came from terraform-alicloud-modules/ecs-instance" -> null
      - dry_run                            = false -> null
      - host_name                          = "tf-demo004" -> null
      - id                                 = "i-bp1b3a43b6c7sfffcbsp" -> null
      - image_id                           = "debian_8_09_64_20G_alibase_20170824.vhd" -> null
      - instance_charge_type               = "PostPaid" -> null
      - instance_name                      = "demo_instance_004" -> null
      - instance_type                      = "ecs.s6-c1m1.small" -> null
      - internet_charge_type               = "PayByTraffic" -> null
      - internet_max_bandwidth_in          = -1 -> null
      - internet_max_bandwidth_out         = 0 -> null
      - maintenance_action                 = "AutoRecover" -> null
      - maintenance_notify                 = false -> null
      - operator_type                      = "upgrade" -> null
      - password                           = (sensitive value)
      - private_ip                         = "172.16.0.12" -> null
      - secondary_private_ip_address_count = 0 -> null
      - secondary_private_ips              = [] -> null
      - security_enhancement_strategy      = "Active" -> null
      - security_groups                    = [
          - "sg-bp14g56wck3nk5qlvlyl",
        ] -> null
      - spot_price_limit                   = 0 -> null
      - spot_strategy                      = "NoSpot" -> null
      - status                             = "Running" -> null
      - stopped_mode                       = "Not-applicable" -> null
      - subnet_id                          = "vsw-bp14yu7pmsg7zkkvry3vb" -> null
      - system_disk_category               = "cloud_ssd" -> null
      - system_disk_encrypted              = false -> null
      - system_disk_size                   = 20 -> null
      - tags                               = {
          - "Created"     = "Terraform"
          - "Name"        = "demo_instance_004"
          - "application" = "online-shop"
          - "env"         = "pre"
        } -> null
      - volume_tags                        = {
          - "Name" = "demo_instance_004"
        } -> null
      - vswitch_id                         = "vsw-bp14yu7pmsg7zkkvry3vb" -> null
    }

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

可见,这里列出了需要销毁的8个刚创建的资源。你可以在完成使用后,再及时释放这些资源。

配置公网访问

如上所示,我们顺利地创建了4台符合要求的ECS实例(已完成要求a)。但是这些实例默认是不能访问公网的,如果你确有需求需要访问公网,应该怎么办呢?

还需要依次申请nat网关、eip公网IP地址,将nat与eip绑定,并配置snat entry规则。你可以新增以下resource资源配置:

resource "alicloud_nat_gateway" "nat" {
  depends_on       = [alicloud_vswitch.vsw]
  vpc_id           = alicloud_vpc.vpc.id
  nat_gateway_name = "natGateway-tf"
  payment_type     = "PayAsYouGo"
  vswitch_id       = alicloud_vswitch.vsw.id
  nat_type         = "Enhanced"
}

resource "alicloud_eip_address" "eip" {
  address_name         = "eip-tf"
  isp                  = "BGP"
  internet_charge_type = "PayByBandwidth"
  payment_type         = "PayAsYouGo"
  bandwidth            = 1
}

resource "alicloud_eip_association" "eip_nat" {
  allocation_id = alicloud_eip_address.eip.id
  instance_id   = alicloud_nat_gateway.nat.id
}

resource "alicloud_snat_entry" "snat" {
  depends_on        = [alicloud_eip_association.eip_nat]
  snat_table_id     = alicloud_nat_gateway.nat.snat_table_ids
  source_vswitch_id = alicloud_vswitch.vsw.id
  snat_ip           = join(",", alicloud_eip_address.eip.*.ip_address)
}

plan执行结果如下,将提示我们新增以上4个资源:

shanyao:terraform-ata shanyao$ terraform plan
data.alicloud_images.debian: Reading...
alicloud_vpc.vpc: Refreshing state... [id=vpc-bp13jb0hn8gvlk7frvngl]
alicloud_security_group.default: Refreshing state... [id=sg-bp14g56wck3nk5qlvlyl]
alicloud_vswitch.vsw: Refreshing state... [id=vsw-bp14yu7pmsg7zkkvry3vb]
alicloud_security_group_rule.allow_all_tcp: Refreshing state... [id=sg-bp14g56wck3nk5qlvlyl:ingress:tcp:1/65535:intranet:0.0.0.0/0:accept:1]
data.alicloud_images.debian: Read complete after 1s [id=4116092999]
module.tf-instances.alicloud_instance.this[3]: Refreshing state... [id=i-bp1b3a43b6c7sfffcbsp]
module.tf-instances.alicloud_instance.this[1]: Refreshing state... [id=i-bp1dtiqbjgjinghn478b]
module.tf-instances.alicloud_instance.this[0]: Refreshing state... [id=i-bp15veru9k3viz048x9f]
module.tf-instances.alicloud_instance.this[2]: Refreshing state... [id=i-bp176ekeo6unrcunczdx]

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:

  # alicloud_eip_address.eip will be created
  + resource "alicloud_eip_address" "eip" {
      + address_name         = "eip-tf"
      + bandwidth            = "1"
      + deletion_protection  = (known after apply)
      + id                   = (known after apply)
      + instance_charge_type = (known after apply)
      + internet_charge_type = "PayByBandwidth"
      + ip_address           = (known after apply)
      + isp                  = "BGP"
      + name                 = (known after apply)
      + payment_type         = "PayAsYouGo"
      + resource_group_id    = (known after apply)
      + status               = (known after apply)
    }

  # alicloud_eip_association.eip_nat will be created
  + resource "alicloud_eip_association" "eip_nat" {
      + allocation_id      = (known after apply)
      + force              = false
      + id                 = (known after apply)
      + instance_id        = (known after apply)
      + instance_type      = (known after apply)
      + private_ip_address = (known after apply)
    }

  # alicloud_nat_gateway.nat will be created
  + resource "alicloud_nat_gateway" "nat" {
      + bandwidth_package_ids = (known after apply)
      + deletion_protection   = (known after apply)
      + eip_bind_mode         = (known after apply)
      + forward_table_ids     = (known after apply)
      + id                    = (known after apply)
      + instance_charge_type  = (known after apply)
      + internet_charge_type  = (known after apply)
      + name                  = (known after apply)
      + nat_gateway_name      = "natGateway-tf"
      + nat_type              = "Enhanced"
      + network_type          = (known after apply)
      + payment_type          = "PayAsYouGo"
      + snat_table_ids        = (known after apply)
      + specification         = (known after apply)
      + status                = (known after apply)
      + vpc_id                = "vpc-bp13jb0hn8gvlk7frvngl"
      + vswitch_id            = "vsw-bp14yu7pmsg7zkkvry3vb"
    }

  # alicloud_snat_entry.snat will be created
  + resource "alicloud_snat_entry" "snat" {
      + id                = (known after apply)
      + snat_entry_id     = (known after apply)
      + snat_ip           = (known after apply)
      + snat_table_id     = (known after apply)
      + source_cidr       = (known after apply)
      + source_vswitch_id = "vsw-bp14yu7pmsg7zkkvry3vb"
      + status            = (known after apply)
    }

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

apply执行后,将增加以上4个资源。

此时,我们登录ECS实例试验,可验证已经能够成功访问公网了(已完成要求b)。

执行自定义脚本

有时,我们会有进一步的个性化需求,比如我们想完成环境初始化之后,再执行一些自定义的脚本,如创建一些目录、安装nginx等软件、或者执行测试脚本。那该如何实现呢?

可喜的是,terraform提供了user-data机制,借助于底层虚拟机的cloud-init功能,user-data允许我们在虚拟机实例第一次启动时执行一些自定义的脚本来完成初始化操作。阿里云ECS的user_data说明如下:

user_data - (Optional) User-defined data to customize the startup behaviors of an ECS instance and to pass data into an ECS instance. From version 1.60.0, it can be update in-place. If updated, the instance will reboot to make the change take effect. Note: Not all of changes will take effect and it depends on cloud-init module type.

其中特别需要注意的一点是脚本什么执行呢?从原则上来说,user-data只能在实例初始化启动阶段执行,这也就意味着在ECS整个生命周期当中,仅有初始化创建阶段脚本能够被执行,而不能立即生效,也不能通过重启来执行。虽然在较新的版本中,有些更新可以update in-place,但是依然是少数,需要认真辨别。通常来讲,需要destroy掉所有资源,再重新apply所有资源才能使得user_data里的脚本能够被执行。

针对我们的例子,我们可以创建脚本文件cron_task.sh,如下:

#!/bin/bash
dir='/home/tf'
if [ ! -d "$dir" ];then
  mkdir -p $dir
fi
echo "#!/bin/bash
urls=(
'https://www.baidu.com/'
'https://www.youdao.com/'
'https://github.com/terraform-alicloud-modules/terraform-alicloud-ecs-instance'
)
for url in \${urls[*]}
do
  curl --connect-timeout 10 -m 10 -s \$url >> $dir/curl.log
  sleep 5s
done
echo 'curl finished!' >> $dir/curl.log
"> $dir/cron_task.sh

chmod +x $dir/cron_task.sh

echo "0 *    * * *   root    $dir/cron_task.sh">>/etc/crontab
service cron reload
echo 'crontab task initialized successfully, and it will run hourly.' >> $dir/cron.log

并定义local变量,修改instance module,通过‘user_data = local.user_data’来指定user_data,如下:

locals {
  user_data = file("cron_task.sh")
}

module "tf-instances" {
  source                      = "alibaba/ecs-instance/alicloud"
  region                      = var.region
  number_of_instances         = var.instances_number
  vswitch_id                  = alicloud_vswitch.vsw.id
  group_ids                   = [alicloud_security_group.default.id]
  private_ips                 = ["172.16.0.9", "172.16.0.10", "172.16.0.11", "172.16.0.12"]
  image_id                    = data.alicloud_images.debian.ids.0
  instance_type               = "ecs.s6-c1m1.small"
  associate_public_ip_address = false
  instance_name               = "demo_instance_"
  host_name                   = "tf-demo"
  internet_charge_type        = "PayByTraffic"
  password                    = "password123"
  system_disk_category        = "cloud_ssd"
  system_disk_size            = 20
  user_data                   = local.user_data


  tags = {
    Created     = "Terraform"
    env         = "pre"
    application = "online-shop"
  }
}

如前所述,我们可以通过plan,查看执行计划如下:

shanyao:terraform-ata shanyao$ terraform plan
data.alicloud_images.debian: Reading...
alicloud_vpc.vpc: Refreshing state... [id=vpc-bp13jb0hn8gvlk7frvngl]
alicloud_eip_address.eip: Refreshing state... [id=eip-bp1kx6bn9caoxrlpjldlx]
alicloud_security_group.default: Refreshing state... [id=sg-bp14g56wck3nk5qlvlyl]
alicloud_vswitch.vsw: Refreshing state... [id=vsw-bp14yu7pmsg7zkkvry3vb]
alicloud_nat_gateway.nat: Refreshing state... [id=ngw-bp1383r6vru07uhxnnw1c]
alicloud_eip_association.eip_nat: Refreshing state... [id=eip-bp1kx6bn9caoxrlpjldlx:ngw-bp1383r6vru07uhxnnw1c]
alicloud_security_group_rule.allow_all_tcp: Refreshing state... [id=sg-bp14g56wck3nk5qlvlyl:ingress:tcp:1/65535:intranet:0.0.0.0/0:accept:1]
alicloud_snat_entry.snat: Refreshing state... [id=stb-bp11moovtq98n7zjrgrl3:snat-bp1n6jzrjbl8130my7jbj]
data.alicloud_images.debian: Read complete after 1s [id=4116092999]
module.tf-instances.alicloud_instance.this[1]: Refreshing state... [id=i-bp1dtiqbjgjinghn478b]
module.tf-instances.alicloud_instance.this[2]: Refreshing state... [id=i-bp176ekeo6unrcunczdx]
module.tf-instances.alicloud_instance.this[3]: Refreshing state... [id=i-bp1b3a43b6c7sfffcbsp]
module.tf-instances.alicloud_instance.this[0]: Refreshing state... [id=i-bp15veru9k3viz048x9f]

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:

  # module.tf-instances.alicloud_instance.this[0] will be updated in-place
  ~ resource "alicloud_instance" "this" {
        id                                 = "i-bp15veru9k3viz048x9f"
        tags                               = {
            "Created"     = "Terraform"
            "Name"        = "demo_instance_001"
            "application" = "online-shop"
            "env"         = "pre"
        }
      + user_data                          = <<-EOT
            #!/bin/bash
            dir='/home/tf'
            if [ ! -d "$dir" ];then
              mkdir -p $dir
            fi
            echo "#!/bin/bash
            urls=(
            'https://www.baidu.com/'
            'https://www.youdao.com/'
            'https://github.com/terraform-alicloud-modules/terraform-alicloud-ecs-instance'
            )
            for url in \${urls[*]}
            do
              curl --connect-timeout 10 -m 10 -s \$url >> $dir/curl.log
              sleep 5s
            done
            echo 'curl finished!' >> $dir/curl.log
            "> $dir/cron_task.sh
            
            chmod +x $dir/cron_task.sh
            
            echo "0 *    * * *   root    $dir/cron_task.sh">>/etc/crontab
            service cron reload
            echo 'crontab task initialized successfully, and it will run hourly.' >> $dir/cron.log
        EOT
        # (31 unchanged attributes hidden)
    }

  # module.tf-instances.alicloud_instance.this[1] will be updated in-place
  ~ resource "alicloud_instance" "this" {
        id                                 = "i-bp1dtiqbjgjinghn478b"
        tags                               = {
            "Created"     = "Terraform"
            "Name"        = "demo_instance_002"
            "application" = "online-shop"
            "env"         = "pre"
        }
      + user_data                          = <<-EOT
            #!/bin/bash
            dir='/home/tf'
            if [ ! -d "$dir" ];then
              mkdir -p $dir
            fi
            echo "#!/bin/bash
            urls=(
            'https://www.baidu.com/'
            'https://www.youdao.com/'
            'https://github.com/terraform-alicloud-modules/terraform-alicloud-ecs-instance'
            )
            for url in \${urls[*]}
            do
              curl --connect-timeout 10 -m 10 -s \$url >> $dir/curl.log
              sleep 5s
            done
            echo 'curl finished!' >> $dir/curl.log
            "> $dir/cron_task.sh
            
            chmod +x $dir/cron_task.sh
            
            echo "0 *    * * *   root    $dir/cron_task.sh">>/etc/crontab
            service cron reload
            echo 'crontab task initialized successfully, and it will run hourly.' >> $dir/cron.log
        EOT
        # (31 unchanged attributes hidden)
    }

  # module.tf-instances.alicloud_instance.this[2] will be updated in-place
  ~ resource "alicloud_instance" "this" {
        id                                 = "i-bp176ekeo6unrcunczdx"
        tags                               = {
            "Created"     = "Terraform"
            "Name"        = "demo_instance_003"
            "application" = "online-shop"
            "env"         = "pre"
        }
      + user_data                          = <<-EOT
            #!/bin/bash
            dir='/home/tf'
            if [ ! -d "$dir" ];then
              mkdir -p $dir
            fi
            echo "#!/bin/bash
            urls=(
            'https://www.baidu.com/'
            'https://www.youdao.com/'
            'https://github.com/terraform-alicloud-modules/terraform-alicloud-ecs-instance'
            )
            for url in \${urls[*]}
            do
              curl --connect-timeout 10 -m 10 -s \$url >> $dir/curl.log
              sleep 5s
            done
            echo 'curl finished!' >> $dir/curl.log
            "> $dir/cron_task.sh
            
            chmod +x $dir/cron_task.sh
            
            echo "0 *    * * *   root    $dir/cron_task.sh">>/etc/crontab
            service cron reload
            echo 'crontab task initialized successfully, and it will run hourly.' >> $dir/cron.log
        EOT
        # (31 unchanged attributes hidden)
    }

  # module.tf-instances.alicloud_instance.this[3] will be updated in-place
  ~ resource "alicloud_instance" "this" {
        id                                 = "i-bp1b3a43b6c7sfffcbsp"
        tags                               = {
            "Created"     = "Terraform"
            "Name"        = "demo_instance_004"
            "application" = "online-shop"
            "env"         = "pre"
        }
      + user_data                          = <<-EOT
            #!/bin/bash
            dir='/home/tf'
            if [ ! -d "$dir" ];then
              mkdir -p $dir
            fi
            echo "#!/bin/bash
            urls=(
            'https://www.baidu.com/'
            'https://www.youdao.com/'
            'https://github.com/terraform-alicloud-modules/terraform-alicloud-ecs-instance'
            )
            for url in \${urls[*]}
            do
              curl --connect-timeout 10 -m 10 -s \$url >> $dir/curl.log
              sleep 5s
            done
            echo 'curl finished!' >> $dir/curl.log
            "> $dir/cron_task.sh
            
            chmod +x $dir/cron_task.sh
            
            echo "0 *    * * *   root    $dir/cron_task.sh">>/etc/crontab
            service cron reload
            echo 'crontab task initialized successfully, and it will run hourly.' >> $dir/cron.log
        EOT
        # (31 unchanged attributes hidden)
    }

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

但需要注意的是,我们只有通过先用terraformform destroy掉所有资源,再重新执行terraform apply申请所有资源,才可以使user_data里面的脚本真正被调用执行。

完整的terraform脚本如下(至此已完成全部要求abc):

variable "profile" {
  default = "default"
}

variable "region" {
  default = "cn-hangzhou"
}

variable "zone_id" {
  default = "cn-hangzhou-h"
}

variable "instances_number" {
  default = 4
}

provider "alicloud" {
  region  = var.region
  profile = var.profile
}

resource "alicloud_vpc" "vpc" {
  vpc_name   = "vpc_tf_demo"
  cidr_block = "172.16.0.0/12"
}

resource "alicloud_vswitch" "vsw" {
  vpc_id     = alicloud_vpc.vpc.id
  cidr_block = "172.16.0.0/21"
  zone_id    = var.zone_id
}

resource "alicloud_security_group" "default" {
  name   = "default"
  vpc_id = alicloud_vpc.vpc.id
}

resource "alicloud_security_group_rule" "allow_all_tcp" {
  type              = "ingress"
  ip_protocol       = "tcp"
  nic_type          = "intranet"
  policy            = "accept"
  port_range        = "1/65535"
  priority          = 1
  security_group_id = alicloud_security_group.default.id
  cidr_ip           = "0.0.0.0/0"
}

data "alicloud_images" "debian" {
  most_recent = false
  name_regex  = "^debian_8_09_64_20G_alibase_20170824.vhd"
}

resource "alicloud_nat_gateway" "nat" {
  depends_on       = [alicloud_vswitch.vsw]
  vpc_id           = alicloud_vpc.vpc.id
  nat_gateway_name = "natGateway-tf"
  payment_type     = "PayAsYouGo"
  vswitch_id       = alicloud_vswitch.vsw.id
  nat_type         = "Enhanced"
}

resource "alicloud_eip_address" "eip" {
  address_name         = "eip-tf"
  isp                  = "BGP"
  internet_charge_type = "PayByBandwidth"
  payment_type         = "PayAsYouGo"
  bandwidth            = 1
}

resource "alicloud_eip_association" "eip_nat" {
  allocation_id = alicloud_eip_address.eip.id
  instance_id   = alicloud_nat_gateway.nat.id
}

resource "alicloud_snat_entry" "snat" {
  depends_on        = [alicloud_eip_association.eip_nat]
  snat_table_id     = alicloud_nat_gateway.nat.snat_table_ids
  source_vswitch_id = alicloud_vswitch.vsw.id
  snat_ip           = join(",", alicloud_eip_address.eip.*.ip_address)
}

locals {
  user_data = file("cron_task.sh")
}

module "tf-instances" {
  source                      = "alibaba/ecs-instance/alicloud"
  region                      = var.region
  number_of_instances         = var.instances_number
  vswitch_id                  = alicloud_vswitch.vsw.id
  group_ids                   = [alicloud_security_group.default.id]
  private_ips                 = ["172.16.0.9", "172.16.0.10", "172.16.0.11", "172.16.0.12"]
  image_id                    = data.alicloud_images.debian.ids.0
  instance_type               = "ecs.s6-c1m1.small"
  associate_public_ip_address = false
  instance_name               = "demo_instance_"
  host_name                   = "tf-demo"
  internet_charge_type        = "PayByTraffic"
  password                    = "password123"
  system_disk_category        = "cloud_ssd"
  system_disk_size            = 20
  user_data                   = local.user_data


  tags = {
    Created     = "Terraform"
    env         = "pre"
    application = "online-shop"
  }
}

总结

本文以真实的案例,向大家展示了如何使用terraform创建、修改、删除云上环境。示例当中,包含了如何通过简单的命令创建多台ECS实例,也简要介绍了如何解决经常遇到的怎么开通公网访问;在进阶示例中,又简要说明了如何通过user_data在实例初始化阶段执行自定义脚本。Terraform是一个简单且强大的IaC工具,本文简单介绍,希望能对大家有所帮助,更多复杂的功能等待大家去探索。

参考文档

https://www.terraform.io/

https://www.terraform.io/cli/commands/init

https://registry.terraform.io/providers/aliyun/alicloud/latest/docs/resources/instance

https://registry.terraform.io/modules/alibaba/ecs-instance/alicloud/latest

https://registry.terraform.io/providers/aliyun/alicloud/latest/docs/resources/nat_gateway

https://registry.terraform.io/providers/aliyun/alicloud/latest/docs/resources/eip_address

https://registry.terraform.io/providers/aliyun/alicloud/latest/docs/resources/eip_association

https://registry.terraform.io/providers/aliyun/alicloud/latest/docs/resources/snat_entry

相关实践学习
一小时快速掌握 SQL 语法
本实验带您学习SQL的基础语法,快速入门SQL。
7天玩转云服务器
云服务器ECS(Elastic Compute Service)是一种弹性可伸缩的计算服务,可降低 IT 成本,提升运维效率。本课程手把手带你了解ECS、掌握基本操作、动手实操快照管理、镜像管理等。了解产品详情:&nbsp;https://www.aliyun.com/product/ecs
目录
相关文章
|
1天前
|
运维 监控 安全
构建高效自动化运维体系的五大策略
【4月更文挑战第27天】在数字化转型的浪潮中,企业对于IT系统的稳定性和敏捷性要求日益增高。自动化运维作为提升效率、降低错误率、实现快速响应的关键技术手段,已经成为现代IT管理的重要组成部分。本文将探讨构建一个高效自动化运维体系的五大策略,包括基础设施即代码(IaC)的应用、监控与告警系统的集成、持续集成和持续部署(CI/CD)的实践、日志管理和分析以及灾难恢复计划的自动化,旨在为运维团队提供一条清晰的道路图,以支持他们在不断变化的技术环境中保持竞争力。
|
1天前
|
jenkins 测试技术 持续交付
深入探索软件测试中的持续集成与自动化测试实践
【4月更文挑战第27天】 在当今软件开发的快速迭代过程中,持续集成(CI)和自动化测试已成为确保代码质量和加快交付速度的关键因素。本文将探讨如何通过实施持续集成流程,并结合自动化测试策略来优化软件测试工作。我们将分析持续集成的原理、自动化测试的最佳实践以及如何将这些方法应用于实际项目中,旨在为读者提供一套完整的解决方案,以提高软件项目的效率和质量。
10 3
|
1天前
|
Web App开发 IDE 测试技术
深入理解自动化测试框架Selenium的设计与实践
【4月更文挑战第27天】在软件开发周期中,确保代码质量和功能正确性至关重要。随着敏捷开发的普及和持续集成/持续部署(CI/CD)的实践,自动化测试已成为现代开发工作流程的核心部分。本文将探讨一个广泛使用的开源自动化测试工具——Selenium,并剖析其设计原理、架构以及在实际中的应用。我们将通过具体案例分析,展示如何有效利用Selenium进行跨浏览器测试,并讨论在真实环境中可能遇到的挑战及解决方案。
|
3天前
|
运维 监控 安全
构建高效自动化运维体系:策略与实践
【4月更文挑战第25天】在数字化转型的浪潮中,企业IT基础设施日趋复杂多变,传统的手动运维模式已难以满足快速响应和高效管理的需求。本文探讨了构建一个高效自动化运维体系的关键环节,并结合实际案例分析,提出了一系列切实可行的策略与实践方法。文章着重分析了自动化工具选择、流程设计优化以及持续监控的重要性,并讨论了如何通过这些手段降低运维成本,提升系统稳定性和安全性。
|
3天前
|
人工智能 运维 监控
构建高效自动化运维体系的五大关键步骤
【4月更文挑战第25天】 在现代IT架构的复杂多变环境中,传统的手动运维方式已无法满足快速迭代与稳定性的双重要求。本文将深入探讨构建一个高效自动化运维体系的关键步骤,涵盖从工具选型到流程优化的全方位考量。通过引入自动化工具、实施标准化流程、建立监控预警机制、持续集成与部署以及文档化管理,组织能够实现运维效率的显著提升,确保系统的稳定性和可靠性。
7 0
|
3天前
|
存储 运维 Kubernetes
构建高效自动化运维体系:Ansible与Kubernetes的协同策略
【4月更文挑战第25天】 在当今快速迭代的软件开发过程中,自动化运维已成为提升效率、保证一致性和降低人为错误的关键。本文将探讨如何利用Ansible作为配置管理工具,以及Kubernetes作为容器编排系统,共同构建一个高效、可靠的自动化运维体系。文章首先概述了自动化运维的基本概念及其重要性,随后详细分析了Ansible与Kubernetes在自动化流程中的作用与优势,并通过一系列实践案例,展示了两者如何协同工作以优化部署、扩缩容和灾难恢复等关键运维任务。最后,文中还讨论了在实际应用中可能遇到的挑战及相应的解决策略,为读者提供了一套完整的自动化运维解决方案参考。
|
12天前
|
敏捷开发 监控 前端开发
深入理解自动化测试框架Selenium的架构与实践
【4月更文挑战第16天】 在现代软件开发过程中,自动化测试已成为确保产品质量和加快迭代速度的关键手段。Selenium作为一种广泛使用的自动化测试工具,其开源、跨平台的特性使得它成为业界的首选之一。本文旨在剖析Selenium的核心架构,并结合实际案例探讨其在复杂Web应用测试中的高效实践方法。通过详细解读Selenium组件间的交互机制以及如何优化测试脚本,我们希望为读者提供深入理解Selenium并有效运用于日常测试工作的参考。
|
12天前
|
自然语言处理 测试技术 API
深入理解自动化测试框架Selenium的设计理念与实践
【4月更文挑战第15天】 在现代软件开发过程中,自动化测试已成为确保产品质量和加速迭代的关键手段。Selenium作为一种广泛使用的自动化测试框架,提供了对多种浏览器和平台的支持,极大地促进了Web应用的功能测试。本文旨在剖析Selenium的核心设计理念,探讨其在实际项目中的应用,并指出常见的误区及最佳实践,以期帮助测试工程师更高效地利用Selenium进行测试工作。
|
13天前
|
运维 Kubernetes Devops
构建高效自动化运维体系:DevOps与容器技术融合实践
【4月更文挑战第15天】 在当今快速发展的信息技术时代,传统的IT运维模式已难以满足业务敏捷性的需求。本文旨在探讨如何通过整合DevOps理念和容器技术来构建一个高效的自动化运维体系。文章将详细阐述DevOps的核心原则、容器技术的基础知识,以及两者结合的优势。此外,文中还将分享一系列实践经验,包括持续集成/持续部署(CI/CD)流程的搭建、微服务架构的应用,以及监控和日志管理策略的优化,以期帮助企业实现快速、可靠且安全的软件交付过程。
|
13天前
|
测试技术 持续交付 Docker
Django中的自动化部署与持续集成实践
【4月更文挑战第15天】本文介绍了Django项目中自动化部署与持续集成的实践方法。自动化部署通过选择Ansible、Fabric或Docker等工具,编写部署脚本,配置持续集成工具(如Jenkins、GitLab CI),确保服务器环境一致,实现快速应用上线。持续集成则涉及配置版本控制系统,设置自动化构建和测试,编写全面的测试用例,集成代码质量检查工具,并配置通知机制,以提升代码质量和开发效率。这两者结合能有效提升项目的迭代速度和可靠性。

热门文章

最新文章

推荐镜像

更多