想象一下,你是这座城市的绿化管理员。玫瑰园在 A 苗圃,樟树在 B 苗圃,滴灌管道要找 C 厂商——每个苗圃的下单流程、验收标准都不同,而你手上只有一张“理想花园设计图”。如果每换一个苗圃都要重新学一套语言,花园永远建不起来。 Terraform 就是这位“智能园林总管家”。它把“我要一座花园”翻译成一份份清清楚楚的订单:找谁订货(Provider)、定什么货(Resource)、按什么顺序种(依赖图)、记在哪本台账(State),并严格按“先出图纸、再动土、定期巡检、按需清场”的流程执行。 本文用这座花园作比喻,带你看懂 Provider 与 Resource 的生命周期:从定义、配置、创建、读取、更新到删除,再到保护规则、状态台账与依赖顺序,最后用可运行的 Python 演示把整个流程跑一遍。读完你就能自信地解释“为什么 apply 前还要跑 plan”“台账丢了会怎样”“古树为什么拔不掉”。

岗位关联

掌握了 Provider 与 Resource 生命周期,你就能精准控制云端资源从生到死的每一步,写出可预测、可回滚、可审计的基础设施代码——这是云原生时代 SRE、DevOps 与平台工程师的核心基本功。

目录
  1. 可视化演示
  2. 供应商基础:Provider 是什么
  3. 资源定义:Resource 块
  4. 生命周期四阶段
  5. 生命周期规则:Lifecycle 块
  6. 状态管理:那本不能丢的绿化台账
  7. 核心工作流:四道施工工序
  8. 依赖关系图:树荫下的先后顺序
  9. AI 应用场景
  10. Python 演示
  11. 常见误区
  12. 面试题

可视化演示

展示Resource的CRUD四阶段生命周期、Core经Provider桥接外部API的数据流,以及配置与实际状态间的漂移检测。

本节配有交互动效(进入视口后自动加载,约 3KB)。

Resource CRUD生命周期 创建/读取/更新/删除四阶段 CRUD→State 每次操作后资源属性写入State文件。

供应商基础:Provider 是什么

市政绿化处准备在滨江路种一百棵樱花树。电话打到苗圃,接单员张口就问:你们用哪家的苗木编码?土球规格怎么报?验收要不要带检疫证?你若不懂这套“苗圃语言”,一株都订不回来。于是绿化处聘请了一位专职的“苗圃对接员”——他熟记这家苗圃的所有下单规则,你把需求写给他,他去下单、跟踪、验收。

在 Terraform 的世界里,这位对接员就是 Provider。AWS、Azure、阿里云……每个云平台都有自己的“苗圃语言”:API、鉴权方式、资源类型各不相同。Provider 是一个插件,它清楚某个云平台的全部规则,把 Terraform 的通用指令翻译成该平台能执行的具体请求。你在配置里写 resource "alicloud_instance",Provider 就负责把它变成阿里云真实的一次创建调用。

Provider 通常在 .tf 文件中声明,并配上版本约束,避免团队“用的对接员版本不同、下单口径不同”。执行 terraform init 时,Terraform 会根据约束从 Registry(插件仓库)下载对应版本的插件并缓存在本地,像开工前把供货协议签好:

所以,当你说“我要一百棵樱花树”,真正去和苗圃打交道的是对接员(Provider)。后续创建、浇水、换盆、移除,全靠这位对接员用苗圃听得懂的语言一一落地。选对接员,是花园工程的第一步。

∀ r∈R,∃ p∈P:ρ(r)=p,α(p)=API(r)\displaystyle \forall\, r \in \mathcal{R},\quad \exists\, p \in \mathcal{P}:\quad \rho(r)=p,\quad \alpha(p)=\text{API}(r)
每个资源种类都对应一个 Provider,每个 Provider 内部又把资源操作翻译成云端 API——这就是“苗圃对接员”的两层分工。
  1. 资源集合 \(\mathcal{R}\)所有你想在云上创建的“植物”,比如虚拟机、网络、磁盘,它们构成资源集合。
    R={r1, r2, …, rn}\mathcal{R} = \{ r_1,\ r_2,\ \ldots,\ r_n \}
  2. 存在性 \(\exists\, p \in \mathcal{P}\)每种资源必须能找到归属的 Provider。alicloud_instance 由阿里云 Provider 负责,从不越界。
    ρ(r)=p\rho(r) = p
  3. Provider 动作 \(\alpha(p)\)Provider 内部把资源操作翻译成真实 API 请求,也就是“苗圃下单”那一步。
    α(p)={Create, Read, Update, Delete}(r)\alpha(p) = \{\text{Create},\ \text{Read},\ \text{Update},\ \text{Delete}\}(r)
