Terraform综合

Terraform综合

Terraform

基础设置即代码, 管理多云环境

一款基础设施即代码(Infrastructure as Code, IaC)工具
描述和管理云端, 本地, 混合环境中的资源, 实现了 可重复, 可审计, 可共享 的基础设施管理模式

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
https://developer.hashicorp.com/terraform

# 社区模板
https://registry.terraform.io/
https://registry.terraform.io/modules/

# opentofu
是 Terraform 的一个分支, 由社区驱动
https://opentofu.org/
https://github.com/opentofu/opentofu
https://opentofu.org.cn/docs


# http doc
https://registry.terraform.io/providers/hashicorp/http/

https://registry.terraform.io/providers/aliyun/alicloud/latest/docs

# 云文档
https://learn.microsoft.com/zh-cn/azure/developer/terraform/

https://help.aliyun.com/zh/terraform/infrastructure-as-code-overview

场景

多公有云场景, 环境复杂度高

不适合场景

  1. 单云或少量设备, 少量环境
  2. paas 服务, 不需要关注底层设施

组件

  • Provider(供应商): 具体实现的厂家 - 外部环境
  • Module 模块: 是可复用的 Terraform 配置单元;
  • 状态: Terraform 使用 state 文件 追踪已部署资源
  • 数据源: 即只有在运行时才能知道的值

VS Code 集成

HashiCorp Terraform 插件

  1. 提供 语法高亮 / 自动补全 / 格式化
  2. 支持直接运行 terraform plan / apply
  3. 图形化资源依赖关系图

项目组织结构

运行时会读取工作目录中所有的 *.tf, *.tfvars 文件

一般约定
variables.tf 定义变量
terraform.tfvars 传入变量的值, 格式: 简单的键值对

versions.tf 用于约束 tf 的版本和 供应商的版本
output.tf 定义输出内容

provider.tf 云厂商的配置 region 等
terraform.tf tf 环境依赖, 供应商依赖
main.tf 核心逻辑

依赖

  1. 隐式依赖: Terraform 已知的, 可以自动推导出来的; 通过属性值引用赋值的方式来设置
  2. 显示依赖: 使用 depends_on 来显式声明依赖关系
1
2
3
4
resource "alicloud_vpc" "default {
    # 依赖某个资源创建后
    depends_on = ["xxx.xxx"]
}

override 合并文件

内嵌块的内容不会合并
需要文件名显式带 override.tf

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
# example.tf
resource "aws_instance" "web" {
  instance_type = "t2.micro"
  ami           = "ami-408c7f28"
}

# override.tf
resource "aws_instance" "web" {
  ami = "foo"
}

# 最终的文件
resource "aws_instance" "web" {
  instance_type = "t2.micro"
  ami           = "foo"
}

一般流程

  1. 初始化
  2. 格式化与验证
  3. 计划与预览
  4. 执行

初始化

  • 下载安装云厂商的对应版本的 Provider
  • 初始化配置后端 state 文件
  • 当 provider 版本或模块参数变化时, 需要重新初始化
1
2
3
4
5
terraform init
terraform fmt       # 格式化代码, 保持代码整洁
terraform validate  # 对定义的代码进行语法校检
terraform plan
terraform apply     # 执行变更

install cli

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
sudo apt update && sudo apt install terraform   # Ubuntu / Debian
sudo yum install terraform                     # RedHat / CentOS

yum install -y yum-utils
yum-config-manager --add-repo https://rpm.releases.hashicorp.com/RHEL/hashicorp.repo
yum -y install terraform

brew tap hashicorp/tap
brew install hashicorp/tap/terraform
terraform version

wget https://releases.hashicorp.com/terraform/1.13.4/terraform_1.13.4_linux_amd64.zip
unzip terraform_1.13.4_linux_amd64.zip
mv terraform ~/bin/

管理 cli

调试模式

1
2
3
4
5
6
7
8
9
# 启用调试日志
export TF_LOG=TRACE             # DEBUG
export TF_LOG_PATH=./terraform-trace.log

