第 7 章 · 01 基础设施即代码与Terraform核心 手动点击控制台创建资源:慢、易错、不可重复——这是基础设施规模化的三座大山。Terraform 用"代码描述期望状态"的方式推倒它们。本节讲清 IaC 的定位,建立 Terraform 的命令生命周期与 HCL 语法体系,重点是 count 与 foreach 这对高频考点的取舍。 学习目标 说清 IaC 的价值与 Terraform 的定位(声明式、无代理) 掌握 init/plan/apply/destroy 命令生命周期与输出符号 会用 resource/data/variable/output/locals 组织配置 分清 count 与 foreach 的适用场景 理解 provider、构建上下文与插值的机制
手动点击控制台创建资源:慢、易错、不可重复——这是基础设施规模化的三座大山。Terraform 用"代码描述期望状态"的方式推倒它们。本节讲清 IaC 的定位,建立 Terraform 的命令生命周期与 HCL 语法体系,重点是 count 与 for_each 这对高频考点的取舍。
基础设施即代码(IaC)用人类可读的配置文件定义云上和本地资源,可版本化、复用、共享,并管理基础设施全生命周期(低层:计算/存储/网络;高层:DNS、SaaS 功能)。
手动流程为何不可规模化?慢(每步需人介入)、易错(手动配置增加不一致风险)、不可重复(难以跨环境复现同一套环境)。IaC 的三大价值:全自动化(创建/修改/删除全生命周期自动化)、模块化可复用(代码在团队间、云间共享)、可测试(CI 可应用于 IaC 项目,先验证后执行)。
Terraform 的三个核心特性:
声明式——描述"最终状态"而非"操作步骤"(与过程式相对,Ansible 的定位对比见第 3 节)。
无代理(No agents)——通过各云厂商/服务的 API 执行操作,目标机器上无需安装任何东西(对比 Puppet 的 agent-server 模式)。
社区驱动——模块与修复持续发布,Terraform Registry 是 provider 与模块的公共仓库。
| 命令 | 作用 |
|---|---|
| terraform init | 扫描代码识别 provider/模块并下载;改模块 source 后需 -upgrade |
| terraform plan | 预览将要执行的操作,不修改任何资源 |
| terraform apply | 执行定义,增/改/删资源,产生 state 文件 |
| terraform destroy | 清理全部被跟踪的资源(不可回滚) |
| terraform refresh | 刷新 state 与实际资源同步(现代做法用 plan -refresh-only) |
| terraform validate | 语法与 provider 校验 |
| terraform console | 交互式测试函数与语法(只读) |
典型工作流:写 .tf 文件 → init → plan(预览)→ apply(执行)。生产中通过 PR/MR 驱动:提交变更 → 测试 → 合并后自动 apply——"先预览后执行"是 Terraform 安全性的根基。
plan 输出的三个符号:+ 新增、- 删除、-/+ 替换(先删旧再建新,默认生命周期,第 2 节讲如何改写)。
resource "aws_instance" "some-instance" { ami = "ami-201720221991yay" instance_type = "t2.micro" }
resource 块语法:resource "TYPE" "NAME",TYPE 由 provider 提供(如 aws_instance),NAME 是本地逻辑名(不是实例名)。
data source(数据源):只读查询外部数据,语法 data "TYPE" "NAME" {},引用 data.TYPE.NAME.ATTRIBUTE。可组合使用——先取默认 VPC 再用其 ID 过滤子网。
data "aws_vpc" "default" { default = true } data "aws_subnets" "default" { filter { name = "vpc-id" values = [data.aws_vpc.default.id] } }
变量(Input Variables):类型有 string/number/bool/list/set/map/object/tuple,默认类型 any。传值优先级从低到高:环境变量 TF_VAR_名称 → terraform.tfvars 文件 → -var/-var-file 命令行。引用 var.NAME,字符串内插值 "${var.NAME}"(常见于 user_data 脚本)。sensitive = true 让 plan/apply 不显示值——但注意它仍记录在 state 文件中(第 2 节展开)。
输出(Output):打印/暴露数据(最常用:实例 IP)。sensitive = true 避免记录敏感数据;depends_on 显式声明依赖;输出存于 state,可被其他模块通过 terraform_remote_state 读取。
Locals:与变量不同,locals 用户不可覆盖——适用于不想暴露给调用者的重复硬编码值。定义 locals { x = 2 },使用 local.x。
创建多个同类资源,两种元参数:
| 维度 | count | for_each |
|---|---|---|
| 适用集合 | 列表/数字 | 仅 map/set(列表需 toset() 转换) |
| 实例引用 | count.index | each.key / each.value |
| 内联块循环 | 不支持 | 支持(配合 dynamic 块) |
| 列表变更 | 中间删元素导致索引错位,可能误改误删其他资源 | 地址稳定,增删不牵连 |
# count:按列表长度创建 resource "aws_iam_user" "user" { count = length(var.users) name = var.users[count.index] } # for_each:map 驱动 resource "google_compute_instance" "instances" { for_each = var.names_map name = each.value }
选型原则:需要"按集合中元素一一对应、地址稳定"的语义时用 for_each;需要简单数字/列表索引时用 count。动态内联块循环用 dynamic + for_each:
resource "some_instance" "instance" { dynamic "tag" { for_each = var.tags content { key = tag.key value = tag.value } } }
配套表达式:条件 condition ? true_val : false_val(可配合 count = var.amount ? 1 : 0);for 表达式 [for name in var.users : upper(name) if length(name) > 3];投影 users[*].name。注意 count 不能引用其他资源的输出——count 必须在资源创建前确定。
Provider 是与云厂商/API 交互的插件,为 Terraform 添加资源类型与 data source——没有 provider 就管理不了任何基础设施。安装方式:写 provider 块后 terraform init,默认从 Registry 安装。
terraform { required_providers { aws = { source = "hashicorp/aws" version = "~> 3.0" } } } provider "aws" { region = "us-west-2" }
多 region/多账号用 alias 与 assume_role(第 2 节展开)。
本节建立了 Terraform 的操作闭环:命令生命周期管流程,HCL 管描述,count/for_each 管批量,provider 管连接。但还有两个"看不见的支柱"决定 Terraform 能否多人协作——state 文件与模块化,它们是下一节的主题。
第 7 章第 2 节《状态管理与模块化》将解剖 state 文件的三要素与远程 backend 方案,讲解模块设计与 create_before_destroy、import 等生产技巧。