hcl11 行
terraform {  required_providers {    alicloud = {      source  = "aliyun/alicloud"      version = "~> 1.0"    }  }}provider "alicloud" {  region = "cn-hangzhou"}

资源定义:Resource 块

台账上要给每株植物登记品种、位置、浇水频率。品种决定了它能长多大,位置决定它种在哪块地,属性则是它的“习性清单”。Resource 就是这张登记卡——告诉你“种什么、叫什么、怎么养护”。

Terraform 用 resource 块声明“我们希望云端存在某个对象”,它有三个核心要素:type(品种)、name(本地名字)、attrs(属性清单)。把这些写进 .tf 文件,terraform plan 就能据此计算出期望状态。

以阿里云 VPC 为例:

alicloud_vpc 是 type,web 是 name,cidr_block 和 vpc_name 是属性。type 决定能力——它告诉 Terraform 该去找哪个 Provider;name 只在当前配置内有效,类似台账上的编号;属性描述期望状态。其他资源通过 alicloud_vpc.web.id 这样的表达式引用它,从而形成依赖链条。

Resource 就是台账上那一行完整记录。品种对,名字清,属性全,花园里的每一件东西才说得清、管得住。

r=( t, n, A ),A={ a1, a2, …, ak }\displaystyle r = (\,t,\ n,\ \mathcal{A}\,),\qquad\mathcal{A} = \{\, a_1,\ a_2,\ \ldots,\ a_k \,\}
一个资源由“品种 + 名字 + 属性集合”三部分组成,属性集合里的每一项都是一条期望状态记录。
  1. 三元组结构资源的身份由 type 和 name 共同决定,全局唯一地址写作 type.name。
    r=( t, n, A )r = (\,t,\ n,\ \mathcal{A}\,)
  2. 属性集合 \(\mathcal{A}\)每个属性都是一条“期望状态”,比如网段、名称、实例规格。
    ai=(keyi, valuei)a_i = (\text{key}_i,\ \text{value}_i)
  3. 资源引用通过 type.name.attr 把属性暴露给其他资源,比如 alicloud_vpc.web.id。
    ref(r, a)=value(r,a)\text{ref}(r,\ a) = \text{value}_{(r,a)}
hcl4 行
resource "alicloud_vpc" "web" {  cidr_block = "10.0.0.0/16"  vpc_name   = "web-env"}

生命周期四阶段

一株云杉从种子到老树,要经历播种、发芽、长大、移走。Terraform 把这压缩成四个官方操作:Create、Read、Update、Delete。任何资源都逃不出这个循环,就像植物的一生被归纳成四个动作。

这四个操作由 Provider 内部的资源实现提供,对应真实云 API 的若干次调用。Terraform 内核不关心“怎么创建一台 ECS”,它只负责在合适的时机调用 Provider 暴露的四个接口。

  • Create:检测到资源不在 state 中,调用创建接口,成功后记录全新 ID;
  • Read:查询真实资源的状态,用于漂移检测,常在 plan 和 refresh 时触发;
  • Update:配置属性与 state 有 diff 时,调用修改接口完成变更;
  • Delete:执行 destroy 时调用删除接口,并把资源从 state 中移除。

一株植物从种子到移除,四个动作循环往复。Terraform 的全部魔法,就是精确掌控这四步的触发时机与执行顺序。

S∅→CreateSrunning→ReadSverified→UpdateSchanged→DeleteS∅\displaystyle S_{\emptyset} \xrightarrow{\texttt{Create}} S_{\mathrm{running}} \xrightarrow{\texttt{Read}} S_{\mathrm{verified}} \xrightarrow{\texttt{Update}} S_{\mathrm{changed}} \xrightarrow{\texttt{Delete}} S_{\emptyset}
资源从“不存在”出发,经创建、读取、更新,最终回到“不存在”,构成封闭的生命周期环。
  1. 空状态 \(S_{\emptyset}\)起点和终点都是“空”——资源还不存在或已被彻底移除。
    S∅S_{\emptyset}
  2. 创建 \(S_{\mathrm{running}}\)Create 调用云端接口,资源进入运行态,state 记下新身份。
    S∅→CreateSrunningS_{\emptyset} \xrightarrow{\texttt{Create}} S_{\mathrm{running}}
  3. 读取 \(S_{\mathrm{verified}}\)Read 核对真实环境与记录是否一致,状态被“验证”。
    Srunning→ReadSverifiedS_{\mathrm{running}} \xrightarrow{\texttt{Read}} S_{\mathrm{verified}}
  4. 更新与删除Update 把资源改到期望状态,Delete 释放它,环回到空集。
    Schanged→DeleteS∅S_{\mathrm{changed}} \xrightarrow{\texttt{Delete}} S_{\emptyset}

