1. 概述

随着 AWS、Google Cloud 和 Microsoft Azure 等公有云服务的普及,基础设施即代码(Infrastructure-as-Code, IaC)已经成为主流实践。IaC 的核心思想是:像管理应用代码一样管理基础设施资源(计算、网络、存储等)

本文将简要介绍 Terraform —— 一个在 DevOps 团队中广泛使用的基础设施自动化工具。Terraform 的最大优势在于我们只需声明“基础设施应该是什么样子”,工具会自动决定如何实现这个目标。

2. 简要发展史

根据 GitHub 的记录,Terraform 的第一次提交是在 2014 年 5 月 21 日,由 HashiCorp 的联合创始人 Mitchell Hashimoto 提交,当时只有一个 README 文件,其中写到:

Terraform 是一个用于安全、高效地构建和变更基础设施的工具。

从那以后,Terraform 支持的基础设施提供商数量持续增长。截至本文撰写时,Terraform 官方支持约 130 个提供商,社区支持的还有约 160 个。有些提供商只暴露少量资源,而像 AWS 或 Azure 这样的大型云平台则提供了数百种资源类型。

如此庞大的资源支持,使得 Terraform 成为许多 DevOps 工程师的首选工具。使用一个工具统一管理多个云平台的资源,也是其一大优势。

3. 第一个 Terraform 项目

在深入细节之前,我们先来创建一个简单的“Hello World”风格的 Terraform 项目,体验其基本流程。

3.1. 下载与安装

Terraform 发行版只有一个可执行文件,可以从 HashiCorp 的下载页面免费获取。无需任何依赖,只需将可执行文件复制到系统 PATH 中的任意目录即可。

安装完成后,运行以下命令验证是否安装成功:

$ terraform -v
Terraform v0.12.24

如看到类似输出,说明已安装成功 ✅。

3.2. 创建第一个项目

一个 Terraform 项目就是包含资源定义的一组文件,这些文件通常以 .tf 结尾,使用 Terraform 的配置语言进行定义

我们来创建一个简单的项目,目标是生成一个内容为 “Hello, Terraform” 的文件:

$ cd $HOME
$ mkdir hello-terraform
$ cd hello-terraform
$ cat > main.tf <<EOF
provider "local" {
  version = "~> 1.4"
}
resource "local_file" "hello" {
  content = "Hello, Terraform"
  filename = "hello.txt"
}
EOF

main.tf 文件包含两个块(block):

  • provider:声明使用 local 提供商(版本 1.4 或兼容版本)
  • resource:定义了一个名为 hellolocal_file 类型资源,内容为 "Hello, Terraform",保存为 hello.txt

3.3. 初始化、计划与应用

初始化项目

$ terraform init

Initializing the backend...

Initializing provider plugins...
- Checking for available provider plugins...
- Downloading plugin for provider "local" (hashicorp/local) 1.4.0...

Terraform has been successfully initialized!

这一步会自动下载所需的 provider 插件。

查看执行计划

$ terraform plan
...
Terraform will perform the following actions:

  # local_file.hello will be created
  + resource "local_file" "hello" {
      + content              = "Hello, Terraform"
      + directory_permission = "0777"
      + file_permission      = "0777"
      + filename             = "hello.txt"
      + id                   = (known after apply)
    }

Plan: 1 to add, 0 to change, 0 to destroy.

这是“干跑”(dry run)过程,用于预览即将执行的操作。

应用配置

$ terraform apply

An execution plan has been generated and is shown below.
Resource actions are indicated with the following symbols:
  + create

Terraform will perform the following actions:

  # local_file.hello will be created
  + resource "local_file" "hello" {
      + content              = "Hello, Terraform"
      + directory_permission = "0777"
      + file_permission      = "0777"
      + filename             = "hello.txt"
      + id                   = (known after apply)
    }

Plan: 1 to add, 0 to change, 0 to destroy.

Do you want to perform these actions?
  Terraform will perform the actions described above.
  Only 'yes' will be accepted to approve.

  Enter a value: yes

local_file.hello: Creating...
local_file.hello: Creation complete after 0s [id=392b5481eae4ab2178340f62b752297f72695d57]

Apply complete! Resources: 1 added, 0 changed, 0 destroyed.

执行完成后,检查生成的文件:

$ cat hello.txt
Hello, Terraform

✅ 文件已成功创建。

测试配置漂移(Drift)

我们手动修改文件内容:

$ echo foo > hello.txt

再次运行 plan

$ terraform plan
...
An execution plan has been generated and is shown below.
Resource actions are indicated with the following symbols:
  + create

Terraform will perform the following actions:

  # local_file.hello will be created
  + resource "local_file" "hello" {
      + content              = "Hello, Terraform"
      + directory_permission = "0777"
      + file_permission      = "0777"
      + filename             = "hello.txt"
      + id                   = (known after apply)
    }

Plan: 1 to add, 0 to change, 0 to destroy.

Terraform 检测到文件内容被修改,并计划重建文件。

再次执行 apply 恢复内容:

$ terraform apply -auto-approve
...
Apply complete! Resources: 1 added, 0 changed, 0 destroyed.

$ cat hello.txt
Hello, Terraform

✅ 内容恢复成功。

4. 核心概念

4.1. Provider

Provider 是 Terraform 与基础设施交互的桥梁。它就像操作系统中的设备驱动,为资源提供统一的抽象接口,屏蔽底层创建、修改和删除资源的细节

