教程雨

OKX新手入门教程导航,收录OKX注册、充值、买币、提现等基础操作教程

Terraform基础设施即代码开发场景,展示多云资源管理

Terraform基础设施即代码完整教程:2026年云资源管理的最佳实践

前言

我第一次接触Terraform是在三年前,当时公司要把基础设施从手动管理切换到代码化管理。之前的运维同事每次部署都要手动在控制台点来点去,环境一多就容易出错,而且出了问题很难回滚。那次迁移之后,我算是真正体会到了什么叫“基础设施即代码”。

Terraform的好处,用过的人都知道:版本控制、回滚方便、环境一致、协作透明。但真正上手时会发现,想要用好Terraform并不是简单写几个配置文件就行的。状态管理、模块设计、多环境配置、团队协作……这些才是真正的挑战。

这篇文章,我想从自己的踩坑经验出发,讲讲Terraform的使用心得。文章内容会比较偏实战,不会有太多官方文档里都能查到的概念解释,更多是告诉你“为什么要这样做”以及“我在实际工作中是怎么做的”。

Terraform IaC工作流程图,从代码定义到多云部署的完整管线

一、为什么选择Terraform

在说Terraform之前,先聊聊基础设施即代码这个概念。传统的运维模式是登录控制台手动操作,好处是直观,坏处是:

  • 环境不一致:开发、测试、生产环境配置可能完全不同
  • 不可重复:手动操作难以保证每次部署结果相同
  • 难以回滚:出问题只能手动恢复,容易出错
  • 知识丢失:只有操作者知道怎么配置的,换人就抓瞎

IaC(Infrastructure as Code)就是来解决这些问题的。把基础设施的定义写成代码,版本控制系统管理起来,每次部署从代码创建,环境完全一致,出问题也能通过回滚代码来解决。

Terraform是HashiCorp公司开发的IaC工具,跟其他方案相比,它有几个显著优势:

声明式配置。你描述的是最终状态,而不是操作步骤。Terraform会自动计算如何从当前状态到达目标状态。比如你声明要有3台服务器,Terraform会自动创建,不会让你手动去数。

多云支持。AWS、GCP、Azure、阿里云、腾讯云……主流云厂商基本都支持。一套配置管理所有云资源,不用学多套工具。

Plan预览。执行前会先显示计划,让你确认要做什么改动,避免误操作。

状态管理。Terraform会跟踪资源状态,知道哪些需要创建、修改还是删除。

当然,Terraform也有缺点:学习曲线相对陡峭,状态文件管理需要额外注意,对于一些云厂商的高级功能支持可能不够及时。但总体来说,Terraform是当前最成熟的IaC解决方案,值得投入时间学习。

二、核心概念快速入门

2.1 HCL语法基础

Terraform使用HCL(HashiCorp Configuration Language)作为配置文件格式。HCL设计得很直观,对开发者友好。

变量定义

hcl

variable "region" {
  description = "AWS区域"
  type        = string
  default     = "us-east-1"
}

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

资源定义

hcl

resource "aws_instance" "web_server" {
  ami           = "ami-0c55b159cbfafe1f0"
  instance_type = var.instance_type
  
  tags = {
    Name        = "web-server"
    Environment = "production"
    ManagedBy   = "terraform"
  }
}

输出值

hcl

output "instance_public_ip" {
  description = "实例公网IP地址"
  value       = aws_instance.web_server.public_ip
}

HCL的基本语法很简洁:类型关键字 + 名称 + 大括号内的配置。学过JSON或者YAML的话,应该很快能上手。

2.2 Provider是什么

Provider是Terraform与各云服务商之间的桥梁。每个Provider负责管理对应的资源类型。比如AWS Provider知道怎么创建EC2、RDS、S3等AWS资源,Azure Provider知道怎么创建Azure虚拟机、存储账户等。

使用Provider很简单,在配置文件中声明即可:

hcl

terraform {
  required_providers {
    aws = {
      source  = "hashicorp/aws"
      version = "~> 5.0"
    }
  }
}

provider "aws" {
  region = var.region
  
  # 可以配置多个账号、角色等
  alias = "prod"
}

terraform init时,Terraform会自动下载对应的Provider插件。第一次初始化会慢一些,之后会缓存到本地。

2.3 Terraform工作流

Terraform的标准工作流分为四步:

第一步,编写配置。创建.tf文件,定义基础设施。

第二步,terraform init。初始化工作目录,下载Provider插件。

第三步,terraform plan。生成执行计划,Preview要做的改动。

第四步,terraform apply。执行计划,创建/修改/删除资源。

plaintext

# 基本工作流示例
$ terraform init

$ terraform plan -out=tfplan

$ terraform apply tfplan

