基础设置即代码, 管理多云环境
一款基础设施即代码(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
|
场景
多公有云场景, 环境复杂度高
不适合场景
- 单云或少量设备, 少量环境
- paas 服务, 不需要关注底层设施
组件
- Provider(供应商): 具体实现的厂家 - 外部环境
- Module 模块: 是可复用的 Terraform 配置单元;
- 状态: Terraform 使用 state 文件 追踪已部署资源
- 数据源: 即只有在运行时才能知道的值
VS Code 集成
HashiCorp Terraform 插件
- 提供 语法高亮 / 自动补全 / 格式化
- 支持直接运行 terraform plan / apply
- 图形化资源依赖关系图
项目组织结构
运行时会读取工作目录中所有的 *.tf, *.tfvars 文件
一般约定
variables.tf 定义变量
terraform.tfvars 传入变量的值, 格式: 简单的键值对
versions.tf 用于约束 tf 的版本和 供应商的版本
output.tf 定义输出内容
provider.tf 云厂商的配置 region 等
terraform.tf tf 环境依赖, 供应商依赖
main.tf 核心逻辑
依赖
- 隐式依赖: Terraform 已知的, 可以自动推导出来的; 通过属性值引用赋值的方式来设置
- 显示依赖: 使用 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"
}
|
一般流程
- 初始化
- 格式化与验证
- 计划与预览
- 执行
初始化
- 下载安装云厂商的对应版本的 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
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 检查最佳实践
场景
- 例如指定的 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
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 模块
将基础设施配置封装为可重用, 可共享的组件
模块分类
- 本地模块: 可以将通过软连接的方式引用本地的源目录(修改后, 无需操作)
- git远程模块: 即有版本控制系统
- Terraform Registry: 官方仓库模块
管理 cli
1
2
3
4
|
terraform get # 下载模块
terraform graph # 查看模块
terraform graph -module-depth # 汇总对象
|
模块设计
注意
- 避免无意义的模块封装
- 一般嵌套不超过 2 层, 维护可读性和可维护性
- 环境差异过大时, 最好还是拆分2个模块独立维护来减少代码复杂度
有意义的模块封装
- 组合多个官方模块来完成一个项目
- 固化公司/团队规范, 资源标准化, 固定的规范参数
- 屏蔽多云差异
- 可能会大量复用
- 屏蔽调用方不需要关心的细节
- 封装业务概念, 而不是技术概念
两种主要的 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 源
支持
- 本地路径
- Terraform Registry
- git
- http
- GitHub
基础语法
1
2
3
|
module "vpc" {
source = ""
}
|
本地路径
1
2
|
source = "./modules/ecs"
source = "../modules/ecs"
|
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"
|
是同一套代码管理多份独立 State 的内建机制
每个 Workspace 拥有独立的 terraform.tfstate 文件, 在 apply 时互不干扰;
Terraform 默认就有一个名为 default 的 Workspace,
平时使用的 terraform apply 就是在 default Workspace 中操作
注意
- 共用一套代码, 逻辑容易因大量 count 和 lookup 变得臃肿难懂
- 修改 main.tf 会影响所有环境
- 合并代码前, 在所有 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), 不会产生影响, 保证了安全性