# 只针对 provider 进行调试
export TF_LOG_PROVIDER=TRACE


terraform plan
1
export TF_INPUT=false             # 禁用交互式提示,适配 CI 环境

基础命令

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17

terraform init                  # 初始化并下载 provider 插件

terraform plan                  # 查看执行计划
terraform plan -out=tfplan      # 生成详细执行计划文件
terraform plan -input=false     # 如果缺少变量不需要输入 直接退出

terraform output                # 输出所有预定义的 output
terraform output instance_ip    # 显示特定输出
terraform output -json          # 以JSON格式输出

terraform apply                 # 部署资源 - 交互式执行
terraform apply --auto-approve  # 部署资源 - 自动确认, 不需要输入 yes
terraform apply tf.tfplan       # 执行某个计划文件 - 不需要交互确认

terraform destroy                   # 销毁全部资源
terraform destroy --auto-approve

高阶命令

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28

terraform show                      # 显示当前状态
terraform show -json                # 显示状态机读格式
terraform show tfplan               # 显示计划文件内容

terraform init -upgrade     # 更新供应商版本

terraform force-unlock <LOCK_ID>    # 解锁


terraform plan -target=aws_instance.web_server  # 特定资源, 注意会自动关联依赖资源
terraform plan -var-file="prod.tfvars"

terraform plan -destroy             # 生成销毁计划但不执行
terraform plan -destroy -out=tf.tfplan
terraform plan -destroy -out=tf.tfplan -target=resource     # 销毁指定资源

terraform plan -refresh-only        # 执行刷新计划, 只查看状态与云上的差异


# 仅更新本地状态文件,使其与真实云资源重新同步,而不会对实际的云资源做任何修改
terraform apply -refresh-only

terraform apply -parallelism=20         # 并行度控制(默认 10)
terraform apply -target=aws_vpc.main    # 目标特定资源


terraform destroy -target=aws_instance.web_server   # 特定资源

格式化和语法

1
2
3
4
5
6
7
8

terraform fmt               # 语法格式化
terraform fmt -recursive    # 递归格式化子目录
terraform fmt -check        # 检查格式但不修改(用于 CI 检查)
terraform fmt -width 120    # 使用指定格式化宽度

terraform validate          # 验证当前目录下文件, 不会访问云 API 或实际资源
terraform validate /path/to/config  # 验证指定目录配置

state 状态管理

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
terraform state list                            # 列出当前的 state 文件所有管理的项目资源清单
terraform state list -id=vpc-0abcd1234ef567890  # 列出特定资源

terraform state rm alicloud_instance.testecs    # 删除状态内的信息
terraform state rm aws_instance.old_server      # 将资源从状态中移除(但不销毁实际资源)

terraform state show aws_vpc.main               # 显示资源详情

terraform state mv aws_vpc.main aws_vpc.new_name    # 重命名资源名时

# 将现有资源导入状态, 需要目标资源配置模板存在
terraform state import aws_vpc.existing vpc-0abcd1234ef567890

terraformrc 镜像

官方模块仓库的镜像站点配置, 加速下载

~/.terraformrc

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
// 让阿里云相关的 Provider 通过阿里云镜像下载
provider_installation {
  network_mirror {
    url = "https://mirrors.aliyun.com/terraform/"
    include = ["registry.terraform.io/aliyun/alicloud",
               "registry.terraform.io/hashicorp/alicloud"]
  }
  direct {
    exclude = ["registry.terraform.io/aliyun/alicloud",
               "registry.terraform.io/hashicorp/alicloud"]
  }
}

外部工具

tflint

使用 tflint 检查最佳实践

场景

  1. 例如指定的 aws 区域 没有这个 ec2 实例规则时也会检查出错误
1
2
3
4
brew install tflint

tflint --init       # 初始化(下载 Provider 规则插件)
tflint              # 运行检查

driftctl 漂移检测

https://docs.driftctl.com/0.40.0

Driftctl 是专门做漂移检测的开源工具,比 terraform plan 更专业,能并行检测大量资源