生命周期规则:Lifecycle 块

园丁会在树旁挂保护牌:“古树严禁移走”“先栽新区再挖旧树”“这个字段不必每次更新”。牌子不影响植物生长,只约束园丁的操作方式。Terraform 的 lifecycle 块就是这些牌子。

把规则写进资源定义里,可精确干预替换、删除、更新行为:

  • create_before_destroy:需要替换时先建新实例、成功后再删旧实例,减少停机窗口;
  • prevent_destroy:禁止任何删除或替换动作,一旦 plan 中出现删除,apply 会直接报错;
  • ignore_changes:忽略指定属性的变更,允许控制台手动修改而不被 Terraform “纠偏”。

保护牌约束的是园丁,不是台风。生命周期规则只影响 Terraform 自己的行为,明白这一点,才不会把“禁止删除”误当成万能保险。

CBD:  Create(rnew)≺Destroy(rold),PD:  Destroy(r)=∅\displaystyle \texttt{CBD}:\ \ \text{Create}(r_{\mathrm{new}}) \prec \text{Destroy}(r_{\mathrm{old}}), \qquad\texttt{PD}:\ \ \text{Destroy}(r) = \varnothing
先建后删用“先于”符号表示新老资源的先后顺序;禁止删除则让删除操作集合为空。
  1. 偏序符号 \(\prec\)表示“先执行”。CBD 规则规定新资源创建必须先于旧资源销毁。
    Create(rnew)≺Destroy(rold)\text{Create}(r_{\mathrm{new}}) \prec \text{Destroy}(r_{\mathrm{old}})
  2. 空集 \(\varnothing\)prevent_destroy 使删除操作集合为空,任何计划中的销毁都被禁止。
    Destroy(r)=∅\text{Destroy}(r) = \varnothing
  3. 忽略集合ignore_changes = [ami, tags] 等价于从 diff 集合中剔除这些属性。
    diff(r)∖{ami, tags}\text{diff}(r) \setminus \{\text{ami},\ \text{tags}\}
hcl7 行
resource "aws_instance" "db" {  lifecycle {    create_before_destroy = true    prevent_destroy       = true    ignore_changes        = [ami, tags]  }}

状态管理:那本不能丢的绿化台账

老张管花园十三年,靠的是一本厚台账:哪儿种了什么、什么状态、上次修剪是什么时候。一天刮台风,园子面目全非,老张拿着台账一棵棵核对,发现七棵桂花东倒西歪——这就是漂移。台账要是丢了,哪些是自家种的、哪些是野生的,根本分不清。

Terraform 的 terraform.tfstate 就是这本台账。它保存每个资源从 Provider 返回的唯一 ID 与全部属性,是 plan 和 apply 的计算基础:判断资源该增、该改还是该删,全看“台账 vs 期望 vs 真实环境”三者的关系。多人协作时,可把 state 放到远端后端(如 S3 + DynamoDB 锁)防止并发写坏;一旦发现漂移,terraform apply 会把真实环境修回台账记录。

台账让园丁认得每一株自家的植物。state 让 Terraform 认得每一个自己创建的资源,也让它一进门就能发现哪儿不对劲。

drift(r):Attrreal(r)  ≠  Attrstate(r)\displaystyle \mathrm{drift}(r):\quad \mathrm{Attr}_{\mathrm{real}}(r) \;\neq\; \mathrm{Attr}_{\mathrm{state}}(r)
漂移的数学定义很简单:真实环境的属性不等于台账记录的属性,两边对不上了。
  1. 属性函数 \(\mathrm{Attr}\)Attr_real 是云端的真实属性,Attr_state 是台账里记的属性。
    Attrreal(r)与Attrstate(r)\mathrm{Attr}_{\mathrm{real}}(r) \quad \text{与} \quad \mathrm{Attr}_{\mathrm{state}}(r)
  2. 不等号 \(\neq\)只要两边任何属性不一致,就判定该资源发生漂移,需要巡检或修复。
    Attrreal(r)≠Attrstate(r)\mathrm{Attr}_{\mathrm{real}}(r) \neq \mathrm{Attr}_{\mathrm{state}}(r)

