yongz
发布于 2025-12-27 / 1 阅读
0

Terraform 简单使用

Terraform 简单使用

朋友写工具时,分享了我一篇 Terraform 工具介绍,别说还真不错。

参考文章:

1 Terraform 介绍

HashiCorp Terraform 是一款基础设施即代码 (IaC) 工具,它允许您使用易于阅读的配置文件定义云端和本地资源,这些配置文件可以进行版本控制、重用和共享。之后,您可以使用一致的工作流程来配置和管理整个基础设施的整个生命周期。Terraform 可以管理计算、存储和网络资源等底层组件,以及 DNS 条目和 SaaS 功能等高层组件。

Terraform 通过应用程序编程接口(API)在云平台和其他服务上创建和管理资源。服务提供商使 Terraform 能够与几乎任何具有可访问 API 的平台或服务协同工作。

intro-terraform-apis

Terraform 的核心工作流程包含三个阶段:

  • Write: 可以定义资源,这些资源可能跨越多个云提供商和服务。例如,可以创建一个配置,将应用程序部署到虚拟私有云 (VPC) 网络中的虚拟机上,该网络具有安全组和负载均衡器。

  • Plan: Terraform 会创建一个执行计划,根据现有基础设施和配置来描述它将创建、更新或销毁的基础设施。

  • Apply: Terraform 将按正确的顺序执行建议的操作,并遵守所有资源依赖关系。例如,如果更新了 VPC 的属性并更改了该 VPC 中的虚拟机数量,Terraform 会先重新创建 VPC,然后再扩展虚拟机。

intro-terraform-workflow

2 Terraform 配置

2.1 Terraform 安装

访问 Terraform 官网,下载适合的 Windows 操作系统版本的 Terraform 二进制文件并解压:

image-20251227173100791

解压完成后,为后续方便使用将文件放在 D 盘(D:\Program Files):

image-20251227173352301

配置环境变量,将 Terraform 的安装路径(D:\Program Files\terraform_1.14.3_windows_amd64)添加到系统环境变量 Path 中。在桌面右键单击此电脑,选择属性 -> 高级系统设置 -> 环境变量 -> 系统变量/用户变量:

image-20251227173440203

验证安装成功,同时按下 Win+R 键,输入 cmd 单击确定,打开命令提示符。执行 terraform --version,如果出现以下信息则表示安装成功。

image-20251227173517937

2.2 Terraform 配置文件

配置文件是 Terraform 的核心,用于定义在云端或本地部署的基础设施资源,如 VPC、ECS、OSS 等。

创建一个新的文件夹,例如命名为 Aliyun-ECS,并在该文件夹下创建一个 Terraform 配置文件,例如配置文件名称为 main.tf

image-20251227173651643
image-20251227173710208

[!Warning]

为每个 Terraform 项目创建独立执行目录可以确保资源组织清晰,避免状态文件混淆,便于版本控制和团队协作,同时有利于实现环境隔离和模块化管理,提高配置管理的可维护性和安全性。

编写 Terraform 配置文件,以创建 ECS 为例。将 ECS 实例及其依赖的资源(VPC,安全组等)以代码化的方式定义到配置文件中。为了方便起见,可以将以下代码复制到 main.tf 中:

[!Warning]

  1. 示例中创建的 ECS 为按量计费,资源创建后将会产生相关费用。

  2. Aliyun 官方文档给的示例用不了,调了我好久。

  3. AK 和 SK 请自行前往 Aliyun 控制台获取。

# 我是直接将 Aliyun 配置写在这里,官方文档是在环境变量里面
provider "alicloud" {
  access_key = "xxxxxx"
  secret_key = "xxxxxx"
  region     = "cn-hangzhou"
}

variable "name" {
  type    = string
  default = "tf-test"
}

# 设置地域
variable "region" {
  type    = string
  default = "cn-beijing"
}

# 公网带宽,当大于0的时候,将为实例分配一个公网。
variable "internet_max_bandwidth_out" {
  type    = number
  default = 10
}

# 设置ECS实例规格
variable "instance_type" {
  type    = string
  default = "ecs.e-c1m1.large"
}

# 设置镜像ID
variable "image_id" {
  default = "ubuntu_18_04_64_20G_alibase_20190624.vhd"
}

