Provider 和资源管理
Provider 概述
Provider 是 Terraform 与云平台、SaaS 服务交互的插件。每个 Provider 负责理解 API 交互并暴露资源。
Provider 架构
┌─────────────┐
│ Terraform │
│ Core │
└──────┬──────┘
│
├──────────┬──────────┬──────────┐
│ │ │ │
▼ ▼ ▼ ▼
┌────────┐ ┌────────┐ ┌────────┐ ┌────────┐
│ AWS │ │ Azure │ │ GCP │ │Alibaba │
│Provider│ │Provider│ │Provider│ │Provider│
└────┬───┘ └────┬───┘ └────┬───┘ └────┬───┘
│ │ │ │
▼ ▼ ▼ ▼
┌────────┐ ┌────────┐ ┌────────┐ ┌────────┐
│AWS API │ │Azure │ │GCP API │ │Aliyun │
│ │ │ARM API │ │ │ │API │
└────────┘ └────────┘ └────────┘ └────────┘
配置 Provider
基本配置
terraform {
required_providers {
aws = {
source = "hashicorp/aws"
version = "~> 5.0"
}
}
}
provider "aws" {
region = "us-east-1"
}
Provider 版本约束
terraform {
required_providers {
aws = {
source = "hashicorp/aws"
version = "~> 5.0" # >= 5.0.0, < 6.0.0
# version = ">= 5.0" # >= 5.0.0
# version = "= 5.31.0" # 精确版本
# version = "!= 5.0.0" # 排除特定版本
}
}
}
多个 Provider 实例
# 默认 Provider
provider "aws" {
region = "us-east-1"
}
# 别名 Provider
provider "aws" {
alias = "west"
region = "us-west-2"
}
# 使用特定 Provider
resource "aws_instance" "east" {
provider = aws
ami = "ami-xxx"
}
resource "aws_instance" "west" {
provider = aws.west
ami = "ami-yyy"
}
常见 Provider 配置
AWS Provider
provider "aws" {
region = "us-east-1"
access_key = var.aws_access_key
secret_key = var.aws_secret_key
# 假设角色
assume_role {
role_arn = "arn:aws:iam::123456789012:role/TerraformRole"
}
# 默认标签
default_tags {
tags = {
Environment = "Production"
ManagedBy = "Terraform"
}
}
}
Azure Provider
provider "azurerm" {
features {
resource_group {
prevent_deletion_if_contains_resources = true
}
virtual_machine {
delete_os_disk_on_deletion = true
}
}
subscription_id = var.subscription_id
tenant_id = var.tenant_id
}
GCP Provider
provider "google" {
project = "my-project-id"
region = "us-central1"
zone = "us-central1-a"
credentials = file("service-account.json")
}
阿里云 Provider
provider "alicloud" {
access_key = var.access_key
secret_key = var.secret_key
region = "cn-beijing"
}
Kubernetes Provider
provider "kubernetes" {
config_path = "~/.kube/config"
config_context = "my-context"
}
# 或使用集群配置
provider "kubernetes" {
host = "https://cluster-api.example.com"
client_certificate = file("~/.kube/client-cert.pem")
client_key = file("~/.kube/client-key.pem")
cluster_ca_certificate = file("~/.kube/cluster-ca-cert.pem")
}
资源(Resource)
资源语法
resource "<RESOURCE_TYPE>" "<RESOURCE_NAME>" {
<ARGUMENT_NAME> = <ARGUMENT_VALUE>
# ...
}
# 示例
resource "aws_instance" "web" {
ami = "ami-0c55b159cbfafe1f0"
instance_type = "t2.micro"
tags = {
Name = "Web Server"
}
}
资源元参数
1. depends_on - 显式依赖:
resource "aws_instance" "web" {
ami = "ami-xxx"
instance_type = "t2.micro"
# 显式依赖(Terraform 通常自动处理)
depends_on = [aws_security_group.web_sg]
}
resource "aws_security_group" "web_sg" {
name = "web-sg"
}
2. count - 创建多个资源:
resource "aws_instance" "web" {
count = 3
ami = "ami-xxx"
instance_type = "t2.micro"
tags = {
Name = "web-${count.index}"
}
}
# 引用
output "instance_ids" {
value = aws_instance.web[*].id
}
3. for_each - 使用集合创建资源:
variable "instances" {
default = {
web = "t2.micro"
db = "t2.small"
cache = "t2.micro"
}
}
resource "aws_instance" "servers" {
for_each = var.instances
ami = "ami-xxx"
instance_type = each.value
tags = {
Name = each.key
}
}
# 引用
output "server_ips" {
value = {
for name, instance in aws_instance.servers : name => instance.public_ip
}
}
4. provider - 指定 Provider:
resource "aws_instance" "west_server" {
provider = aws.west
ami = "ami-xxx"
instance_type = "t2.micro"
}
5. lifecycle - 生命周期规则:
resource "aws_instance" "web" {
ami = "ami-xxx"
instance_type = "t2.micro"
lifecycle {
# 先创建新资源再删除旧资源
create_before_destroy = true
# 防止资源被销毁
prevent_destroy = false
# 忽略特定属性的变更
ignore_changes = [
tags,
user_data,
]
# 替换触发条件
replace_triggered_by = [
aws_security_group.web_sg
]
}
}
6. provisioner - 配置器:
resource "aws_instance" "web" {
ami = "ami-xxx"
instance_type = "t2.micro"
# 本地执行
provisioner "local-exec" {
command = "echo ${self.private_ip} >> private_ips.txt"
}
# 远程执行
provisioner "remote-exec" {
inline = [
"sudo apt-get update",
"sudo apt-get install -y nginx",
]
connection {
type = "ssh"
user = "ubuntu"
private_key = file("~/.ssh/id_rsa")
host = self.public_ip
}
}
# 文件上传
provisioner "file" {
source = "app.conf"
destination = "/etc/app/app.conf"
connection {
type = "ssh"
user = "ubuntu"
host = self.public_ip
}
}
}
资源示例
AWS EC2 实例
resource "aws_instance" "web" {
ami = "ami-0c55b159cbfafe1f0"
instance_type = "t2.micro"
key_name = "my-key-pair"
vpc_security_group_ids = [aws_security_group.web_sg.id]
subnet_id = aws_subnet.public.id
user_data = <<-EOF
#!/bin/bash
apt-get update
apt-get install -y nginx
systemctl start nginx
EOF
tags = {
Name = "Web Server"
Environment = "Production"
}
root_block_device {
volume_size = 20
volume_type = "gp3"
encrypted = true
}
}
AWS S3 Bucket
resource "aws_s3_bucket" "data" {
bucket = "my-unique-bucket-name"
tags = {
Name = "Data Bucket"
Environment = "Production"
}
}
resource "aws_s3_bucket_versioning" "data" {
bucket = aws_s3_bucket.data.id
versioning_configuration {
status = "Enabled"
}
}
resource "aws_s3_bucket_encryption" "data" {
bucket = aws_s3_bucket.data.id
rule {
apply_server_side_encryption_by_default {
sse_algorithm = "AES256"
}
}
}
AWS VPC 网络
resource "aws_vpc" "main" {
cidr_block = "10.0.0.0/16"
enable_dns_hostnames = true
enable_dns_support = true
tags = {
Name = "Main VPC"
}
}
resource "aws_subnet" "public" {
vpc_id = aws_vpc.main.id
cidr_block = "10.0.1.0/24"
availability_zone = "us-east-1a"
map_public_ip_on_launch = true
tags = {
Name = "Public Subnet"
}
}
resource "aws_internet_gateway" "main" {
vpc_id = aws_vpc.main.id
tags = {
Name = "Main IGW"
}
}
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 = "Public Route Table"
}
}
resource "aws_route_table_association" "public" {
subnet_id = aws_subnet.public.id
route_table_id = aws_route_table.public.id
}
AWS Security Group
resource "aws_security_group" "web" {
name = "web-sg"
description = "Security group for web servers"
vpc_id = aws_vpc.main.id
# HTTP
ingress {
from_port = 80
to_port = 80
protocol = "tcp"
cidr_blocks = ["0.0.0.0/0"]
}
# HTTPS
ingress {
from_port = 443
to_port = 443
protocol = "tcp"
cidr_blocks = ["0.0.0.0/0"]
}
# SSH (限制 IP)
ingress {
from_port = 22
to_port = 22
protocol = "tcp"
cidr_blocks = ["203.0.113.0/24"]
}
# 出站流量
egress {
from_port = 0
to_port = 0
protocol = "-1"
cidr_blocks = ["0.0.0.0/0"]
}
tags = {
Name = "Web Security Group"
}
}
数据源(Data Source)
数据源允许你查询现有资源的信息。
语法
data "<DATA_SOURCE_TYPE>" "<NAME>" {
<ARGUMENT_NAME> = <ARGUMENT_VALUE>
}
AWS 示例
查询 AMI:
data "aws_ami" "ubuntu" {
most_recent = true
owners = ["099720109477"] # Canonical
filter {
name = "name"
values = ["ubuntu/images/hvm-ssd/ubuntu-focal-20.04-amd64-server-*"]
}
filter {
name = "virtualization-type"
values = ["hvm"]
}
}
resource "aws_instance" "web" {
ami = data.aws_ami.ubuntu.id
instance_type = "t2.micro"
}
查询可用区:
data "aws_availability_zones" "available" {
state = "available"
}
resource "aws_subnet" "example" {
count = 2
vpc_id = aws_vpc.main.id
cidr_block = cidrsubnet(aws_vpc.main.cidr_block, 8, count.index)
availability_zone = data.aws_availability_zones.available.names[count.index]
}
查询现有 VPC:
data "aws_vpc" "default" {
default = true
}
data "aws_subnets" "default" {
filter {
name = "vpc-id"
values = [data.aws_vpc.default.id]
}
}
资源导入
导入现有资源到 Terraform 管理。
导入步骤
1. 编写资源配置:
resource "aws_instance" "imported" {
# 配置参数
ami = "ami-xxx"
instance_type = "t2.micro"
}
2. 导入资源:
terraform import aws_instance.imported i-1234567890abcdef0
3. 查看状态并更新配置:
terraform show aws_instance.imported
# 根据输出更新配置文件
导入块(Terraform 1.5+)
import {
to = aws_instance.example
id = "i-1234567890abcdef0"
}
resource "aws_instance" "example" {
ami = "ami-xxx"
instance_type = "t2.micro"
}
然后运行:
terraform plan -generate-config-out=generated.tf
最佳实践
1. Provider 版本锁定
terraform {
required_version = ">= 1.0"
required_providers {
aws = {
source = "hashicorp/aws"
version = "~> 5.0" # 锁定主版本
}
}
}
2. 使用数据源而非硬编码
# ❌ 硬编码
resource "aws_instance" "web" {
ami = "ami-0c55b159cbfafe1f0" # 可能过时
}
# ✅ 使用数据源
data "aws_ami" "latest_ubuntu" {
most_recent = true
owners = ["099720109477"]
filter {
name = "name"
values = ["ubuntu/images/hvm-ssd/ubuntu-focal-20.04-amd64-server-*"]
}
}
resource "aws_instance" "web" {
ami = data.aws_ami.latest_ubuntu.id
}
3. 合理使用生命周期规则
resource "aws_instance" "web" {
ami = "ami-xxx"
instance_type = "t2.micro"
lifecycle {
create_before_destroy = true # 避免停机
ignore_changes = [tags] # 允许手动修改标签
}
}
4. 使用 for_each 而非 count
# ✅ for_each - 更稳定
resource "aws_instance" "servers" {
for_each = toset(["web", "api", "worker"])
ami = "ami-xxx"
instance_type = "t2.micro"
tags = {
Name = each.key
}
}
5. 避免过度使用 Provisioner
# ❌ 避免
resource "aws_instance" "web" {
provisioner "remote-exec" {
# 复杂的配置管理
}
}
# ✅ 推荐
# 使用 user_data、cloud-init 或 Ansible
resource "aws_instance" "web" {
user_data = file("user-data.sh")
}
总结
- Provider:连接云平台的插件
- Resource:要创建和管理的基础设施组件
- Data Source:查询现有资源信息
- 元参数:count、for_each、depends_on、lifecycle
- 最佳实践:版本锁定、使用数据源、合理生命周期
掌握 Provider 和资源管理是使用 Terraform 的核心技能!
下一章:变量和输出