核心工作流:四道施工工序

花园改造不是抡起铁锹就挖。懂行的施工队先备料(init),再出图纸给你确认(plan),签字后动工(apply),完工后撤场并复垦(destroy)。四道工序,一道都不能乱。

Terraform 的核心工作流正是这四步:

  • init:下载 Provider 插件,初始化状态后端,相当于“备料+签协议”;
  • plan:对比 state 与真实环境,输出变更计划(+ 新增、~ 修改、- 删除),相当于“出图纸”;
  • apply:按依赖图依次执行变更,逐步更新 state,相当于“动工”;
  • destroy:删除 state 中记录的全部资源,释放云端占用,相当于“清场”。

辅助命令 fmt、validate、refresh 则像施工前的检查工具,保证图纸整洁、文法正确、台账够新。

工序井然,花园才不会越修越乱。Terraform 用四道固定工序,把“改基础设施”从即兴发挥变成可评审、可回滚的工程流程。

T0→  init  T1→  plan  T2→  apply  T3→  destroy  T0\displaystyle T_0 \xrightarrow{\;\texttt{init}\;} T_1 \xrightarrow{\;\texttt{plan}\;} T_2 \xrightarrow{\;\texttt{apply}\;} T_3 \xrightarrow{\;\texttt{destroy}\;} T_0
四个命令把环境状态从一个时点推进到下一个时点,destroy 之后又回到起点,形成闭环。
  1. 时点 \(T_0 \sim T_3\)每个时点代表环境的一类状态:未初始化、已计划、已应用、已清理。
    T0, T1, T2, T3T_0,\ T_1,\ T_2,\ T_3
  2. 命令箭头每个箭头是一次命令执行,箭头上的命令把状态推进到下一时点。
    T2→  apply  T3T_2 \xrightarrow{\;\texttt{apply}\;} T_3
  3. 闭环返回 \(T_0\)destroy 把环境还原成起点,便于下次从零开始。
    T3→  destroy  T0T_3 \xrightarrow{\;\texttt{destroy}\;} T_0

依赖关系图:树荫下的先后顺序

玉簪花喜阴,必须先种大树再种花;喷灌要把水管接好才能浇花。花园里到处是这种“先有谁、后有谁”的关系。园丁把关系画成一张网,按顺序施工,互不相关的角落则同时开工。

Terraform 通过解析资源间的引用(比如某个安全组引用了 VPC 的 ID)自动建立隐式依赖;也可以在资源配置中写 depends_on 做显式声明。所有依赖关系汇成一张有向无环图(DAG),通过拓扑排序决定执行顺序:无依赖的节点并行执行,有依赖的严格按序。如果出现循环依赖,plan 会直接报错,提示“你的花园设计里有个死结”。

先种大树,再种喜阴的花;先修水管,再浇灌花园。依赖图把这份园丁的经验固化成可计算的顺序,也让 Terraform 在复杂的基建中永远知道谁先谁后。

G=(V, E),(u,v)∈E  ⇒  order(u)<order(v)\displaystyle G = (\mathcal{V},\ \mathcal{E}), \qquad(u, v) \in \mathcal{E} \;\Rightarrow\; \text{order}(u) < \text{order}(v)
依赖图由顶点和边组成;只要存在一条从 u 指向 v 的边,u 就必须排在 v 前面。
  1. 顶点集 \(\mathcal{V}\) 与边集 \(\mathcal{E}\)每个资源是一个顶点,每条依赖关系是一条有向边。
    G=(V, E)G = (\mathcal{V},\ \mathcal{E})
  2. 有向边 \((u, v)\)表示“u 先于 v”,比如 VPC 必须先于交换机创建。
    (u,v)∈E(u, v) \in \mathcal{E}
  3. 拓扑序 \(\text{order}\)拓扑排序给每个顶点一个序号,保证所有边都从小编号指向大编号。
    order(u)<order(v)\text{order}(u) < \text{order}(v)

AI 应用场景

多云统一交付:一张设计图管两座苗圃

