规范
- 使用 required_version 限制 Terraform 版本
- 使用远程 state 存储
- 保持 provider 配置统一
- 遵循团队命名规范
- 定期运行 plan 监测漂移, 如果发现不一致, 自动报警
多环境管理
- 不同分支对应不同环境
- 不同目录结构对应不同环境; 推荐
1
2
3
4
|
# 文件
main.tf
variables.tf
outputs.tf
|
.gitignore
1
2
3
4
5
6
|
.terraform/
*.tfstate
*.tfstate.backup
*.tfvars
.terraform.lock.hcl
terraform.tfvars
|
cicd
- 开发者提交 PR
- CI 工具自动运行 plan, 并将结果以评论形式反馈到 PR
- 架构师 / 运维负责人审核 Plan 结果后 Merge
- main 合并后自动触发 terraform apply
1
2
3
4
5
|
terraform plan -out=tfplan
terraform apply tfplan
# 状态码监测: 0 没变更, 1 出错, 2 有变更
terraform plan -detailed-exitcode
|
资源命名规范
{project}-{environment}-{resource_type}-{purpose}
示例:
myproject-prod-vpc
myproject-prod-subnet-public-a
myproject-prod-nat-1
myproject-prod-sg-default
myproject-prod-igw
模块化
自定义模块应该是业务资源的抽象, 避免对已有资源的重复定义
技术要点回顾
模块化设计: 一套代码支持多环境部署, 减少重复
状态管理: 使用远程后端, 启用锁机制, 确保团队协作安全
渐进式部署: 先 dev, 再 staging, 最后 prod
成本意识: 开发环境精简配置, 生产环境保证高可用
版本锁定
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
terraform {
required_version = ">= 1.5.0, < 1.7.0"
required_providers {
aws = {
source = "hashicorp/aws"
version = "~> 5.0"
}
random = {
source = "hashicorp/random"
version = "~> 3.0"
}
}
}
|
1
2
3
|
~> 5.0: 允许 5.x 的任何版本, 不允许升到 6.0
~> 5.36.0: 允许 5.36.x 的任何版本, 不允许升到 5.37
>= 5.0, < 6.0: 允许 5.0 及以上, 6.0 以下
|
敏感信息传递
通过环境变量传递 provider 所需要的认证信息
1
2
|
export AWS_ACCESS_KEY_ID="your-key"
export AWS_SECRET_ACCESS_KEY="your-secret"
|
Vault
1
2
|
# env 传递
export TF_VAR_db_password="your-secret-password"
|
使用 ssm
1
2
3
4
5
6
7
8
9
|
variable "db_password" {
description = "Database password"
type = string
sensitive = true
}
data "aws_ssm_parameter" "db_password" {
name = "/prod/db/password"
}
|
生命周期管理
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
|
resource "aws_rds_cluster" "main" {
# 防止被意外删除
lifecycle {
# 生产核心资源必须加
# 当 plan 结果包含删除这个资源, Terraform 会直接报错, 不会执行
prevent_destroy = true
}
lifecycle {
# 对于需要替换的资源, 先创建再删除
create_before_destroy = true
}
lifecycle {
# 忽略某些状态的变化, 如密码用户等, 自动扩缩容相关的动态属性
ignore_changes = [desired_capacity, cidr_ip]
# 特殊情况下 也可以忽略全部字段的变更
ignore_changes = all
}
}
|
依赖管理
- 会自动识别依赖关系
- 通过 depends_on = [] 手动指定依赖
导入资源状态
- 手写基础配置
- 导入
1
2
|
terraform import aws_vpc.existing vpc-0abc12345
terraform plan
|
重建资源
1
|
terraform apply -replace=aws_instance.web
|
状态引用
跨模块引用 output
1
2
3
4
5
|
# 在 RDS 模块里引用 VPC 模块的输出
resource "aws_db_subnet_group" "main" {
name = "prod-db-subnet"
subnet_ids = module.vpc.private_subnet_ids
}
|
跨状态文件引用
1
2
3
4
5
6
7
8
9
10
11
12
|
data "terraform_remote_state" "vpc" {
backend = "s3"
config = {
bucket = "my-org-terraform-state"
key = "prod/network/terraform.tfstate"
region = "ap-northeast-1"
}
}
resource "aws_db_subnet_group" "main" {
subnet_ids = data.terraform_remote_state.vpc.outputs.private_subnet_ids
}
|
最佳实践 Checklist
[ ] 项目分层: modules/, environments/, providers/ 分离;
[ ] 状态后端用远程(不用 local), 加锁, 加密;
[ ] 变量用 variable 块定义, 敏感变量标记 sensitive, 机密使用 ephemeral
[ ] 资源命名规范: {project}-{env}-{role}-{instance};
[ ] 统一标签: Environment, ManagedBy, Project, CostCenter, Owner;
[ ] 模块化: 业务模块和基础设施模块分开;
[ ] 版本约束: required_version, required_providers, module.version;
[ ] terraform fmt 统一格式;
[ ] terraform validate 静态检查;
[ ] tflint 静态检查(社区 linter);
[ ] checkov / tfsec 安全扫描;
[ ] 关键资源加 prevent_destroy;
[ ] 漂移检测: 定时 plan / driftctl;
[ ] CI 集成: plan → review → apply 流程;
[ ] plan 输出已经仔细审查
[ ] 敏感信息用 Vault / KMS, 不进代码;
[ ] Policy as Code: OPA / Conftest 阻断违规;
[ ] 成本监控: Infracost / 账单分析;
[ ] 文档: 每个 module 有 README, 说明 inputs / outputs / 用途
[ ] 状态备份
[ ] 有回滚方案
[ ] 有通知机制
基于服务健康的 Provider 配置
一些 Provider 可以根据服务健康情况动态调整配置
例如 Consul Service Health Check, 在服务不可用时跳过某些资源创建
资源管理
资源重命名
- 先修改 tf 文件内的名字
- state mv
删除资源
- 配置文件内删除, 然后 terraform apply
- state rm 只删除状态文件内配置
- 只是移除纳管, 云上保留, 使用 removed{}, 可保留记录
1
2
3
4
5
6
7
|
removed {
from = alicloud_eip.example
lifecycle {
destroy = false # 明确不要销毁真实资源
}
}
|
操作项目内部分资源
- -target 参数指定操作资源
- 模块化拆分, 生产级做法
- 功能开关变量
功能开关变量(适合同一项目内控制) - 不推荐
在模块内通过传入的开关参数来决定是否启用某资源或特性
1
2
3
4
5
6
7
8
9
10
11
|
variable "create_nat_gateway" {
description = "是否创建 NAT 网关"
type = bool
default = false
}
resource "alicloud_nat_gateway" "main" {
count = var.create_nat_gateway ? 1 : 0 # 条件创建
vpc_id = alicloud_vpc.main.id
}
|
1
|
terraform apply -var="create_nat_gateway=false"
|