# 设置标签
variable "tags" {
  type    = map(string)
  default = {
    From =  "Terraform"
    Usage =  "demo"
  }
}

# 获取zone_id
data "alicloud_zones" "default" {
  available_instance_type = var.instance_type
  available_resource_creation = "VSwitch"
  available_disk_category = "cloud_ssd"
}

# 创建VPC
resource "alicloud_vpc" "vpc" {
  vpc_name   = var.name
  cidr_block = "172.16.0.0/12"
}

# 创建vswitch
resource "alicloud_vswitch" "vsw" {
  vpc_id     = alicloud_vpc.vpc.id
  cidr_block = "172.16.0.0/21"
  zone_id    = data.alicloud_zones.default.zones[0].id
}

# 创建安全组
resource "alicloud_security_group" "default" {
  name   = var.name
  vpc_id = alicloud_vpc.vpc.id
}

# 向安全组添加入方向放行规则
resource "alicloud_security_group_rule" "allow_tcp_22" {
  type              = "ingress"
  ip_protocol       = "tcp"
  nic_type          = "intranet"
  policy            = "accept"
  port_range        = "22/22"
  priority          = 1
  security_group_id = alicloud_security_group.default.id
  cidr_ip           = "0.0.0.0/0"
}

# 创建ECS
resource "alicloud_instance" "instance" {
  availability_zone          = data.alicloud_zones.default.zones.0.id
  security_groups            = alicloud_security_group.default.*.id
  instance_type              = var.instance_type
  # 云盘类型要改
  system_disk_category       = "cloud_essd"
  image_id                   = var.image_id
  instance_name              = var.name
  vswitch_id                 = alicloud_vswitch.vsw.id
  internet_max_bandwidth_out = var.internet_max_bandwidth_out
  tags                       = var.tags
  
  # 提供一个默认的密码,不然要用 Aliyun Cli 去调
  password 				     = "Pass@Aliyun123"
}

output "ecs_id" {
  value = alicloud_instance.instance.id
}

output "ecs_ip" {
  value = alicloud_instance.instance.public_ip
}

2.3 Terraform 初始化与创建资源

Terraform 配置文件编写完成后,需要先初始化工作目录,然后再创建资源。

在 cmd 中切换到所创建的文件夹,然后执行 terraform init 命令进行初始化。terraform init 命令是使用任何 Terraform 配置文件之前必须执行的第一个命令,主要目的是初始化一个 Terraform 工作目录,包括下载必要的阿里云提供商插件以及各种其他记录文件:

image-20251227175143433

[!Warning]

若出现网络延迟等原因造成的 terraform init 超时,导致无法正常下载 Provider 等情况时,可通过配置阿里云镜像站解决。

Terraform Init 加速方案配置:https://help.aliyun.com/zh/terraform/terraform-init-acceleration-solution-configuration

执行 terraform plan 创建一个执行计划,并详细展示了在执行 terraform apply 时将创建、修改或销毁的所有资源信息:

image-20251227175324016

执行 terraform apply 时,将根据 terraform plan 生成的执行计划来创建资源。在创建过程中,需要按照提示输入 yes,以继续创建资源:

image-20251227175440627
image-20251227175507118

使用 SSH 远程连接一下(默认账号是 root):

image-20251227180127407

通过 terraform show 命令查看资源的详细信息。

image-20251227175656013

通过 terraform state list 命令列出所有已创建的资源。

image-20251227175735269

Terraform 在完成资源的创建和修改后,会将资源的状态和属性信息保存在 terraform.tfstate 文件中。

当不再需要所创建的资源时,可以通过 terraform destroy 命令销毁所有已创建的资源:

image-20251227175911772

3 完整的示例配置

上述演示完后,那我想要批量创建或者安装服务呢?这里用豆包生成了一个较为完善的示例配置:

# 定义同时创建的 ECS 示例数量
variable "ecs_instance_count" {
  type    = number                        # 变量类型:数字 
  default = 2                             # 默认创建 2 台 ECS,改 3 就创建 3 台,以此类推
}

# 定义资源前缀
variable "name" {
  type    = string                        # 变量类型:字符串
  default = "TF-Aliyun-Server"            # 默认名称前缀,用于 VPC/安全组/ECS 等资源命名
}