阶段:基础设施即代码(IaC)定义与编排

解决的问题:AI 训练集群需要 GPU 实例、VPC 网络、对象存储。传统方式要在多个云控制台手工点击几十次,容易配错安全组、记漏资源。用 Terraform 的多 Provider,一份代码同时对接阿里云与火山引擎,把不同云的差异封装在资源类型后面。

具体案例:某算法团队用 aliyun/alicloud 与 volcengine/volcengine 两个 Provider 定义同一套训练环境模板。terraform apply 一次拉起两朵云的资源,plan 先预览所有差异,误配在动工前就被拦下。

漂移巡检与自动恢复:给台账装上警报器

阶段:持续运维与状态一致性保障

解决的问题:生产环境偶尔被人绕过 Terraform 手动改动(例如临时开端口),导致台账失真。SRE 定时执行 terraform plan -detailed-exitcode,把非零退出码接入告警系统,再用受控的 apply 自动恢复,像园丁每天巡园、把被风吹歪的树扶正。

具体案例:某金融公司每晚自动巡检 200 余个资源,发现过 3 次未登记的安全组变更,均在 2 分钟内自动收敛回期望状态。

临时测试环境生命周期管理:用完即拆的临时花园

阶段:CI/CD 中的环境供给与回收

解决的问题:每次代码合并都要一套完整测试环境,人工搭建慢、长期占用又费钱。Terraform 的标准工作流正好解决:init + plan + apply 自动拉环境,合并后 destroy 一键回收。

具体案例:某互联网公司在 GitLab CI 中加入 Terraform 流水线,每个 MR 启动一套包含 RDS、Redis 的隔离环境,合并后自动销毁,云成本下降约 40%。

Python 演示

python83 行
# -*- coding: utf-8 -*-"""迷你 Terraform: 演示 Provider 的资源生命周期与依赖排序""" from collections import deque  class Provider:    """迷你 Provider: 像苗圃对接员, 负责把资源操作翻译成云端动作"""     def __init__(self, name):        self.name = name        self.backend = {}        self.log = []     def create(self, res):        self.backend[res.node_id] = {"state": "available"}        self.log.append("[{}] Create  {}".format(self.name, res.node_id))     def read(self, res):        if res.node_id not in self.backend:            self.log.append("[{}] Read    {} 漂移: 真实环境不存在".format(self.name, res.node_id))        else:            self.log.append("[{}] Read    {} 状态正常".format(self.name, res.node_id))     def update(self, res):        if res.node_id in self.backend:            self.backend[res.node_id]["state"] = "updated"            self.log.append("[{}] Update  {}".format(self.name, res.node_id))        else:            self.log.append("[{}] Update  {} 被跳过".format(self.name, res.node_id))     def delete(self, res):        existed = self.backend.pop(res.node_id, None) is not None        self.log.append("[{}] Delete  {} {}".format(self.name, res.node_id, "成功" if existed else "跳过"))  class Resource:    """一个资源: 只有名字和它依赖的其他资源"""     def __init__(self, node_id, deps=None):        self.node_id = node_id        self.deps = deps or []  def topo_sort(resources):    """拓扑排序: 先建被依赖方(VPC), 再建依赖方(ECS)"""    visited = set()    order = deque()     def dfs(node):        if node.node_id in visited:            return        visited.add(node.node_id)        for dep in node.deps:            dfs(dep)        order.append(node)     for res in resources:        dfs(res)    return list(order)  if __name__ == "__main__":    provider = Provider("aliyun")    vpc = Resource("vpc-web")    vswitch = Resource("vswitch-web", deps=[vpc])    ecs = Resource("ecs-web", deps=[vswitch])    resources = [vpc, vswitch, ecs]     seq = topo_sort(resources)    print("依赖顺序:", " -> ".join(res.node_id for res in seq))     for res in seq:        provider.create(res)    for res in seq:        provider.read(res)    provider.backend["vswitch-web"]["state"] = "port_changed"    for res in seq:        provider.update(res)    for res in reversed(seq):        provider.delete(res)     print("\n".join(provider.log))
目的:用约 60 行 Python 模拟 Terraform 的核心机制——依赖排序(拓扑排序)+ Provider 对每个资源执行 Create/Read/Update/Delete 生命周期,并演示漂移如何被发现和修复。
  1. class Provider:定义一个迷你 Provider,相当于苗圃对接员,所有 CRUD 操作都由它执行。
  2. self.backend = {}用字典模拟云端后端:key 是资源 ID,value 是资源状态。
  3. def create(self, res):对应生命周期的 Create 阶段,把资源登记进“云端”。
  4. if res.node_id not in self.backend:Read 时先查表,查不到就说明发生了漂移。
  5. def topo_sort(resources):实现依赖图的拓扑排序,保证 VPC 先于 VSwitch 创建。
  6. visited.add(node.node_id)避免重复访问同一个节点,防止依赖图中出现环时死循环。
  7. order.append(node)深度优先搜索的后序写入,天然保证“被依赖方排在前面”。
  8. for res in seq:apply 阶段按依赖顺序逐个执行 Create。
  9. provider.backend["vswitch-web"]["state"] = "port_changed"模拟有人绕过 Terraform 手动改资源,制造漂移。
  10. for res in reversed(seq):destroy 必须逆拓扑序,先删 ECS 再删 VPC,否则会破坏依赖。
  11. print("\n".join(provider.log))输出完整操作日志,让整个生命周期一目了然。

