Terraform 基础设施即代码部署与管理实战

一、概述

Terraform 是 HashiCorp 推出的基础设施即代码(IaC)工具,通过声明式配置文件实现云资源和基础设施的自动化部署与管理。本文详细介绍 Terraform 在生产环境中的部署流程、最佳实践及常见问题解决方案。

1.1 核心优势

  • 声明式配置:描述期望状态,Terraform 自动计算变更
  • 状态管理:通过状态文件追踪资源变更历史
  • 模块化设计:支持代码复用和团队协作
  • 多云支持:统一语法管理 AWS、Azure、GCP、阿里云等

1.2 适用场景

  • 云资源批量部署与变更管理
  • 多环境(dev/staging/prod)基础设施一致性保障
  • 基础设施版本控制与审计
  • 灾难恢复与快速重建

二、环境准备与安装

2.1 系统要求

组件 最低要求 推荐配置
操作系统 Linux/macOS/Windows Ubuntu 22.04+
CPU 2 核 4 核+
内存 2GB 8GB+
磁盘 1GB 10GB+

2.2 安装 Terraform

1
2
3
4
5
6
7
# Ubuntu/Debian
wget -O- https://apt.releases.hashicorp.com/gpg | gpg --dearmor | sudo tee /usr/share/keyrings/hashicorp-archive-keyring.gpg
echo "deb [signed-by=/usr/share/keyrings/hashicorp-archive-keyring.gpg] https://apt.releases.hashicorp.com $(lsb_release -cs) main" | sudo tee /etc/apt/sources.list.d/hashicorp.list
sudo apt update && sudo apt install terraform

# 验证安装
terraform version

2.3 配置 Provider

创建 providers.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
terraform {
required_version = ">= 1.6.0"

required_providers {
aws = {
source = "hashicorp/aws"
version = "~> 5.0"
}
azurerm = {
source = "hashicorp/azurerm"
version = "~> 3.0"
}
aliyun = {
source = "aliyun/aliyun"
version = "~> 1.240.0"
}
}

# 配置远程状态后端(推荐)
backend "s3" {
bucket = "terraform-state-prod"
key = "project/terraform.tfstate"
region = "ap-southeast-1"
}
}

2.4 初始化工作目录

1
2
3
4
5
6
7
8
# 初始化并下载 Provider
terraform init

# 升级 Provider 到最新兼容版本
terraform init -upgrade

# 验证配置
terraform validate

三、核心配置详解