Driftctl 还能识别"已删除"和"未管理"的资源

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
curl -L https://github.com/snyk/driftctl/releases/latest/download/driftctl_linux_amd64 -o driftctl

# 扫描
driftctl scan --to aws+tf

driftctl scan --from tfstate://terraform.tfstate
driftctl scan --from tfstate+s3://my-bucket/path/to/state.tfstate
driftctl scan --from tfstate://terraform_S3.tfstate --from tfstate://terraform_VPC.tfstate

driftctl scan --from tfstate://path/to/**/*.tfstate
driftctl scan --from tfstate+s3://path/to/**/*.tfstate

# 输出 JSON
driftctl scan --to aws+tf --output json://drift.json

# 支持上传到 S3 / 发 Slack 通知

infracost成本预估

https://github.com/infracost/infracost

云成本预估

支持 AWS, Azure, GCP 等主流厂商

在 terraform plan 之前查看预期的账单费用

实时估价: 扫描 *.tf 文件 → 调用 Infracost Cloud Pricing API → 输出资源级月费用列表
差异对比: infracost diff 可对比两条 plan 之间的成本差
用量文件: 对 Lambda, S3 等按量计费资源, 可用 YAML 自定义"每月调用次数 / 存储量", 进行估算

1
2
3
4
curl -fsSL https://raw.githubusercontent.com/infracost/infracost/master/scripts/install.sh | sh

# 安装 Infracost
brew install infracost
1
2
3
4
5
6
7
# 注册免费 API Key 后登录
infracost register

infracost auth login

# 浏览器跳转, 完成后 key 写入
~/.config/infracost/credentials.yml
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
# 估算成本
infracost breakdown --path .

# 变更费用评估一
# 进入含 Terraform 代码的目录, 生成 baseline(当前成本快照)
infracost breakdown --path . --format json --out-file infracost-base.json

# 修改 *.tf 后, 查看差异
infracost diff --path . --compare-to infracost-base.json


# 变更费用评估二
在 PR 里直接给出变更前后每月费用的差异
infracost breakdown --path . --format json --out-file infracost.json

infracost diff --path infracost.json

# out
Project: terraform-alicloud
Monthly cost change for this PR: - ¥ 1,243.50
  ├─ alicloud_instance.api[2]       - ¥ 980.00  (changed from ecs.c7.large -> ecs.c7.medium)
  └─ alicloud_slb.lb_public         - ¥ 263.50  (deleted)


# 生成总的费用报告
infracost breakdown --path . --format html > report.html

# 对比不同配置的成本
infracost diff --path . --compare-to terraform.tfstate

# 生成 HTML 报告
infracost output --path /tmp/infracost.json --format html > cost-report.html

其它工具

1
2
3
4
5
6
7
8

# 版本管理工具
tfenv
tfenv list         # 查看已有版本
tfenv use 1.9.5    # 切换到1.9.5版本

# 使用 tfsec 安全扫描
tfsec .

Provider 供应商管理

1
2
3
4
5
https://registry.terraform.io/
https://registry.terraform.io/browse/providers
https://registry.terraform.io/providers/hashicorp/aws/latest/docs
https://registry.terraform.io/providers/aliyun/alicloud/latest/docs
https://registry.terraform.io/providers/hashicorp/kubernetes/latest/docs

Provider 约束

Terraform 会在 .terraform.lock.hcl 文件中锁定版本

1
2
3
4
provider "aws" {
  version = "~> 5.0"   # 允许 5.x 升级, 不跨到 6.x
  region  = "us-west-2"
}

说明

  • 各个 Provider 插件都是独立的进程, 与 Terraform 主进程之间通过 RPC 进行通讯

Terraform 引擎首先读取并分析用户编写的 Terraform 代码, 形成一个由 data 与 resource 组成的图(Graph),
再通过 RPC 调用这些 data 与 resource 所对应的 Provider 插件

市场插件分类

  • Offical 官方插件
  • Partner 第三方认证插件
  • Community 社区版本插件

常见问题处理

1. terraform init 失败

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
# 检查网络连接
curl -I https://registry.terraform.io