常见误区

误区看到 `provider "aws" {}` 就以为“我在配置里写了一个 Provider,就相当于创建了一台 AWS 服务器”。

正解Provider 是翻译官、是插件,它自己不创建任何东西;真正被创建的是 Resource。Provider 负责把资源操作翻译成云平台 API 调用。你雇了对接员不等于苗圃里已经种好了树,还得下订单(定义 resource)才行。

为何常见:HCL 里 provider 和 resource 块长得像,加上 init 会“下载东西”,容易让人误以为 Provider 就是云端对象。

误区`terraform apply` 输出 "Apply complete!" 就万事大吉,资源会一直听话。

正解Terraform 只保证 apply 那一刻的状态收敛,不提供“持续守护”。真实环境可能被手动改动、被故障破坏,这就是漂移。要像园丁定期巡园一样,靠 plan/refresh 发现漂移,再靠 apply 修复。

为何常见:“Apply complete!“ 太有误导性,让人以为基础设施是”一次性交付”,忽略了声明式系统的常态是持续校准。

误区给资源加上 `prevent_destroy = true`,就再也不怕资源丢掉了。

正解它只在 Terraform 自己的 destroy/替换流程中生效,拦不住有人在控制台手动删除、账号被清理,更防不住 state 文件被删。而且加了它之后,想删这个资源必须先改代码,反而增加维护负担。挂牌子约束的是园丁,不是台风。

为何常见:规则名字太像“安全保险”,让人高估了它对真实世界的约束力。

误区反正有 `.tf` 配置,把 `terraform.tfstate` 删了,重新 apply 就能再建出来。

正解配置是“目标”,state 是“现状”。丢了 state,Terraform 就不知道真实资源的 ID,plan 拿配置和“空台账”比较,会把已有资源当成全新资源,apply 后可能创建出重复资源,甚至先 destroy 再 create,造成业务中断。台账丢了,园丁根本不认识谁是自己种的。

为何常见:混淆了“期望状态”与“实际状态”,以为配置里写清楚就万事俱备。

误区`terraform destroy` 很安全,它只删除“这次新建”的资源。

正解destroy 会按照依赖图删除 state 中记录的全部资源,等于把整座花园连根拔起。对共享或生产环境执行,后果是灾难性的。应通过 terraform destroy -target=... 精确清理,或用目录/workspace 隔离环境。

为何常见:没有意识到 state 记录的是“全部家当”,而不是“最近的操作记录”。

面试题

Q1. Terraform 中 Provider 的主要职责是?

单选题 · easy

  • A.编写资源属性的配置
  • B.对接云平台的 API 插件← 正确答案
  • C.创建云端对象实例
  • D.存储资源状态文件

答案:B。Provider 是翻译官插件,负责把资源操作翻译成云平台 API 调用,本身不创建对象(那是 Resource 的事),也不负责存储状态(那是 state 的事)。就像苗圃对接员只负责下单沟通,不负责种树。

Q2. terraform init 命令的核心作用是?

单选题 · easy

  • A.下载插件并初始化后端← 正确答案
  • B.创建云端全部资源
  • C.生成资源变更计划
  • D.删除已创建的资源

答案:A。init 对应施工前的“备料+签协议”:下载 Provider 插件并初始化状态后端。创建资源是 apply,生成计划是 plan,删除资源是 destroy。四道工序各司其职,init 永远在最前面。