# 设置地域
variable "region" {
  type    = string                        # 变量类型:字符串
  default = "cn-beijing"                  # 默认地域:北京
}

# 公网带宽,当大于 0 的时候,将为实例分配一个公网 IP
variable "internet_max_bandwidth_out" {
  type    = number                        # 变量类型:数字
  default = 10                            # 默认公网出带宽:10 Mbps
}

# 设置 ECS 实例规格
variable "instance_type" {
  type    = string                        # 变量类型:字符串
  default = "ecs.e-c1m1.large"            # 默认实例规格:1 核 2G 共享型
}

# 设置镜像 ID
variable "image_id" {
  type    = string                        # 变量类型:字符串
  default = "ubuntu_18_04_64_20G_alibase_20190624.vhd"  # Ubuntu 18.04 64 位镜像
}

# 设置 ECS 标签
variable "tags" {
  type    = map(string)                   # 变量类型:字符串类型的键值对
  default = {
    From =  "Terraform"                   # 标签:资源来源(Terraform 创建)
    Usage =  "demo"                       # 标签:资源用途(演示)
  }
}

# 阿里云提供商配置
provider "alicloud" {
  # 替换为你的阿里云 AccessKey ID(生产环境建议用环境变量,避免硬编码)
  access_key = "xxxxxx"           # 访问密钥 ID:用于阿里云 API 认证
  # 替换为你的阿里云 AccessKey Secret(生产环境禁止硬编码!)
  secret_key = "xxxxxx"     # 访问密钥 Secret:API 认证密钥
  # 替换为你的阿里云地域(如 cn-hangzhou、cn-beijing 等)
  region     = "cn-hangzhou"                        # 提供商地域:指定 API 调用的地域(优先级高于 region 变量)
}

# 获取可用区信息
data "alicloud_zones" "default" {
  available_instance_type = var.instance_type       # 筛选支持该实例规格的可用区
  available_resource_creation = "VSwitch"           # 筛选支持创建交换机的可用区
  available_disk_category = "cloud_ssd"             # 筛选支持云 SSD 磁盘的可用区
}

# 创建 VPC
resource "alicloud_vpc" "vpc" {
  vpc_name   = var.name                             # VPC 名称:使用变量中的名称前缀
  cidr_block = "172.16.0.0/12"                      # VPC 网段:私有网段,范围 172.16.0.0-172.31.255.255
}

# 创建 vswitch
resource "alicloud_vswitch" "vsw" {
  vpc_id     = alicloud_vpc.vpc.id                  # 关联所属 VPC:引用上面创建的 VPC ID
  cidr_block = "172.16.0.0/21"                      # 交换机网段:属于 VPC 网段的子集,范围 172.16.0.0-172.16.7.255
  zone_id    = data.alicloud_zones.default.zones[0].id  # 可用区:使用上面筛选的第一个可用区
}

# 创建安全组
resource "alicloud_security_group" "default" {
  # 改用新的字段(阿里云 Terraform Provider 新版本规范)
  security_group_name = var.name                    # 安全组名称:替代旧的 name 字段
  vpc_id              = alicloud_vpc.vpc.id         # 关联 VPC:指定安全组所属的 VPC
}

# 向安全组添加入方向放行规则
resource "alicloud_security_group_rule" "allow_all_ingress" {
  type              = "ingress"                           # 规则类型:入方向(外部→ECS)
  ip_protocol       = "all"                               # 协议类型:允许所有协议(TCP/UDP/ICMP 等)
  nic_type          = "intranet"                          # 网卡类型:内网(VPC 环境下兼容公网)
  policy            = "accept"                            # 访问策略:允许(拒绝为 drop)
  port_range        = "-1/-1"                             # 端口范围:-1/-1 表示所有端口(替代 80/22 等具体端口)
  priority          = 1                                   # 优先级:数字越小优先级越高(1-100)
  security_group_id = alicloud_security_group.default.id  # 关联安全组:引用上面创建的安全组 ID
  cidr_ip           = "0.0.0.0/0"                         # 来源 IP:允许所有 IP 访问(生产环境建议缩小范围)
}