Terraform 会根据项目中使用的资源自动从 Terraform Registry 下载对应的 Provider,也可以手动安装自定义插件。

通常需要为 Provider 配置一些参数,例如认证信息:

provider "kubernetes" {
  version = "~> 1.10"
}

如未显式配置参数,Terraform 会从其他位置获取(如环境变量、kubectl 配置等)。

4.2. Resource

Resource 是 Terraform 中最基本的构建块,表示可以进行 CRUD 操作的基础设施对象

示例:

resource "aws_instance" "web" {
  ami = "some-ami-id"
  instance_type = "t2.micro"
}
  • resource:关键字
  • aws_instance:资源类型(AWS 提供商定义)
  • web:资源名称(在同一模块中必须唯一)
  • amiinstance_type:资源参数

资源创建后,可以通过表达式引用其属性

resource "aws_instance" "web" {
  ami = "some-ami-id"
  instance_type = "t2.micro"
  subnet_id = aws_subnet.frontend.id
}
resource "aws_subnet" "frontend" {
  vpc_id = aws_vpc.apps.id
  cidr_block = "10.0.1.0/24"
}
resource "aws_vpc" "apps" {
  cidr_block = "10.0.0.0/16"
}

Terraform 会自动分析资源之间的依赖关系并按顺序执行操作。

4.3. countfor_each

这两个元参数用于创建多个资源实例。

  • count:指定数量
  • for_each:基于集合(map 或 list)创建多个资源

示例(使用 count 创建多个 EC2 实例):

resource "aws_instance" "server" {
  count = var.server_count 
  ami = "ami-xxxxxxx"
  instance_type = "t2.micro"
  tags = {
    Name = "WebServer - ${count.index}"
  }
}

使用 for_each

variable "instances" {
  type = map(string)
}
resource "aws_instance" "server" {
  for_each = var.instances 
  ami = each.value
  instance_type = "t2.micro"
  tags = {
    Name = each.key
  }
}

⚠️ 注意countfor_each 的值必须在执行前确定,不能依赖其他资源的输出。

4.4. Data Source

Data Source 是“只读资源”,用于从现有基础设施中获取信息,供其他资源使用。

示例:从 AWS 获取最新 Ubuntu AMI:

data "aws_ami" "ubuntu" {
  most_recent = true
  filter {
    name   = "name"
    values = ["ubuntu/images/hvm-ssd/ubuntu-trusty-14.04-amd64-server-*"]
  }
  filter {
    name   = "virtualization-type"
    values = ["hvm"]
  }
  owners = ["099720109477"] # Canonical
}

在资源中使用:

resource "aws_instance" "web" {
  ami = data.aws_ami.ubuntu.id 
  instance_type = "t2.micro"
}

4.5. State

State 文件记录了项目中所有资源的当前状态,是 Terraform 理解“当前基础设施长什么样”的关键。

⚠️ 重要提示:State 文件可能包含敏感信息,如数据库初始密码、私钥等。

默认使用本地 local 后端存储,也可以使用远程后端,例如 S3:

terraform {
  backend "s3" {
    bucket = "some-bucket"
    key = "some-storage-key"
    region = "us-east-1"
  }
}

4.6. Module

模块是 Terraform 实现代码复用的核心机制。就像写代码时将功能模块化一样,Terraform 也可以将资源定义组织成模块。

模块就是一个包含 .tf 文件的目录。即使是单个文件,也默认是一个模块。

引用子模块:

module "networking" {
  source = "./networking"
  create_public_ip = true
}

⚠️ 注意:目前 Terraform 不支持对模块使用 countfor_each

4.7. Input Variables

模块可以通过 variable 块定义输入变量:

variable "myvar" {
  type = string
  default = "Some Value"
  description = "MyVar description"
}

变量值可以通过以下方式传入:

  • 命令行参数 -var
  • .tfvars 文件
  • 环境变量 TF_VAR_xxx
  • 默认值

使用方式:

resource "xxx_type" "some_name" {
  arg = var.myvar
}

4.8. Output Values

模块可以通过 output 暴露其资源的属性:

output "web_addr" {
  value = aws_instance.web.private_ip
  description = "Web server's private IP address"
}

其他模块可以通过 module.module_name.web_addr 使用该输出值。

4.9. Local Variables

Local 变量作用域仅限于当前模块,常用于简化表达式和减少重复代码:

locals {
  vpc_id = module.network.vpc_id
}
module "network" {
  source = "./network"
}
module "service1" {
  source = "./service1"
  vpc_id = local.vpc_id
}
module "service2" {
  source = "./service2"
  vpc_id = local.vpc_id
}

4.10. Workspaces

Terraform 支持多个工作区(Workspace),用于维护同一项目在不同环境下的状态文件(如 DEV、QA、PROD)。

创建新工作区:

$ terraform workspace new dev

查看当前工作区:

$ terraform workspace show

可以使用内置变量 terraform.workspace 控制资源是否创建:

resource "aws_instance" "web" {
  count = terraform.workspace == "prod" ? 3 : 1
  ...
}

5. 小结

Terraform 是一个功能强大的基础设施即代码工具,它帮助我们实现基础设施的版本化、自动化和可重复部署。虽然功能强大,但也需要理解其核心概念与工作机制。

如需完整代码示例,请参考:GitHub 项目地址


原始标题:Introduction to Terraform