# 如果在国内, 配置镜像
# 创建 ~/.terraformrc
cat > ~/.terraformrc << EOF
provider_installation {
  network_mirror {
    url = "https://mirrors.aliyun.com/terraform/"
  }
}
EOF

# 或者使用代理
export HTTPS_PROXY="http://proxy:8080"
terraform init

2. 状态文件损坏

1
2
3
4
5
6
7
8
9
# 从远程后端拉取最新状态
terraform state pull > terraform.tfstate.backup

# 如果状态与实际不符, 导入资源
terraform import aws_vpc.main vpc-xxxxxxxx

# 刷新状态
# terraform refresh         # 旧命令
terraform apply -refresh-only

3. 资源漂移检测

如果有漂移, 需要决定是 更新状态 还是 覆盖资源

1
2
3
4
5
6
7
8
# 检测实际资源与状态的差异
terraform plan -refresh-only

# 更新状态到实际值 - 线上为准
terraform apply -refresh-only

# 或者重新应用配置 - 状态为准
terraform apply

Module 模块

将基础设施配置封装为可重用, 可共享的组件

模块分类

  1. 本地模块: 可以将通过软连接的方式引用本地的源目录(修改后, 无需操作)
  2. git远程模块: 即有版本控制系统
  3. Terraform Registry: 官方仓库模块

管理 cli

1
2
3
4

terraform get                   # 下载模块
terraform graph                 # 查看模块
terraform graph -module-depth   # 汇总对象

模块设计

注意

  1. 避免无意义的模块封装
  2. 一般嵌套不超过 2 层, 维护可读性和可维护性
  3. 环境差异过大时, 最好还是拆分2个模块独立维护来减少代码复杂度

有意义的模块封装

  1. 组合多个官方模块来完成一个项目
  2. 固化公司/团队规范, 资源标准化, 固定的规范参数
  3. 屏蔽多云差异
  4. 可能会大量复用
  5. 屏蔽调用方不需要关心的细节
  6. 封装业务概念, 而不是技术概念

两种主要的 Module 类型

Root Module(根模块): 是 Module 配置的主入口, 负责调用和组织其他模块
Child Module(子模块): 被 Root Module 或其他模块调用的模块, 专注于特定功能, 可被多次重用

组织结构

方式1: 平铺, 阶段式

1
2
3
4
5
6
7
8
9
environments/
├── dev/
│   ├── 01-base/          # 第一步: VPC, 交换机, NAT 网关
│   ├── 02-security/      # 第二步: 安全组, 网络 ACL
│   └── 03-application/   # 第三步: ECS, RDS, 应用资源
└── prod/                 # 生产环境同样的结构
    ├── 01-base/
    ├── 02-security/
    └── 03-application/

方式2: 模块化

1
2
3
4
5
6
7
terraform-project/environment/dev
terraform-project/environment/prod
terraform-project/environment/test1
terraform-project/modules/vpc
terraform-project/modules/rds
terraform-project/modules/ec2
terraform-project/modules/eks

语法

1
2
3
name:  模块的名称, 在 terraform 中可以使用模块名称进行引用
source: 模块代码的路径, 本地或者远程的仓库
version: 版本信息

引用
模块的输出值, 便于其他模块引用
引用方式: module.MODULE_NAME.OUTPUT_NAME

实例化
一个模块可以被多次实例化
每个实例定义唯一的名称, 指定相同的 source 来源

模块版本约束

本地路径模块不支持版本控制

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
# 推荐: 生产环境锁定具体版本
module "vpc_prod" {
  source  = "alibaba/vpc/alicloud"
  version = "1.10.0"  # 锁定精确版本, 确保稳定性
}

# 开发环境: 允许补丁版本更新
module "vpc_dev" {
  source  = "alibaba/vpc/alicloud"
  version = "~> 1.10.0"  # 允许 1.10.x 的补丁更新
}

# 不推荐: 过于宽松的版本约束
module "vpc_risky" {
  source  = "alibaba/vpc/alicloud"
  version = ">= 1.0.0"  # 风险较高, 可能引入破坏性变更
}