plan阶段的输出非常重要,建议仔细阅读,确认改动的资源和数量是否符合预期。特别是生产环境,养成先plan再apply的习惯。

三、实战:从创建一台EC2说起

3.1 最小化配置

先用最少的配置创建一台EC2实例:

hcl

terraform {
  required_version = ">= 1.5.0"
  
  required_providers {
    aws = {
      source  = "hashicorp/aws"
      version = "~> 5.0"
    }
  }
}

provider "aws" {
  region = "us-east-1"
}

resource "aws_instance" "example" {
  ami           = "ami-0c55b159cbfafe1f0"
  instance_type = "t3.micro"
  
  tags = {
    Name = "terraform-example"
  }
}

output "instance_id" {
  value = aws_instance.example.id
}

保存为main.tf,然后执行:

bash

terraform init
terraform plan
terraform apply

输入yes确认后,Terraform会创建EC2实例。完成后,输出会显示实例ID。

3.2 添加更多配置

基础的EC2用处不大,通常还需要:

  • 安全组:控制端口访问
  • 弹性IP:固定公网IP
  • 用户数据:初始化脚本

hcl

# 安全组
resource "aws_security_group" "web" {
  name        = "web-sg"
  description = "Web服务器安全组"
  
  ingress {
    from_port   = 80
    to_port     = 80
    protocol    = "tcp"
    cidr_blocks = ["0.0.0.0/0"]
  }
  
  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 = {
    Name = "web-sg"
  }
}

# EC2实例
resource "aws_instance" "web" {
  ami           = "ami-0c55b159cbfafe1f0"
  instance_type = "t3.micro"
  vpc_security_group_ids = [aws_security_group.web.id]
  
  user_data = <<-EOF
              #!/bin/bash
              yum install -y httpd
              systemctl start httpd
              systemctl enable httpd
              EOF
  
  tags = {
    Name = "web-server"
  }
}

# 弹性IP
resource "aws_eip" "web" {
  instance = aws_instance.web.id
}

output "public_ip" {
  value = aws_eip.web.public_ip
}

这个配置创建了一台带安全组和弹性IP的Web服务器。安全组开放了80和443端口,用户数据脚本自动安装了Apache并启动服务。

3.3 常用命令一览

Terraform的命令行工具很丰富,以下是日常使用最频繁的几个:

命令用途
terraform init初始化工作目录
terraform plan生成执行计划
terraform apply执行计划
terraform destroy删除所有资源
terraform show查看当前状态
terraform output查看输出值
terraform refresh同步远程状态
terraform validate验证配置语法
terraform fmt格式化配置文件

bash

# 常用命令组合
terraform init && terraform plan   # 初始化并查看计划
terraform apply -auto-approve      # 自动确认执行
terraform destroy -target=aws_instance.web  # 只删除特定资源
terraform output public_ip         # 查看输出值

destroy命令很危险,生产环境执行前一定要确认。可以用-target参数限定删除范围,或者提前备份状态文件。

四、状态管理:Terraform的精髓

4.1 为什么要管理状态

Terraform用状态文件(terraform.tfstate)来跟踪资源信息。每次执行plan或apply,Terraform会用当前配置与状态文件对比,计算需要做什么改动。

状态文件不仅记录资源ID,还缓存了资源的属性信息。比如EC2实例的私有IP、公有IP等信息都会保存在状态里,不需要每次都查询云API。

本地状态只适合个人学习或单机环境。团队协作和生产环境必须用远程状态:

  • S3 + DynamoDB(AWS原生方案)
  • Azure Blob Storage
  • Google Cloud Storage
  • Terraform Cloud
  • MinIO(私有化部署)

4.2 配置远程状态

以AWS S3为例,配置远程状态:

hcl

terraform {
  backend "s3" {
    bucket         = "my-terraform-state"
    key            = "prod/terraform.tfstate"
    region         = "us-east-1"
    encrypt        = true
    dynamodb_table = "terraform-locks"
  }
}

这里同时配置了DynamoDB表来管理锁,防止多人同时操作导致状态冲突。

创建S3 Bucket和DynamoDB表的配置:

hcl

# S3 Bucket
resource "aws_s3_bucket" "terraform_state" {
  bucket = "my-terraform-state"
  
  lifecycle {
    prevent_destroy = true
  }
}

resource "aws_s3_bucket_versioning" "terraform_state" {
  bucket = aws_s3_bucket.terraform_state.id
  
  versioning_configuration {
    status = "Enabled"
  }
}

resource "aws_s3_bucket_server_side_encryption_configuration" "terraform_state" {
  bucket = aws_s3_bucket.terraform_state.id
  
  rule {
    apply_server_side_encryption_by_default {
      sse_algorithm = "AES256"
    }
  }
}