3.1 变量定义(variables.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
variable "environment" {
description = "部署环境"
type = string
default = "prod"

validation {
condition = contains(["dev", "staging", "prod"], var.environment)
error_message = "环境必须是 dev、staging 或 prod"
}
}

variable "instance_type" {
description = "EC2 实例类型"
type = string
default = "t3.medium"
}

variable "vpc_cidr" {
description = "VPC CIDR 块"
type = string
default = "10.0.0.0/16"
}

variable "tags" {
description = "资源标签"
type = map(string)
default = {
Project = "MyApp"
ManagedBy = "Terraform"
}
}

3.2 本地值(locals.tf)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
locals {
common_tags = merge(
var.tags,
{
Environment = var.environment
CreatedAt = timestamp()
}
)

instance_config = {
dev = { type = "t3.small", count = 1 }
staging = { type = "t3.medium", count = 2 }
prod = { type = "t3.large", count = 4 }
}

current_instance = local.instance_config[var.environment]
}

3.3 资源定义(main.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
55
56
57
58
59
60
61
62
63
# VPC
resource "aws_vpc" "main" {
cidr_block = var.vpc_cidr
enable_dns_hostnames = true
enable_dns_support = true

tags = local.common_tags
}

# 子网
resource "aws_subnet" "public" {
count = 2
vpc_id = aws_vpc.main.id
cidr_block = cidrsubnet(var.vpc_cidr, 8, count.index)
availability_zone = data.aws_availability_zones.available.names[count.index]

tags = merge(local.common_tags, {
Type = "Public"
})
}

# 安全组
resource "aws_security_group" "app" {
name = "${var.environment}-app-sg"
description = "应用安全组"
vpc_id = aws_vpc.main.id

ingress {
from_port = 443
to_port = 443
protocol = "tcp"
cidr_blocks = ["0.0.0.0/0"]
}

egress {
from_port = 0
to_port = 0
protocol = "-1"
cidr_blocks = ["0.0.0.0/0"]
}

tags = local.common_tags
}

# EC2 实例
resource "aws_instance" "app" {
count = local.current_instance.count
ami = data.aws_ami.ubuntu.id
instance_type = local.current_instance.type
subnet_id = aws_subnet.public[count.index % 2].id

vpc_security_group_ids = [aws_security_group.app.id]

root_block_device {
volume_type = "gp3"
volume_size = 50
encrypted = true
}

tags = merge(local.common_tags, {
Name = "${var.environment}-app-${count.index + 1}"
})
}

3.4 数据源查询(data.tf)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
data "aws_ami" "ubuntu" {
most_recent = true
owners = ["099720109477"] # Canonical

filter {
name = "name"
values = ["ubuntu/images/hvm-ssd/ubuntu-jammy-22.04-amd64-server-*"]
}
}

data "aws_availability_zones" "available" {
state = "available"
}

data "aws_caller_identity" "current" {}

3.5 输出定义(outputs.tf)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
output "vpc_id" {
description = "VPC ID"
value = aws_vpc.main.id
}

output "public_subnet_ids" {
description = "公共子网 ID 列表"
value = aws_subnet.public[*].id
}

output "instance_public_ips" {
description = "实例公网 IP 列表"
value = aws_instance.app[*].public_ip
}

output "security_group_id" {
description = "安全组 ID"
value = aws_security_group.app.id
}

四、模块化部署

4.1 创建可复用模块

目录结构:

1
2
3
4
5
6
modules/
└── vpc/
├── main.tf
├── variables.tf
├── outputs.tf
└── README.md

modules/vpc/main.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
resource "aws_vpc" "this" {
cidr_block = var.cidr_block
enable_dns_hostnames = true
enable_dns_support = true

tags = var.tags
}

resource "aws_internet_gateway" "this" {
vpc_id = aws_vpc.this.id

tags = var.tags
}

resource "aws_route_table" "public" {
vpc_id = aws_vpc.this.id

route {
cidr_block = "0.0.0.0/0"
gateway_id = aws_internet_gateway.this.id
}

tags = var.tags
}

modules/vpc/variables.tf:

1
2
3
4
5
6
7
8
9
10
variable "cidr_block" {
description = "VPC CIDR"
type = string
}

variable "tags" {
description = "资源标签"
type = map(string)
default = {}
}

modules/vpc/outputs.tf:

1
2
3
4
5
6
7
output "vpc_id" {
value = aws_vpc.this.id
}

output "route_table_id" {
value = aws_route_table.public.id
}

4.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
module "vpc" {
source = "./modules/vpc"
cidr_block = "10.0.0.0/16"
tags = {
Name = "production-vpc"
}
}

module "eks" {
source = "terraform-aws-modules/eks/aws"
version = "~> 20.0"

cluster_name = "prod-cluster"
cluster_version = "1.28"
vpc_id = module.vpc.vpc_id

subnet_ids = module.vpc.subnet_ids

eks_managed_node_groups = {
default = {
instance_types = ["t3.medium"]
min_size = 2
max_size = 10
desired_size = 3
}
}
}

五、状态管理最佳实践

5.1 远程状态配置

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
# S3 + DynamoDB(AWS)
terraform {
backend "s3" {
bucket = "terraform-state-prod"
key = "infra/terraform.tfstate"
region = "ap-southeast-1"
encrypt = true
dynamodb_table = "terraform-locks"
}
}

# Azure Blob Storage
terraform {
backend "azurerm" {
resource_group_name = "tfstate-rg"
storage_account_name = "tfstateprod"
container_name = "tfstate"
key = "infra.terraform.tfstate"
}
}

5.2 状态锁定

使用 DynamoDB 防止并发修改:

1
2
3
4
5
6
7
8
9
10
resource "aws_dynamodb_table" "terraform_locks" {
name = "terraform-locks"
billing_mode = "PAY_PER_REQUEST"
hash_key = "LockID"

attribute {
name = "LockID"
type = "S"
}
}

5.3 状态文件操作

1
2
3
4
5
6
7
8
9
10
11
12
13
14
# 查看资源列表
terraform state list

# 查看资源详情
terraform state show aws_instance.app[0]

# 移动资源(重构时)
terraform state mv aws_instance.old aws_instance.new

# 从状态中移除(不删除实际资源)
terraform state rm aws_instance.deprecated

# 导入现有资源
terraform import aws_instance.existing i-1234567890abcdef0

六、工作流与协作

6.1 标准操作流程

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. 拉取最新代码
git pull origin main

# 2. 初始化(首次或切换分支时)
terraform init

# 3. 格式化代码
terraform fmt -recursive

# 4. 验证配置
terraform validate

# 5. 查看变更计划
terraform plan -out=tfplan

# 6. 审查计划(人工)
terraform show tfplan

# 7. 应用变更
terraform apply tfplan

# 8. 提交代码
git add . && git commit -m "feat: add new resources"
git push origin main

6.2 多环境管理

目录结构:

1
2
3
4
5
6
7
8
9
10
environments/
├── dev/
│ ├── terraform.tfvars
│ └── backend.tf
├── staging/
│ ├── terraform.tfvars
│ └── backend.tf
└── prod/
├── terraform.tfvars
└── backend.tf

dev/terraform.tfvars:

1
2
3
environment = "dev"
instance_type = "t3.small"
vpc_cidr = "10.0.0.0/16"

prod/terraform.tfvars:

1
2
3
environment = "prod"
instance_type = "t3.large"
vpc_cidr = "10.1.0.0/16"

6.3 CI/CD 集成

GitLab CI 示例(.gitlab-ci.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
33
34
35
36
stages:
- validate
- plan
- apply

variables:
TF_IN_AUTOMATION: "true"
TF_INPUT: "false"

validate:
stage: validate
script:
- terraform init
- terraform fmt -check -recursive
- terraform validate

plan:
stage: plan
script:
- terraform init
- terraform plan -out=tfplan
artifacts:
paths:
- tfplan
expire_in: 1 hour
when: manual

apply:
stage: apply
script:
- terraform init
- terraform apply -auto-approve tfplan
dependencies:
- plan
when: manual
environment: production

七、常见问题与解决方案

7.1 Provider 认证问题

问题Error: Invalid provider configuration

解决方案

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
# AWS - 使用环境变量
export AWS_ACCESS_KEY_ID=your_key
export AWS_SECRET_ACCESS_KEY=your_secret
export AWS_DEFAULT_REGION=ap-southeast-1

# 或使用配置文件
mkdir -p ~/.aws
cat > ~/.aws/credentials << EOF
[default]
aws_access_key_id = your_key
aws_secret_access_key = your_secret
EOF

# Azure
export ARM_CLIENT_ID=xxx
export ARM_CLIENT_SECRET=xxx
export ARM_SUBSCRIPTION_ID=xxx
export ARM_TENANT_ID=xxx

7.2 状态文件损坏

问题Error: Failed to load state

解决方案

1
2
3
4
5
6
7
8
9
# 1. 备份当前状态
cp terraform.tfstate terraform.tfstate.backup

# 2. 尝试修复
terraform state pull > state.json
terraform state push state.json

# 3. 如果严重损坏,从远程备份恢复
aws s3 cp s3://bucket/terraform.tfstate.backup ./terraform.tfstate

7.3 资源漂移检测

问题:手动变更导致状态不一致

解决方案

1
2
3
4
5
6
7
8
# 定期检测漂移
terraform plan -refresh-only -out=drift.tfplan
terraform show drift.tfplan

# 自动修复(谨慎使用)
terraform apply -refresh-only -auto-approve

# 建议:设置定期 CI 任务检测漂移

7.4 大规模资源管理

问题:资源数量过多导致 plan/apply 缓慢

解决方案

1
2
3
4
5
6
7
8
9
# 使用 target 定向操作
terraform apply -target=aws_instance.app
terraform apply -target=module.vpc

# 并行操作(谨慎)
export TF_PARALLELISM=10

# 拆分状态文件
# 按功能模块分离:network/, compute/, database/

7.5 敏感信息管理

问题:密钥硬编码在配置中

解决方案

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
# 使用变量 + 环境变量
variable "db_password" {
type = string
sensitive = true
}

# 使用 Secrets Manager
data "aws_secretsmanager_secret_version" "db_cred" {
secret_id = "prod/db/credentials"
}

resource "aws_db_instance" "main" {
password = data.aws_secretsmanager_secret_version.db_cred.secret_string
}

# 使用 Vault Provider
provider "vault" {
address = "https://vault.example.com"
}

data "vault_generic_secret" "db" {
path = "secret/data/prod/db"
}

八、安全最佳实践

8.1 最小权限原则

IAM Policy 示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": [
"ec2:Describe*",
"ec2:RunInstances",
"ec2:TerminateInstances"
],
"Resource": "*"
},
{
"Effect": "Allow",
"Action": [
"s3:GetObject",
"s3:PutObject"
],
"Resource": "arn:aws:s3:::terraform-state-prod/*"
}
]
}

