第 7 章 · 02 状态管理与模块化


文档摘要

第 7 章 · 02 状态管理与模块化 Terraform 与"跑一遍脚本"的本质区别是 state:一份记录"代码声明的资源与现实资源对应关系"的档案。没有 state,Terraform 就不知道哪些资源归自己管、管到哪一步了。本节解剖 state 的三要素、远程 backend 与锁,再讲模块化设计与依赖生命周期的生产技巧。 学习目标 说出 state 文件的三要素与四大作用 配置 S3 + DynamoDB 远程 backend 并理解锁的机制 知道为什么 state 不能放 Git、不能手工编辑 设计结构良好的模块并用版本约束调用 掌握 createbeforedestroy、taint、provisioner、import 的用法 一、state:Terraform 的记忆

第 7 章 · 02 状态管理与模块化

Terraform 与"跑一遍脚本"的本质区别是 state:一份记录"代码声明的资源与现实资源对应关系"的档案。没有 state,Terraform 就不知道哪些资源归自己管、管到哪一步了。本节解剖 state 的三要素、远程 backend 与锁,再讲模块化设计与依赖生命周期的生产技巧。

学习目标

  • 说出 state 文件的三要素与四大作用
  • 配置 S3 + DynamoDB 远程 backend 并理解锁的机制
  • 知道为什么 state 不能放 Git、不能手工编辑
  • 设计结构良好的模块并用版本约束调用
  • 掌握 create_before_destroy、taint、provisioner、import 的用法

一、state:Terraform 的记忆

terraform.tfstate 是 JSON 文件,包含三要素:

  1. 资源的表示——资源的 JSON 形式(属性、ID 等),用于识别资源及 .tf 中定义的内容;
  2. Terraform 版本
  3. 输出(Outputs)

state 的四大作用:把真实基础设施对象映射到代码声明的资源;跟踪依赖;检测漂移(drift,现实与代码不一致);缓存属性数据加速 plan。

由此推出四条铁律:

state 不能放 Git——它包含明文凭据;分支切换/checkout 可能导致 apply 非最新代码;缺乏锁与审计。应放加密 + 版本化 + 带锁的远程 backend。

不能并发修改——两个进程同时编辑会得到无效 state;后端支持时 Terraform 自动加锁,自动化管道应等待锁释放而非强制解锁。

不手工编辑——用 state mv / state rm / state replace-provider 等受支持命令操作。

定期备份——指定"state 负责人",把 .gitignore 中排除 .tfstate 与 .terraform/。

二、远程 backend:S3 + DynamoDB 锁

backend 决定 state 如何存储与加载,默认是 local(本地磁盘,适合单人实验)。只要超过一个操作者或自动化管道,就必须用远程 backend。黄金组合是 S3 + DynamoDB:

terraform { required_version = ">= 1.6.0" backend "s3" { bucket = "my-tfstate-bucket" key = "prod/network/terraform.tfstate" region = "us-east-1" dynamodb_table = "tf-locks" encrypt = true } }

配套要求:S3 桶开启版本化、默认加密、阻止公共访问;DynamoDB 表主键必须为 LockID;IAM 最小权限。远程 backend 下的 apply 流程:获取锁(防并发)→ 下载最新 state 快照 → 规划 → apply → 完成后原子上传更新后的 state。

两个限制要知道:backend 块中不能使用变量(已知限制,可用部分配置 + -backend-config 解决);本地 state 迁入远程用 terraform init -migrate-state。跨模块取输出用 data.terraform_remote_state。

多环境 state 管理:推荐每个环境独立 state 文件——不同 backend 配置(不同 S3 前缀/桶)、不同 workspace 或目录(隔离 state + 不同凭据)。workspace 让同一 backend 内隔离 state,但所有 workspace 共享同一访问控制与认证,staging 与 production 不应共用凭据——环境隔离更推荐独立目录/独立 backend/独立凭据。

三、模块:可复用的基础设施积木

任何目录下的 .tf 文件都是模块——根配置本身是模块,被调用的叫子模块。模块的价值:以架构视角(VPC、数据库)而非物理对象视角描述基础设施;跨仓库、跨环境复用;把最佳实践(命名、标签、安全控制)固化为稳定接口;缩小爆炸半径。

