Terraform最佳实践

docker

规范

  1. 使用 required_version 限制 Terraform 版本
  2. 使用远程 state 存储
  3. 保持 provider 配置统一
  4. 遵循团队命名规范
  5. 定期运行 plan 监测漂移, 如果发现不一致, 自动报警

多环境管理

  1. 不同分支对应不同环境
  2. 不同目录结构对应不同环境; 推荐
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

  1. 开发者提交 PR
  2. CI 工具自动运行 plan, 并将结果以评论形式反馈到 PR
  3. 架构师 / 运维负责人审核 Plan 结果后 Merge
  4. 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
  }

}

依赖管理

  1. 会自动识别依赖关系
  2. 通过 depends_on = [] 手动指定依赖

导入资源状态

  1. 手写基础配置
  2. 导入
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, 在服务不可用时跳过某些资源创建

资源管理

资源重命名

  1. 先修改 tf 文件内的名字
  2. state mv

删除资源

  1. 配置文件内删除, 然后 terraform apply
  2. state rm 只删除状态文件内配置
  3. 只是移除纳管, 云上保留, 使用 removed{}, 可保留记录
1
2
3
4
5
6
7
removed {
  from = alicloud_eip.example

  lifecycle {
    destroy = false     # 明确不要销毁真实资源
  }
}

操作项目内部分资源

  1. -target 参数指定操作资源
  2. 模块化拆分, 生产级做法
  3. 功能开关变量

功能开关变量(适合同一项目内控制) - 不推荐

在模块内通过传入的开关参数来决定是否启用某资源或特性

 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"
Licensed under CC BY-NC-SA 4.0
转载或引用本文时请遵守许可协议,知会作者并注明出处
不得用于商业用途!
最后更新于 2025-09-20 00:00 UTC