# DynamoDB表(用于状态锁)
resource "aws_dynamodb_table" "terraform_locks" {
  name         = "terraform-locks"
  billing_mode = "PAY_PER_REQUEST"
  hash_key     = "LockID"
  
  attribute {
    name = "LockID"
    type = "S"
  }
}

配置好远程状态后,每次terraform apply都会自动锁定状态,其他成员无法同时操作,解决了团队协作的问题。

4.3 状态隔离策略

大型项目通常需要多个环境(开发、测试、生产),每个环境应该有独立的状态文件。推荐的结构:

plaintext

.
├── envs/
│   ├── dev/
│   │   ├── main.tf
│   │   └── variables.tf
│   ├── staging/
│   │   ├── main.tf
│   │   └── variables.tf
│   └── prod/
│       ├── main.tf
│       └── variables.tf
└── modules/
    ├── vpc/
    ├── ec2/
    └── rds/

每个环境的配置放在独立目录,通过不同的backend key区分状态文件。modules目录存放可复用的模块,供各环境引用。

五、模块化设计:复用与封装

5.1 为什么要用模块

Terraform的模块(Module)是对一组资源的封装。不用模块的话,每次创建类似的资源都要重复写配置,修改也要改多处。用了模块,一个地方改,所有引用都生效。

举一个实际的例子:Web服务通常需要EC2 + 安全组 + 弹性IP的组合。不用模块时:

hcl

# 环境A
resource "aws_instance" "web_a" { ... }
resource "aws_security_group" "web_a" { ... }
resource "aws_eip" "web_a" { ... }

# 环境B
resource "aws_instance" "web_b" { ... }
resource "aws_security_group" "web_b" { ... }
resource "aws_eip" "web_b" { ... }

用模块后:

hcl

# 定义模块
# modules/web-server/main.tf
variable "name" {}
variable "instance_type" {}

resource "aws_instance" "this" {
  ami           = "ami-0c55b159cbfafe1f0"
  instance_type = var.instance_type
  # ...
}

# 引用模块
module "web_a" {
  source        = "../../modules/web-server"
  name          = "web-a"
  instance_type = "t3.micro"
}

module "web_b" {
  source        = "../../modules/web-server"
  name          = "web-b"
  instance_type = "t3.small"
}

模块让代码更简洁,配置更一致,修改更方便。

5.2 模块设计原则

好的模块设计应该:

单一职责。每个模块只做一件事。比如VPC模块只创建网络,RDS模块只创建数据库。组合使用时再拼装成完整的基础设施。

合理暴露参数。模块内部应该隐藏细节,只暴露必要的配置项。暴露太少模块不灵活,暴露太多使用复杂。通常只暴露业务相关的参数,如实例数量、实例规格等,技术细节由模块内部处理。

hcl

# modules/web-cluster/main.tf
variable "cluster_name" {
  description = "集群名称"
  type        = string
}

variable "instance_count" {
  description = "实例数量"
  type        = number
  default     = 2
}

variable "instance_type" {
  description = "实例规格"
  type        = string
  default     = "t3.micro"
}

# 内部资源,对外隐藏
resource "aws_launch_template" "this" { ... }
resource "aws_autoscaling_group" "this" { ... }
resource "aws_security_group" "this" { ... }

output "asg_name" {
  value = aws_autoscaling_group.this.name
}

版本管理。Terraform支持模块版本控制。使用远程模块时指定版本:

hcl

module "vpc" {
  source  = "terraform-aws-modules/vpc/aws"
  version = "~> 5.0"
  
  name = "my-vpc"
  cidr = "10.0.0.0/16"
}

Terraform Registry上有大量官方和社区维护的模块,如terraform-aws-modules/vpc、terraform-aws-modules/ecs等,开箱即用,可以参考学习。

六、变量与条件逻辑

6.1 变量使用技巧

Terraform的变量系统很灵活,支持多种类型和默认值:

字符串变量

hcl

variable "environment" {
  type    = string
  default = "dev"
  
  validation {
    condition     = contains(["dev", "staging", "prod"], var.environment)
    error_message = "Environment must be dev, staging, or prod."
  }
}

对象变量

hcl

variable "web_server" {
  type = object({
    instance_type = string
    ami_id        = string
    volume_size   = number
  })
  
  default = {
    instance_type = "t3.micro"
    ami_id        = "ami-0c55b159cbfafe1f0"
    volume_size   = 20
  }
}

# 使用
resource "aws_instance" "web" {
  instance_type = var.web_server.instance_type
  ami           = var.web_server.ami_id
  root_block_device {
    volume_size = var.web_server.volume_size
  }
}

Map和列表变量

hcl