source 源

支持

  1. 本地路径
  2. Terraform Registry
  3. git
  4. http
  5. GitHub

基础语法

1
2
3
module "vpc" {
  source = ""
}

本地路径

1
2
    source = "./modules/ecs"
    source = "../modules/ecs"

Terraform Registry

1
2
    source  = "alibaba/ecs-instance/alicloud"
    version = "1.2.0"

github

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
    # HTTPS 方式
    source = "github.com/alibabacloud-automation/terraform-alicloud-ram"

    # SSH 方式
    source = "git@github.com:alibabacloud-automation/terraform-alicloud-ram.git"

    # 引用GitHub仓库中的子模块
    source = "github.com/alibabacloud-automation/terraform-alicloud-ram//modules/ram-user"

    # 通过SSH方式引用特定版本的子模块
    source = "git@github.com:alibabacloud-automation/terraform-alicloud-ram.git//modules/ram-user?ref=v2.1.0"

    source = "git::https://github.com/org/vpc-module.git?ref=v1.2.0"

私有git仓库认证

1
2
3
4
5
export GITHUB_TOKEN="ghp_xxx"

module "vpc" {
  source = "git::https://github.com/my-org/terraform-vpc.git?ref=v1.0.0"
}

通用 Git 仓库

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
    # HTTPS 方式
    source = "git::https://github.com/alibabacloud-automation/terraform-alicloud-vpc.git"

    # SSH 方式
    source = "git::ssh://git@github.com/alibabacloud-automation/terraform-alicloud-vpc.git"

    # 引用Git仓库中的子模块并选择特定版本
    source = "git::https://github.com/alibabacloud-automation/terraform-alicloud-ram.git//modules/ram-user?ref=v2.1.0"

    # 引用私有Git仓库中的子模块
    source = "git::ssh://git@example.com/company/terraform-modules.git//security/waf?ref=v1.0.0"


    # 选择特定版本 - 默认是 默认分支, 选择特定标签
    source = "git::https://github.com/alibabacloud-automation/terraform-alicloud-vpc.git?ref=v1.11.0"

    # 选择特定提交
    source = "git::https://github.com/alibabacloud-automation/terraform-alicloud-vpc.git?ref=51d462976d84fdea54b47d80dcabbf680badcdb8"

    # 浅克隆
    source = "git::https://github.com/alibabacloud-automation/terraform-alicloud-vpc.git?depth=1&ref=v1.11.0"

http

1
2
3
4
5
6
    source = "https://example.com/modules/vpc"

    source = "https://example.com/vpc-module.zip"

    # 从zip归档文件中引用子模块
    source = "https://example.com/vpc-module.zip//modules/subnet"

Terraform Workspace

是同一套代码管理多份独立 State 的内建机制

每个 Workspace 拥有独立的 terraform.tfstate 文件, 在 apply 时互不干扰;

Terraform 默认就有一个名为 default 的 Workspace,
平时使用的 terraform apply 就是在 default Workspace 中操作

注意

  1. 共用一套代码, 逻辑容易因大量 count 和 lookup 变得臃肿难懂
  2. 修改 main.tf 会影响所有环境
  3. 合并代码前, 在所有 Workspace 下执行 plan

因为抽象层度很高, 除非很标准化的场景, 尽量避免使用 workspace 这种模式

实例设计

dev / staging / prod 三环境设计

命名规范: 所有资源名称统一添加 Workspace 前缀, 确保同一账号下资源不冲突:

1
2
3
4
5
6
7
8
9
# 命名模式: landing-zone-{workspace}-{resource}
# 示例:
landing-zone-dev-vpc
landing-zone-dev-sg-app
landing-zone-dev-app-1 # ECS 实例
landing-zone-staging-vpc
landing-zone-staging-nat-gw
landing-zone-prod-vpc
landing-zone-prod-app-rt # 路由表

在代码中通过 local.ws.project_name 自动获取当前环境的项目名前缀, 各模块的资源名均基于 project_name 变量构建

workspace.tf