# 创建 ECS
resource "alicloud_instance" "instance" {
  count                       = var.ecs_instance_count                  # 实例数量:引用变量,控制创建多台 ECS
  availability_zone          = data.alicloud_zones.default.zones.0.id   # 可用区:和交换机一致
  security_groups            = alicloud_security_group.default.*.id     # 关联安全组:引用安全组 ID(列表形式)
  instance_type              = var.instance_type                        # 实例规格:引用变量中的规格
  system_disk_category       = "cloud_essd"                             # 系统盘类型:极速云 SSD(性能优于普通 SSD)
  system_disk_size           = 40                                       # 系统盘大小:40GB(默认最小 20GB)
  image_id                   = var.image_id                             # 镜像 ID:引用变量中的 Ubuntu 镜像
  instance_name              = "${var.name}-${count.index + 1}"         # 实例名称:前缀+序号(count.index 从 0 开始)
  vswitch_id                 = alicloud_vswitch.vsw.id                  # 关联交换机:指定 ECS 所属的交换机
  internet_max_bandwidth_out = var.internet_max_bandwidth_out           # 公网带宽:引用变量中的带宽值
  tags                       = merge(var.tags, { "Instance" = "${count.index + 1}" })   # 标签:合并默认标签+实例序号
  password 				           = "Pass@Aliyun123"                         # 登录密码:ECS 的 root/管理员密码(生产环境建议用密钥对)

  # 远程执行器,定义需要执行的命令
  provisioner "remote-exec" {
    on_failure = fail                                       # 执行失败策略:失败则终止 Terraform 部署(可选:continue 继续)
    connection {
      type        = "ssh"                                   # 连接类型:SSH(远程登录)
      host        = self.public_ip                          # 连接地址:ECS 的公网 IP(self 表示当前 ECS 资源)
      user        = "root"                                  # 登录用户:Ubuntu 系统默认 root 用户
      password    = self.password                           # 登录密码:使用上面配置的 ECS 密码
      port        = 22                                      # SSH 端口:默认 22 端口
      timeout     = "5m"                                    # 超时时间:5 分钟(防止安装过程超时)
    }

    # 具体命令操作:apt 安装 Nginx 的命令(极简版)
    inline = [
      # 第一步:创建临时日志文件,捕获所有输出(仅报错时打印)
      "LOG_FILE=/tmp/nginx_install.log",
      "echo '开始部署 ECS ${count.index + 1} Nginx...' > $LOG_FILE",
      
      # 第二步:更新包列表(所有输出写入日志,仅报错时终止)
      "apt update -y >> $LOG_FILE 2>&1 || { echo 'ERROR: ECS ${count.index + 1} apt update 失败,日志:'; cat $LOG_FILE; exit 1; }",
      
      # 第三步:安装 Nginx(输出写入日志,报错时打印日志并终止)
      "apt install -y nginx >> $LOG_FILE 2>&1 || { echo 'ERROR: ECS ${count.index + 1} Nginx 安装失败,日志:'; cat $LOG_FILE; exit 1; }",
      
      # 第四步:设置开机自启并启动(报错时打印日志)
      "systemctl enable nginx --now >> $LOG_FILE 2>&1 || { echo 'ERROR: ECS ${count.index + 1} Nginx 启动失败,日志:'; cat $LOG_FILE; exit 1; }",
      
      # 第五步:验证 Nginx 状态(报错时打印日志)
      "if ! systemctl is-active nginx >> $LOG_FILE 2>&1; then echo 'ERROR: ECS ${count.index + 1} Nginx 未运行,日志:'; cat $LOG_FILE; exit 1; fi",
      
      # 第六步:清理日志,输出成功提示(仅正常时显示)
      "rm -f $LOG_FILE",
      "echo '✅ ECS ${count.index + 1} Nginx 部署成功!访问地址:http://${self.public_ip}'"
    ]

  }
  
}

# 输出所有 ECS 的 ID(输出资源:部署完成后显示的信息)
output "ecs_ids" {
  value = alicloud_instance.instance[*].id                                        # 输出值:所有 ECS 实例的 ID(列表形式)
}

# 输出所有 ECS 的公网 IP
output "ecs_ips" {
  value = alicloud_instance.instance[*].public_ip                                 # 输出值:所有 ECS 的公网 IP(列表形式)
}

# 输出所有 ECS 的 Nginx 访问地址
output "nginx_access_urls" {
  value = [for ip in alicloud_instance.instance[*].public_ip : "http://${ip}"]    # 循环生成访问URL
}