variable "environment_config" {
  type = map(object({
    instance_type = string
    desired_capacity = number
  }))
  
  default = {
    dev = {
      instance_type     = "t3.micro"
      desired_capacity   = 1
    }
    prod = {
      instance_type     = "t3.large"
      desired_capacity   = 3
    }
  }
}

# 使用
locals {
  config = var.environment_config[terraform.workspace]
}

6.2 条件表达式

Terraform支持条件表达式,可以根据变量值决定创建什么资源:

hcl

# 根据环境决定是否创建公有IP
resource "aws_eip" "web" {
  count = var.enable_public_ip ? 1 : 0
  instance = aws_instance.web.id
}

# 根据条件选择不同的配置
locals {
  instance_type = var.environment == "prod" ? "t3.large" : "t3.micro"
}

6.3 循环与动态块

Terraform支持count和for_each两种循环方式:

count方式

hcl

resource "aws_security_group_rule" "ingress" {
  count = length(var.allowed_ports)
  
  type              = "ingress"
  from_port         = var.allowed_ports[count.index]
  to_port           = var.allowed_ports[count.index]
  protocol          = "tcp"
  cidr_blocks       = ["0.0.0.0/0"]
  security_group_id = aws_security_group.main.id
}

for_each方式(推荐)

hcl

resource "aws_security_group_rule" "ingress" {
  for_each = toset(var.allowed_ports)
  
  type              = "ingress"
  from_port         = each.value
  to_port           = each.value
  protocol          = "tcp"
  cidr_blocks       = ["0.0.0.0/0"]
  security_group_id = aws_security_group.main.id
}

for_each比count更清晰,删除某个元素时不会导致其他资源重建。Terraform 0.12之后的版本应该优先使用for_each。

动态块:当需要根据列表生成嵌套配置时使用:

hcl

resource "aws_lambda_function" "this" {
  # ... 基础配置
  
  dynamic "environment" {
    for_each = var.environment_variables
    content {
      variables = environment.value
    }
  }
}

七、工作区与多环境管理

7.1 Terraform工作区

Terraform内置了工作区(Workspace)功能,可以在一份配置下管理多个环境的状态:

bash

terraform workspace list    # 列出所有工作区
terraform workspace new dev  # 创建新工作区
terraform workspace select dev  # 切换工作区
terraform workspace show    # 显示当前工作区

配合不同的backend key,工作区可以实现状态隔离:

hcl

terraform {
  backend "s3" {
    bucket = "my-terraform-state"
    key    = "${terraform.workspace}/terraform.tfstate"
    region = "us-east-1"
  }
}

dev环境的状态会保存在dev/terraform.tfstate,prod环境的状态保存在prod/terraform.tfstate。

7.2 工作区的注意事项

工作区适合环境间配置差异较小的情况。如果环境间差异很大,比如生产环境用不同规格的实例、更严格的安全策略等,硬要用工作区会导致配置里充满条件判断,代码变得难维护。

对于差异大的环境,推荐使用目录隔离(前面提到的envs结构)。每个环境独立配置,共享的逻辑抽成模块复用。两种方式各有适用场景,根据实际情况选择。

八、最佳实践总结

8.1 版本控制

  • 所有.tf文件都加入Git管理
  • .gitignore排除.tfstate文件和敏感信息
  • 每次重大变更单独提交,方便回滚
  • 使用语义化的提交信息

8.2 敏感信息处理

不要把密钥、密码等敏感信息写在配置文件里。Terraform支持从环境变量或密钥管理服务读取:

hcl

# 从环境变量读取
provider "aws" {
  secret_key = var.aws_secret_key
  token      = var.aws_session_token
}

# 从AWS Secrets Manager读取
data "aws_secretsmanager_secret_version" "db_password" {
  secret_id = "prod/db-password"
}

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

8.3 团队协作规范

  • 代码Review:所有变更经过Review再合并
  • 环境分离:开发和测试环境可以随意操作,生产环境必须谨慎
  • 状态锁:确保backend配置了状态锁
  • 文档:复杂逻辑添加注释,重要决策记录在README

结语

Terraform这几年用下来,最大的感受是:它真的把运维从“体力活”变成了“技术活”。以前部署一个环境要一整天,现在可能只需要几十分钟;以前出了问题手忙脚乱,现在一个terraform destroy就能回到干净的状态。

当然,Terraform不是银弹,它有学习成本,有状态管理的复杂度,有云厂商API的限制。但相比带来的收益,这些问题都是可以接受的。

如果你是第一次接触Terraform,建议从简单的单资源开始练习,熟悉了基本操作后再尝试多资源组合、多环境管理。遇到问题多查官方文档,Terraform的文档质量很高,基本能覆盖所有常见场景。

基础设施即代码是大势所趋,早点掌握早点受益。希望这篇教程能帮你少走一些弯路。

相关资源推荐:

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注