环境变量映射

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
# ==========================================
# Workspace 环境配置映射
# ==========================================
# 根据当前 workspace 名称自动选择环境参数
# 支持 dev / staging / prod 三个环境
locals {
  # ========================================
  # 各环境的差异化配置
  # ========================================
  workspace_config = {
    dev = {
      environment       = "dev"
      project_name      = "landing-zone-dev"
      vpc_cidr          = "172.16.0.0/16"
      management_cidrs  = ["172.16.0.0/24", "172.16.1.0/24"]
      application_cidrs = ["172.16.10.0/24", "172.16.11.0/24"]
      database_cidrs    = ["172.16.20.0/24", "172.16.21.0/24"]
      ecs_instance_type = "ecs.u1-c1m1.large"
      ecs_image_id      = "rockylinux_9_7_x64_20G_alibase_20260213.vhd"
      ecs_disk_size     = 20
      ecs_count         = 1
      nat_bandwidth     = 5
    }
    staging = {
      environment       = "staging"
      project_name      = "landing-zone-staging"
      vpc_cidr          = "172.17.0.0/16"
      management_cidrs  = ["172.17.0.0/24", "172.17.1.0/24"]
      application_cidrs = ["172.17.10.0/24", "172.17.11.0/24"]
      database_cidrs    = ["172.17.20.0/24", "172.17.21.0/24"]
      ecs_instance_type = "ecs.u1-c1m1.large"
      ecs_image_id      = "rockylinux_9_7_x64_20G_alibase_20260213.vhd"
      ecs_disk_size     = 20
      ecs_count         = 2
      nat_bandwidth     = 10
    }
    prod = {
      environment       = "prod"
      project_name      = "landing-zone-prod"
      vpc_cidr          = "10.0.0.0/16"
      management_cidrs  = ["10.0.0.0/24", "10.0.1.0/24"]
      application_cidrs = ["10.0.10.0/24", "10.0.11.0/24"]
      database_cidrs    = ["10.0.20.0/24", "10.0.21.0/24"]
      ecs_instance_type = "ecs.u1-c1m1.large"
      ecs_image_id      = "rockylinux_9_7_x64_20G_alibase_20260213.vhd"
      ecs_disk_size     = 40
      ecs_count         = 2
      nat_bandwidth     = 50
    }
  }
  # 当前 workspace 对应的配置(将默认 default workspace 映射到 dev)
  ws_name = contains(keys(local.workspace_config), terraform.workspace) ? terraform.workspace : "dev"
  ws      = local.workspace_config[local.ws_name]
}

locals.tf

使用 local.ws 替代硬编码的变量值

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19

locals {
  # 通用标签 — 自动携带当前环境信息
  common_tags = {
    Project     = local.ws.project_name
    Environment = local.ws.environment
    Workspace   = terraform.workspace
    ManagedBy   = "Terraform"
  }
  # 可用区选择 — 筛选条件:
  #   1. 支持指定 ECS 实例规格 + 磁盘类型(来自 alicloud_zones)
  #   2. 镜像存在性由 data.alicloud_images.ecs_image 在 plan 阶段验证
  #   3. 根据环境 ecs_count 决定最终数量
  available_zone_ids = data.alicloud_zones.available_zones.ids
  selected_zones     = slice(
    local.available_zone_ids, 0,
    min(local.ws.ecs_count, length(local.available_zone_ids))
  )
}

main.tf

原来引用 var.xxx 的地方改为引用 local.ws.xxx

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
# ==========================================
# 根模块 - 调用各子模块
# ==========================================
# ==========================================
# 1. VPC 模块
# ==========================================
module "vpc" {
  source = "./modules/vpc"
  vpc_name = "${local.ws.project_name}-vpc"
  vpc_cidr = local.ws.vpc_cidr
  tags     = local.common_tags
}
# ==========================================
# 2. VSwitch 模块 - 管理网段
# ==========================================
module "vswitch_management" {
  source = "./modules/vswitch"
  vpc_id       = module.vpc.vpc_id
  zone_ids     = local.selected_zones
  cidr_blocks  = local.ws.management_cidrs
  subnet_tier  = "management"
  project_name = local.ws.project_name
  tags         = local.common_tags
}