Q3. 以下哪个生命周期规则表示“先建新资源再删旧资源”?

单选题 · mid

  • A.prevent_destroy 表示禁止删除
  • B.create_before_destroy 先建后删← 正确答案
  • C.ignore_changes 忽略字段变更
  • D.depends_on 声明资源依赖

答案:B。create_before_destroy 的语义是 Create(新) 先于 Destroy(旧),用于减少替换资源时的停机窗口。prevent_destroy 是禁止删除,ignore_changes 是忽略属性 diff,depends_on 则属于依赖声明,不属于生命周期规则。

Q4. 资源引用表达式 alicloud_vpc.web.id 的三段结构是?

单选题 · mid

  • A.资源类型 资源名 属性名← 正确答案
  • B.资源名 资源类型 属性名
  • C.Provider 名 资源类型 属性名
  • D.属性名 资源类型 资源名

答案:A。Terraform 的资源引用固定为 type.name.attr:alicloud_vpc 是类型,web 是本地名字,id 是属性。这与台账上“品种-编号-字段”的登记逻辑一致。

Q5. 基础设施漂移 (drift) 指的是什么?

单选题 · mid

  • A.配置代码修改但没重新执行 apply
  • B.Provider 插件版本互相冲突
  • C.真实环境与云端台账记录不一致← 正确答案
  • D.依赖关系图中出现环路错误

答案:C。漂移数学定义是 Attr_real(r) ≠ Attr_state(r),即真实环境与 state 记录不一致。选项 A 只是“没执行 apply”,B 是版本冲突,D 是依赖图环,都不属于漂移。

Q6. 关于依赖图与执行顺序,正确的是?

单选题 · hard

  • A.隐式依赖的优先级低于显式依赖
  • B.互不依赖的资源可以并行执行← 正确答案
  • C.Terraform 始终按照声明顺序执行
  • D.依赖图中有环时自动跳过一条边

答案:B。DAG 的拓扑排序允许无依赖节点并行执行,有依赖的严格按序。隐式依赖与显式依赖没有优先级之分;Terraform 不按声明顺序执行;遇到循环依赖会在 plan 阶段直接报错,而不是跳边。

Q7. Read 读取操作通常在什么阶段触发?

单选题 · easy

  • A.init 阶段在下载插件时触发
  • B.destroy 删除资源前触发
  • C.apply 创建完资源后触发一次
  • D.plan 或 refresh 巡检真实环境时触发← 正确答案

答案:D。Read 主要用于漂移检测,在 terraform plan 和 terraform refresh 时读取真实环境与 state 对比,就像园丁巡园核对台账。init 只下载插件,create 时才写 state,destroy 前不强制 read。

Q8. 哪些做法可能导致 state 与实际环境不一致?

多选题 · mid

  • A.在控制台上手动删除资源实例← 正确答案
  • B.将 Provider 版本约束改得更严格
  • C.绕过 Terraform 直接修改资源属性← 正确答案
  • D.每天定时执行 terraform plan 检查

答案:AC。手动删除资源或直接改属性都会让真实环境偏离台账,产生漂移。修改版本约束只是改变插件版本选型;定期执行 plan 反而是发现漂移的正确做法,不会造成不一致。

Q9. 关于 prevent_destroy 规则,下列说法正确的有?

多选题 · hard

  • A.它能阻止替换该资源时先删旧资源← 正确答案
  • B.需要删除资源时必须先移除该规则← 正确答案
  • C.它能防止后端 state 文件被误删
  • D.对数据源 data 资源也有保护效果

答案:AB。prevent_destroy 会让一切 destroy 计划(包括替换)直接报错,因此想删必须先删掉这个规则——A、B 正确。它约束不了手动删 state 或控制台操作,且只作用于 resource,对 data 源无效。

Q10. 关于状态管理与工作流的说法,正确的有?

多选题 · extreme

  • A.远端后端配合锁机制防范并发写入← 正确答案
  • B.plan 会综合比较配置、真实环境与 state← 正确答案
  • C.apply 执行失败后 state 一定不会更新
  • D.destroy 仅删除本次新创建的资源

答案:AB。远端后端通常用锁(如 DynamoDB)防止多人同时写 state,A 正确;plan 的输出正是三向对比的结果,B 正确。apply 部分失败时 state 只更新成功部分,C 错误;destroy 删除的是 state 中记录的全部资源,D 错误。