Terraform 教程:基础设施即代码工具详解
1. 什么是 Terraform?
Terraform 是一个开源的基础设施即代码(Infrastructure as Code,IaC)工具,由 HashiCorp 开发。它允许用户使用声明式配置语言定义和管理基础设施,支持多种云服务提供商,如 AWS、Azure、Google Cloud、阿里云、腾讯云等。
1.1 核心概念
- 资源(Resource):基础设施的基本构建块,如虚拟机、网络、存储等。
- 模块(Module):可重用的资源配置集合。
- 状态(State):记录基础设施当前状态的文件,用于跟踪资源的变更。
- 提供者(Provider):与特定云服务提供商交互的插件。
- 变量(Variable):用于参数化配置。
- 输出(Output):用于暴露资源的属性值。
- 数据源(Data Source):用于查询现有基础设施的信息。
1.2 核心特性
- 多云支持:支持多种云服务提供商,实现多云部署。
- 声明式配置:使用 HCL(HashiCorp Configuration Language)定义基础设施。
- 状态管理:通过状态文件跟踪基础设施的变更。
- 模块系统:支持模块化和代码重用。
- 计划和执行:在执行前预览变更,确保操作的安全性。
- 资源依赖管理:自动处理资源之间的依赖关系。
- 插件系统:通过提供者插件支持新的云服务和功能。
1.3 适用场景
- 云基础设施管理:创建和管理云资源。
- 多环境部署:在开发、测试和生产环境中一致部署基础设施。
- 基础设施自动化:自动化基础设施的创建、更新和销毁。
- 多云战略:在多个云提供商之间管理资源。
- 基础设施即代码:将基础设施配置版本控制,实现可追溯性和可重复性。
2. 安装和配置
2.1 安装 Terraform
2.1.1 Windows 安装
- 访问 Terraform 官方下载页面:https://www.terraform.io/downloads
- 下载适用于 Windows 的 64 位版本
- 解压下载的 zip 文件,将 terraform.exe 复制到系统路径中的目录,如
C:\Windows\System32 - 打开命令提示符,验证安装:
terraform --version2.1.2 macOS 安装
使用 Homebrew 安装:
brew tap hashicorp/tap
brew install hashicorp/tap/terraform
# 验证安装
terraform --version2.1.3 Linux 安装
# 下载 Terraform
wget https://releases.hashicorp.com/terraform/1.5.0/terraform_1.5.0_linux_amd64.zip
# 解压
unzip terraform_1.5.0_linux_amd64.zip
# 移动到系统路径
sudo mv terraform /usr/local/bin/
# 验证安装
terraform --version2.2 配置云提供商凭证
2.2.1 AWS 配置
# 安装 AWS CLI
pip install awscli
# 配置 AWS 凭证
aws configure按照提示输入 AWS Access Key ID、AWS Secret Access Key、默认区域和输出格式。
2.2.2 Azure 配置
# 安装 Azure CLI
curl -sL https://aka.ms/InstallAzureCLIDeb | sudo bash
# 登录 Azure
az login
# 设置默认订阅
az account set --subscription "your-subscription-id"2.2.3 Google Cloud 配置
# 安装 Google Cloud SDK
curl https://sdk.cloud.google.com | bash
# 初始化 Google Cloud SDK
gcloud init
# 登录 Google Cloud
gcloud auth login
# 设置默认项目
gcloud config set project "your-project-id"3. 基本使用
3.1 初始化项目
# 创建项目目录
mkdir terraform-demo
cd terraform-demo
# 创建主配置文件
vim main.tf3.2 编写基本配置
创建一个 main.tf 文件,定义一个 AWS EC2 实例:
provider "aws" {
region = "us-west-2"
}
resource "aws_instance" "example" {
ami = "ami-0c55b159cbfafe1f0"
instance_type = "t2.micro"
tags = {
Name = "ExampleInstance"
}
}3.3 初始化和执行
# 初始化 Terraform 项目(下载提供者插件)
terraform init
# 预览变更
terraform plan
# 执行变更
terraform apply
# 查看状态
terraform show
# 销毁资源
terraform destroy3.4 使用变量和输出
创建 variables.tf 文件:
variable "region" {
description = "AWS region"
type = string
default = "us-west-2"
}
variable "instance_type" {
description = "EC2 instance type"
type = string
default = "t2.micro"
}
variable "ami" {
description = "EC2 AMI ID"
type = string
default = "ami-0c55b159cbfafe1f0"
}创建 outputs.tf 文件:
output "instance_id" {
description = "EC2 instance ID"
value = aws_instance.example.id
}
output "public_ip" {
description = "EC2 public IP address"
value = aws_instance.example.public_ip
}
output "public_dns" {
description = "EC2 public DNS name"
value = aws_instance.example.public_dns
}更新 main.tf 文件:
provider "aws" {
region = var.region
}
resource "aws_instance" "example" {
ami = var.ami
instance_type = var.instance_type
tags = {
Name = "ExampleInstance"
}
}3.5 使用模块
创建一个 modules 目录和 modules/ec2 子目录,在其中创建 main.tf、variables.tf 和 outputs.tf 文件:
modules/ec2/main.tf:
resource "aws_instance" "instance" {
ami = var.ami
instance_type = var.instance_type
tags = {
Name = var.instance_name
}
}modules/ec2/variables.tf:
variable "ami" {
description = "EC2 AMI ID"
type = string
}
variable "instance_type" {
description = "EC2 instance type"
type = string
default = "t2.micro"
}
variable "instance_name" {
description = "EC2 instance name"
type = string
}modules/ec2/outputs.tf:
output "instance_id" {
description = "EC2 instance ID"
value = aws_instance.instance.id
}
output "public_ip" {
description = "EC2 public IP address"
value = aws_instance.instance.public_ip
}在根目录的 main.tf 文件中使用模块:
provider "aws" {
region = var.region
}
module "ec2_instance" {
source = "./modules/ec2"
ami = var.ami
instance_type = var.instance_type
instance_name = "ExampleInstance"
}4. 高级功能
4.1 状态管理
4.1.1 远程状态
使用 S3 存储远程状态:
terraform {
backend "s3" {
bucket = "terraform-state-bucket"
key = "example/terraform.tfstate"
region = "us-west-2"
encrypt = true
dynamodb_table = "terraform-state-lock"
}
}4.1.2 状态锁定
使用 DynamoDB 实现状态锁定,防止并发操作:
# 创建 DynamoDB 表
aws dynamodb create-table \
--table-name terraform-state-lock \
--attribute-definitions AttributeName=LockID,AttributeType=S \
--key-schema AttributeName=LockID,KeyType=HASH \
--provisioned-throughput ReadCapacityUnits=1,WriteCapacityUnits=14.2 工作空间
工作空间用于在同一配置下管理多个环境:
# 创建工作空间
terraform workspace new development
# 切换工作空间
terraform workspace select production
# 查看工作空间
terraform workspace list
# 在当前工作空间中执行操作
terraform apply4.3 数据源
使用数据源查询现有资源:
data "aws_ami" "ubuntu" {
most_recent = true
filter {
name = "name"
values = ["ubuntu/images/hvm-ssd/ubuntu-focal-20.04-amd64-server-*"]
}
filter {
name = "virtualization-type"
values = ["hvm"]
}
owners = ["099720109477"] # Canonical
}
resource "aws_instance" "example" {
ami = data.aws_ami.ubuntu.id
instance_type = "t2.micro"
tags = {
Name = "ExampleInstance"
}
}4.4 资源依赖
Terraform 自动处理资源依赖,但也可以显式指定:
resource "aws_vpc" "main" {
cidr_block = "10.0.0.0/16"
}
resource "aws_subnet" "main" {
vpc_id = aws_vpc.main.id
cidr_block = "10.0.1.0/24"
availability_zone = "us-west-2a"
}
resource "aws_instance" "example" {
ami = "ami-0c55b159cbfafe1f0"
instance_type = "t2.micro"
subnet_id = aws_subnet.main.id
# 显式依赖
depends_on = [aws_subnet.main]
tags = {
Name = "ExampleInstance"
}
}4.5 条件表达式和循环
4.5.1 条件表达式
resource "aws_instance" "example" {
ami = var.ami
instance_type = var.environment == "production" ? "t2.large" : "t2.micro"
tags = {
Name = "ExampleInstance"
Environment = var.environment
}
}4.5.2 循环
variable "instance_count" {
type = number
default = 3
}
resource "aws_instance" "example" {
count = var.instance_count
ami = var.ami
instance_type = "t2.micro"
tags = {
Name = "ExampleInstance-${count.index}"
}
}5. 实用案例
5.1 创建 AWS EC2 实例
场景:使用 Terraform 创建一个 AWS EC2 实例。
步骤:
- 创建项目目录:
mkdir terraform-aws-ec2 && cd terraform-aws-ec2- 创建配置文件:
main.tf:
terraform {
required_providers {
aws = {
source = "hashicorp/aws"
version = "~> 4.0"
}
}
}
provider "aws" {
region = "us-west-2"
}
resource "aws_instance" "example" {
ami = "ami-0c55b159cbfafe1f0"
instance_type = "t2.micro"
tags = {
Name = "ExampleInstance"
}
}- 初始化和执行:
# 初始化
terraform init
# 预览
terraform plan
# 执行
terraform apply
# 查看输出
terraform output
# 销毁
terraform destroy5.2 创建完整的网络基础设施
场景:使用 Terraform 创建一个包含 VPC、子网、安全组和 EC2 实例的完整网络基础设施。
步骤:
- 创建项目目录:
mkdir terraform-aws-network && cd terraform-aws-network- 创建配置文件:
variables.tf:
variable "region" {
description = "AWS region"
type = string
default = "us-west-2"
}
variable "vpc_cidr" {
description = "VPC CIDR block"
type = string
default = "10.0.0.0/16"
}
variable "subnet_cidr" {
description = "Subnet CIDR block"
type = string
default = "10.0.1.0/24"
}
variable "instance_type" {
description = "EC2 instance type"
type = string
default = "t2.micro"
}
variable "ami" {
description = "EC2 AMI ID"
type = string
default = "ami-0c55b159cbfafe1f0"
}main.tf:
terraform {
required_providers {
aws = {
source = "hashicorp/aws"
version = "~> 4.0"
}
}
}
provider "aws" {
region = var.region
}
# 创建 VPC
resource "aws_vpc" "main" {
cidr_block = var.vpc_cidr
tags = {
Name = "MainVPC"
}
}
# 创建子网
resource "aws_subnet" "main" {
vpc_id = aws_vpc.main.id
cidr_block = var.subnet_cidr
availability_zone = "${var.region}a"
tags = {
Name = "MainSubnet"
}
}
# 创建 Internet Gateway
resource "aws_internet_gateway" "main" {
vpc_id = aws_vpc.main.id
tags = {
Name = "MainIGW"
}
}
# 创建路由表
resource "aws_route_table" "main" {
vpc_id = aws_vpc.main.id
route {
cidr_block = "0.0.0.0/0"
gateway_id = aws_internet_gateway.main.id
}
tags = {
Name = "MainRouteTable"
}
}
# 关联路由表
resource "aws_route_table_association" "main" {
subnet_id = aws_subnet.main.id
route_table_id = aws_route_table.main.id
}
# 创建安全组
resource "aws_security_group" "main" {
name = "MainSecurityGroup"
description = "Allow HTTP and SSH traffic"
vpc_id = aws_vpc.main.id
ingress {
from_port = 22
to_port = 22
protocol = "tcp"
cidr_blocks = ["0.0.0.0/0"]
}
ingress {
from_port = 80
to_port = 80
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 = "MainSecurityGroup"
}
}
# 创建 EC2 实例
resource "aws_instance" "example" {
ami = var.ami
instance_type = var.instance_type
subnet_id = aws_subnet.main.id
vpc_security_group_ids = [aws_security_group.main.id]
tags = {
Name = "ExampleInstance"
}
}outputs.tf:
output "instance_id" {
description = "EC2 instance ID"
value = aws_instance.example.id
}
output "public_ip" {
description = "EC2 public IP address"
value = aws_instance.example.public_ip
}
output "public_dns" {
description = "EC2 public DNS name"
value = aws_instance.example.public_dns
}
output "vpc_id" {
description = "VPC ID"
value = aws_vpc.main.id
}
output "subnet_id" {
description = "Subnet ID"
value = aws_subnet.main.id
}- 初始化和执行:
# 初始化
terraform init
# 预览
terraform plan
# 执行
terraform apply
# 查看输出
terraform output
# 销毁
terraform destroy5.2 创建 Azure 虚拟机
场景:使用 Terraform 创建一个 Azure 虚拟机。
步骤:
- 创建项目目录:
mkdir terraform-azure-vm && cd terraform-azure-vm- 创建配置文件:
main.tf:
terraform {
required_providers {
azurerm = {
source = "hashicorp/azurerm"
version = "~> 3.0"
}
}
}
provider "azurerm" {
features {}
}
resource "azurerm_resource_group" "example" {
name = "example-resources"
location = "East US"
}
resource "azurerm_virtual_network" "example" {
name = "example-network"
address_space = ["10.0.0.0/16"]
location = azurerm_resource_group.example.location
resource_group_name = azurerm_resource_group.example.name
}
resource "azurerm_subnet" "example" {
name = "example-subnet"
resource_group_name = azurerm_resource_group.example.name
virtual_network_name = azurerm_virtual_network.example.name
address_prefixes = ["10.0.1.0/24"]
}
resource "azurerm_network_interface" "example" {
name = "example-nic"
location = azurerm_resource_group.example.location
resource_group_name = azurerm_resource_group.example.name
ip_configuration {
name = "internal"
subnet_id = azurerm_subnet.example.id
private_ip_address_allocation = "Dynamic"
public_ip_address_id = azurerm_public_ip.example.id
}
}
resource "azurerm_public_ip" "example" {
name = "example-pip"
location = azurerm_resource_group.example.location
resource_group_name = azurerm_resource_group.example.name
allocation_method = "Static"
}
resource "azurerm_network_security_group" "example" {
name = "example-nsg"
location = azurerm_resource_group.example.location
resource_group_name = azurerm_resource_group.example.name
security_rule {
name = "SSH"
priority = 1001
direction = "Inbound"
access = "Allow"
protocol = "Tcp"
source_port_range = "*"
destination_port_range = "22"
source_address_prefix = "*"
destination_address_prefix = "*"
}
security_rule {
name = "HTTP"
priority = 1002
direction = "Inbound"
access = "Allow"
protocol = "Tcp"
source_port_range = "*"
destination_port_range = "80"
source_address_prefix = "*"
destination_address_prefix = "*"
}
}
resource "azurerm_subnet_network_security_group_association" "example" {
subnet_id = azurerm_subnet.example.id
network_security_group_id = azurerm_network_security_group.example.id
}
resource "azurerm_linux_virtual_machine" "example" {
name = "example-vm"
resource_group_name = azurerm_resource_group.example.name
location = azurerm_resource_group.example.location
size = "Standard_B2s"
admin_username = "adminuser"
network_interface_ids = [azurerm_network_interface.example.id]
admin_ssh_key {
username = "adminuser"
public_key = file("~/.ssh/id_rsa.pub")
}
os_disk {
caching = "ReadWrite"
storage_account_type = "Standard_LRS"
}
source_image_reference {
publisher = "Canonical"
offer = "UbuntuServer"
sku = "18.04-LTS"
version = "latest"
}
}- 初始化和执行:
# 初始化
terraform init
# 预览
terraform plan
# 执行
terraform apply
# 查看输出
terraform output
# 销毁
terraform destroy6. 代码示例
6.1 AWS VPC 完整配置
main.tf:
terraform {
required_providers {
aws = {
source = "hashicorp/aws"
version = "~> 4.0"
}
}
}
provider "aws" {
region = "us-west-2"
}
resource "aws_vpc" "main" {
cidr_block = "10.0.0.0/16"
tags = {
Name = "MainVPC"
}
}
resource "aws_subnet" "public" {
vpc_id = aws_vpc.main.id
cidr_block = "10.0.1.0/24"
availability_zone = "${var.region}a"
map_public_ip_on_launch = true
tags = {
Name = "PublicSubnet"
}
}
resource "aws_subnet" "private" {
vpc_id = aws_vpc.main.id
cidr_block = "10.0.2.0/24"
availability_zone = "${var.region}a"
tags = {
Name = "PrivateSubnet"
}
}
resource "aws_internet_gateway" "main" {
vpc_id = aws_vpc.main.id
tags = {
Name = "MainIGW"
}
}
resource "aws_route_table" "public" {
vpc_id = aws_vpc.main.id
route {
cidr_block = "0.0.0.0/0"
gateway_id = aws_internet_gateway.main.id
}
tags = {
Name = "PublicRouteTable"
}
}
resource "aws_route_table_association" "public" {
subnet_id = aws_subnet.public.id
route_table_id = aws_route_table.public.id
}
resource "aws_security_group" "web" {
name = "WebSecurityGroup"
description = "Allow HTTP and SSH traffic"
vpc_id = aws_vpc.main.id
ingress {
from_port = 22
to_port = 22
protocol = "tcp"
cidr_blocks = ["0.0.0.0/0"]
}
ingress {
from_port = 80
to_port = 80
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 = "WebSecurityGroup"
}
}
resource "aws_instance" "web" {
ami = "ami-0c55b159cbfafe1f0"
instance_type = "t2.micro"
subnet_id = aws_subnet.public.id
vpc_security_group_ids = [aws_security_group.web.id]
tags = {
Name = "WebServer"
}
}
output "web_instance_public_ip" {
value = aws_instance.web.public_ip
}
output "vpc_id" {
value = aws_vpc.main.id
}执行命令:
# 初始化
terraform init
# 预览
terraform plan
# 执行
terraform apply
# 查看输出
terraform output
# 销毁
terraform destroy6.2 Google Cloud 虚拟机配置
main.tf:
terraform {
required_providers {
google = {
source = "hashicorp/google"
version = "~> 4.0"
}
}
}
provider "google" {
project = "your-project-id"
region = "us-central1"
zone = "us-central1-a"
}
resource "google_compute_network" "vpc_network" {
name = "terraform-network"
}
resource "google_compute_subnetwork" "subnetwork" {
name = "terraform-subnetwork"
network = google_compute_network.vpc_network.name
region = "us-central1"
ip_cidr_range = "10.2.0.0/16"
}
resource "google_compute_firewall" "firewall" {
name = "terraform-firewall"
network = google_compute_network.vpc_network.name
allow {
protocol = "tcp"
ports = ["22", "80"]
}
source_ranges = ["0.0.0.0/0"]
}
resource "google_compute_instance" "vm_instance" {
name = "terraform-instance"
machine_type = "e2-micro"
zone = "us-central1-a"
boot_disk {
initialize_params {
image = "debian-cloud/debian-10"
}
}
network_interface {
network = google_compute_network.vpc_network.name
subnetwork = google_compute_subnetwork.subnetwork.name
access_config {
// Ephemeral public IP
}
}
metadata = {
ssh-keys = "admin:${file("~/.ssh/id_rsa.pub"}"
}
}
output "instance_ip_addr" {
value = google_compute_instance.vm_instance.network_interface[0].access_config[0].nat_ip
description = "The public IP address of the VM instance"
}执行命令:
# 初始化
terraform init
# 预览
terraform plan
# 执行
terraform apply
# 查看输出
terraform output
# 销毁
terraform destroy7. 常见问题和解决方案
7.1 提供者插件安装失败
问题:初始化时提供者插件安装失败。
解决方案:
- 检查网络连接
- 检查 Terraform 版本和提供者版本兼容性
- 使用代理服务器:
export HTTPS_PROXY=http://proxy.example.com:8080 - 手动下载提供者插件并放置到相应目录
7.2 状态文件锁定
问题:执行 terraform apply 时提示状态文件被锁定。
解决方案:
- 等待其他操作完成
- 如果是因为之前的操作异常终止,使用
terraform force-unlock LOCK_ID解锁 - 使用远程状态和状态锁定机制
7.3 资源创建失败
问题:资源创建失败,提示权限不足或资源不存在。
解决方案:
- 检查云提供商凭证是否正确
- 检查用户权限是否足够
- 检查资源配置是否正确
- 检查云提供商的服务限制
7.4 循环依赖
问题:Terraform 提示循环依赖错误。
解决方案:
- 重新设计资源的依赖关系
- 使用数据源查询现有资源
- 避免不必要的 depends_on 声明
7.5 变量验证失败
问题:变量验证失败,提示类型不匹配或值无效。
解决方案:
- 检查变量定义和使用是否一致
- 检查变量值是否符合验证规则
- 使用正确的类型转换
8. 总结
Terraform 是一个强大的基础设施即代码工具,它通过声明式配置语言和状态管理,实现了基础设施的自动化管理和版本控制。Terraform 支持多种云服务提供商,为多云战略和混合云部署提供了统一的管理界面。
8.1 优点
- 多云支持:支持多种云服务提供商,实现多云部署。
- 声明式配置:使用 HCL 定义基础设施,配置清晰易读。
- 状态管理:通过状态文件跟踪基础设施的变更,确保操作的一致性。
- 模块系统:支持模块化和代码重用,提高配置的可维护性。
- 计划和执行:在执行前预览变更,确保操作的安全性。
- 资源依赖管理:自动处理资源之间的依赖关系。
- 版本控制:基础设施配置可以纳入版本控制系统,实现可追溯性。
8.2 局限性
- 学习曲线:对于初学者来说,Terraform 有一定的学习曲线。
- 状态管理复杂性:在团队协作中,状态管理可能变得复杂。
- 性能:对于大规模基础设施,Terraform 的执行速度可能较慢。
- 错误处理:错误消息有时不够清晰,需要一定的调试经验。
- 云提供商特定功能:某些云提供商的特定功能可能需要使用 provider-specific 的配置。
8.3 适用场景
- 云基础设施管理:创建和管理云资源。
- 多环境部署:在开发、测试和生产环境中一致部署基础设施。
- 多云战略:在多个云提供商之间管理资源。
- 基础设施自动化:自动化基础设施的创建、更新和销毁。
- 灾难恢复:快速重建基础设施。
- 成本优化:通过代码管理资源,避免资源浪费。
通过本教程,你应该已经掌握了 Terraform 的基本概念、安装配置、基本使用和常见场景的应用。在实际开发和运维中,Terraform 可以大大简化基础设施的管理工作,提高工作效率和基础设施的可靠性。