State 隔离机制

Workspace 的核心价值在于自动隔离 State;
不同 Workspace 的 State 完全独立, 对 dev 执行 terraform destroy 不会影响 prod

本地后端 State 结构

1
2
3
4
5
6
7
8
9
terraform-landing-zone/
├── terraform.tfstate                    # default workspace
└── terraform.tfstate.d/
    ├── dev/
    │   └── terraform.tfstate            # dev workspace
    ├── staging/
    │   └── terraform.tfstate            # staging workspace
    └── prod/
        └── terraform.tfstate            # prod workspace

OSS 远程后端

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22

# ==========================================
# 远程状态存储 - 阿里云 OSS
# ==========================================
# Workspace 启用后, State 路径自动变为:
#   terraform-landing-zone/env:/dev/terraform.tfstate
#   terraform-landing-zone/env:/staging/terraform.tfstate
#   terraform-landing-zone/env:/prod/terraform.tfstate
# ==========================================
terraform {
  required_version = ">= 1.0"
  backend "oss" {
    bucket  = "myself-terraform-state"
    prefix  = "terraform-landing-zone"
    region  = "cn-beijing"
    encrypt = true
    # TableStore 状态锁(防止并发修改)
    tablestore_endpoint      = "https://mytfstate.cn-beijing.ots.aliyuncs.com"
    tablestore_instance_name = "mytfstate"
    tablestore_table         = "terraform_state_lock"
  }
}

初始化和操作

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42

# 进入项目目录
cd terraform-landing-zone

terraform init      # 初始化 Terraform(下载 provider 插件)

terraform workspace new dev         # 创建并切换到 dev workspace
terraform plan
terraform apply
terraform output summary            # 检查部署资源

terraform workspace new staging     # 创建 staging Workspace 并部署
terraform plan
terraform apply
terraform output summary

terraform workspace new prod         # 创建并切换到 prod workspace
terraform plan
terraform apply
terraform output summary

terraform workspace select prod      # 手动切换环境

## 删除资源
terraform workspace select prod
terraform destroy

terraform workspace select staging
terraform destroy

terraform workspace select dev
terraform destroy

## 删除 Workspace(State 清空后才能删除)

terraform workspace select default
terraform workspace delete prod
terraform workspace delete staging
terraform workspace delete dev


terraform workspace list    # 确认只剩 default 后, 再删除

不同环境下的资源差异处理

差异 1: 如果只是规则不同

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
locals {
  # 获取当前环境
  env = terraform.workspace

  # 根据不同环境, 定义服务器类型
  server_type = {
    default = "t3.micro"
    dev     = "t3.small"    # dev 环境新增的服务类型
    prod    = "t3.large"
  }

}


resource "aws_instance" "my_server" {
  # 使用 lookup 函数, 找不到时使用 default 值, 优雅降级
  instance_type = lookup(local.server_type, local.env, local.server_type["default"])
}

差异 2: 结构性差异, 通过创建特性开关来处理

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
# locals.tf
locals {
  env = terraform.workspace

  # 定义哪些环境需要这个特殊的"缓存服务"
  enable_redis_cache = {
    dev  = true   # 只在 dev 环境创建
    prod = false
    staging = false
  }

  # 决定是否创建
  should_create_redis = lookup(local.enable_redis_cache, local.env, false)
}

# 只有当 should_create_redis 为 true 时, count 才等于 1, 从而创建资源
resource "aws_elasticache_cluster" "dev_only_cache" {
  count = local.should_create_redis ? 1 : 0

  cluster_id = "dev-cache-cluster"
  engine     = "redis"
  node_type  = "cache.t3.micro"
  num_cache_nodes = 1

  tags = {
    Environment = local.env
    Note        = "This is a DEV-only service"
  }
}

这样在 prod 或 staging 环境下运行 plan 时,
Terraform 会显示这个资源被忽略(0 to add), 不会产生影响, 保证了安全性

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