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
:定义了一个名为hello
的local_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
:资源名称(在同一模块中必须唯一)ami
、instance_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. count
与 for_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
}
}
⚠️ 注意:count
和 for_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 不支持对模块使用 count
或 for_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 项目地址。