推荐布局:

modules/vpc/ main.tf # 资源、data source、locals variables.tf # 带类型/描述/校验的输入 outputs.tf # 最小化的下游所需输出 versions.tf # 锁定 Terraform/provider 版本 README.md examples/ basic/ main.tf # 可运行示例,供文档与 CI 测试

调用模块:source 必填(本地路径、Git URL、Registry);始终钉住模块版本保证 plan 可复现——Registry 用 version 约束,VCS 源用 ?ref=tag。

module "vpc" { source = "git::https://github.com/org/infra-modules.git//vpc?ref=v1.2.3" version = ">= 1.2.0, < 2.0.0" # 仅 Registry 模块 cidr = "10.0.0.0/16" azs = ["us-east-1a", "us-east-1b"] }

访问模块输出:module.名称.输出变量名。模块反模式清单:内嵌 provider 块(跨账号复用困难)、接受 map(any) 泛型输入(隐藏结构)、输出密钥未标 sensitive、"上帝根模块"混杂网络/计算/应用、复制粘贴不版本化不测试。测试手段:terraform validate、对 examples 跑 plan、tflint --module、Terratest(真实创建+断言+销毁),并纳入 CI。

注意模块共享后相对路径失效:改用 path.module(当前模块)/path.root(根模块)/path.cwd(工作目录)。

四、依赖与生命周期

隐式依赖:资源引用其他资源属性(aws_security_group.instance.id)时自动建立,Terraform 按依赖顺序创建。terraform graph 输出 DOT 格式依赖图。

显式 depends_on:依赖无法从引用推导时使用(如"应用部署在集群上"但代码里没引用集群属性)。

默认生命周期:更新资源 = 删除旧的 → 创建新的 → 更新所有引用。用 lifecycle 改写:

lifecycle { create_before_destroy = true }

经典场景:AWS ASG 依赖的 launch configuration 不可修改,默认顺序"先删旧"会导致删除失败——必须"先建新、更新引用、再删旧"。修改后的 user_data 对运行中实例不生效是另一个常见坑:user_data 只在开机时执行,需 user_data_replace_on_change = true。

taint:资源创建成功但 provision 阶段失败时被标记为 tainted;terraform taint 可手动标记,下次 apply 销毁重建。

provisioner:用于服务配置(local-exec 在本机执行、remote-exec 在远程资源执行),常被建议作为最后手段——provisioner 动作多样、plan 难以预知实际效果,能用内置能力就不用(配置类工作交给 Ansible 更合适,见第 3 节)。

import:把已存在资源导入 state(只入 state,不生成配置代码)。步骤:确认资源 → 编写匹配的配置 → terraform import 类型.名称 资源ID。用例:已有资源未纳管;丢失 tfstate 后重建。重命名资源不重建用 terraform state mv。

多 region/多账号:provider alias 定义多个块,资源用 provider = aws.west_region 指定;多账号用 assume_role:

provider "aws" { region = "us-west-1" alias = "west_region" } resource "aws_instance" "west_region_instance" { provider = aws.west_region instance_type = "t2.micro" }

五、生产项目结构

按环境分目录(staging/、production/),每环境独立 backend;环境内再按组件分(applications/、databases/、networking/);文件划分 main.tf、providers.tf、outputs.tf、variables.tf、dependencies.tf;重复代码用模块;重复硬编码值用 locals;标签标准变更用 provider 级 default_tags;重命名资源用 terraform state mv 避免停机。

小结

本节完成了 Terraform 的生产化:state 三要素与四大作用解释了"为什么是声明式工具而非脚本";S3+DynamoDB 远程 backend 解决了多人协作与并发;模块把最佳实践固化为接口;create_before_destroy 与 import 覆盖了生产中最常见的两个改写场景。Terraform 管的是"资源从无到有",而资源上跑什么配置,是 Ansible 的舞台——下一节切换工具。

下一节预告

第 7 章第 3 节《Ansible配置管理与最佳实践》将讲解无代理架构、六大组件、幂等性、变量优先级与执行策略。


发布者: 作者: 灏天文库 转发
评论区 (0)
U