8.2 状态文件加密

1
2
3
4
5
6
7
8
9
10
11
# S3 服务端加密
backend "s3" {
bucket = "terraform-state-prod"
encrypt = true
kms_key_id = "arn:aws:kms:region:account:key/key-id"
}

# Azure 加密
backend "azurerm" {
# 默认启用加密
}

8.3 审计与合规

1
2
3
4
5
6
7
8
9
10
# 启用 CloudTrail 审计
resource "aws_cloudtrail" "terraform_audit" {
name = "terraform-audit"
s3_bucket_name = aws_s3_bucket.audit.id
include_global_service_events = true
is_multi_region_trail = true
}

# 使用 tfsec 进行安全扫描
tfsec . --format sarif --out tfsec-results.sarif

九、性能优化

9.1 加速 Plan/Apply

1
2
3
4
5
6
7
8
# 使用 -parallelism 参数
terraform apply -parallelism=25

# 跳过不必要的刷新
terraform plan -refresh=false # 仅当确信状态一致时

# 使用模块缓存
export TF_PLUGIN_CACHE_DIR=~/.terraform.d/plugin-cache

9.2 优化模块结构

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# 避免过度嵌套
# 不好:module.a.module.b.module.c
# 好:扁平化调用

# 使用 for_each 替代 count(更稳定)
resource "aws_instance" "app" {
for_each = var.instances

ami = data.aws_ami.ubuntu.id
instance_type = each.value.type

tags = {
Name = each.key
}
}

十、总结

Terraform 作为基础设施即代码的核心工具,能够显著提升运维效率和基础设施一致性。关键要点:

  1. 状态管理:使用远程后端 + 状态锁定
  2. 模块化:复用代码,降低维护成本
  3. 版本控制:所有配置纳入 Git 管理
  4. CI/CD 集成:自动化验证和部署
  5. 安全优先:敏感信息使用 Secrets 管理
  6. 定期审计:检测资源漂移,确保合规

通过遵循本文的最佳实践,可以构建可靠、可维护的基础设施自动化体系。

参考资源