3. Provider 与资源管理:Provider 配置、资源与数据源、依赖关系

在 Terraform 工作流中,Provider 是连接 Terraform 与目标基础设施的桥梁,Resource 是声明式定义的具体云对象,Data Source 用于查询已有基础设施信息,而 depends_on 则显式控制资源创建顺序。本章将深入剖析这四者的配置方法与协作模式。

3.1 Provider 配置:声明与版本约束

Provider 是 Terraform 与云平台(AWS、Azure、阿里云等)或服务(Kubernetes、Helm、DNS 等)交互的插件。每个 Provider 都需要在 required_providers 块中声明,并指定来源与版本约束。

核心原则:始终锁定 Provider 主版本号,避免因 Provider 升级导致基础设施配置意外变更。

3.1.1 标准 Provider 声明模板

terraform {
  required_version = ">= 1.5.0"
  required_providers {
    aws = {
      source  = "hashicorp/aws"
      version = "~> 5.0"   # 允许 5.x 范围内的次版本升级
    }
    kubernetes = {
      source  = "hashicorp/kubernetes"
      version = "~> 2.23"
    }
  }
}

# Provider 配置块
provider "aws" {
  region = var.aws_region
  # 认证信息通过环境变量 AWS_ACCESS_KEY_ID / AWS_SECRET_ACCESS_KEY 传入
}

provider "kubernetes" {
  config_path = "~/.kube/config"
}

3.1.2 版本约束符号速查表

符号 含义 示例 允许版本范围
>= 1.0 大于等于 version = ">= 1.0" 1.0.0 及以上
~> 2.0 悲观约束(只允许最后一位增长) version = "~> 2.0" 2.0.0 ≤ x < 3.0.0
~> 2.3.0 悲观约束(锁定前两位) version = "~> 2.3.0" 2.3.0 ≤ x < 2.4.0
>= 1.0, < 2.0 范围组合 version = ">= 1.0, < 2.0" 1.0.0 ≤ x < 2.0.0

3.2 Resource(资源)与 Data Source(数据源)

Resource 是 Terraform 管理的核心对象——创建、更新、删除都由 Terraform 生命周期控制。Data Source 则用于读取外部已有资源的信息,不管理其生命周期。

3.2.1 资源定义与属性引用

# 创建 AWS EC2 实例(资源)
resource "aws_instance" "web" {
  ami           = "ami-0c55b159cbfafe1f0"
  instance_type = "t3.micro"

  tags = {
    Name = "web-server"
  }
}

# 创建安全组(资源)
resource "aws_security_group" "web_sg" {
  name        = "web-sg"
  description = "Allow HTTP/HTTPS traffic"

  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"]
  }
}

3.2.2 数据源:查询已有基础设施

# 查询已有的 VPC(数据源)
data "aws_vpc" "default" {
  default = true
}

# 查询可用区列表(数据源)
data "aws_availability_zones" "available" {
  state = "available"
}

# 在资源中引用数据源属性
resource "aws_subnet" "main" {
  vpc_id            = data.aws_vpc.default.id
  cidr_block        = "10.0.1.0/24"
  availability_zone = data.aws_availability_zones.available.names[0]
}

3.2.3 Resource 与 Data Source 对比

维度 Resource(资源) Data Source(数据源)
生命周期管理 创建、更新、删除 只读查询,不管理生命周期
声明关键字 resource data
典型用途 创建 EC2、RDS、VPC 等 查询已有 VPC、AMI、可用区等
状态文件影响 写入状态文件 不写入状态文件(仅缓存结果)
删除风险 terraform destroy 会删除 不会删除任何资源

3.3 依赖关系与 depends_on

Terraform 通过分析资源之间的属性引用来自动推断隐式依赖。当资源 A 引用了资源 B 的某个属性(如 aws_instance.web.id)时,Terraform 会自动确保 B 先创建。但在某些场景下,隐式依赖无法覆盖所有顺序要求,此时需要 depends_on 显式声明。

3.3.1 隐式依赖 vs 显式依赖

# 隐式依赖:通过属性引用自动建立
resource "aws_eip" "web_eip" {
  instance = aws_instance.web.id   # 自动依赖 aws_instance.web
}

# 显式依赖:使用 depends_on 强制声明
resource "aws_s3_bucket" "logs" {
  bucket = "app-logs-bucket"
}

resource "aws_s3_bucket_policy" "logs_policy" {
  bucket = aws_s3_bucket.logs.id
  policy = data.aws_iam_policy_document.logs_policy.json

  # 显式依赖:确保 bucket 完全就绪后再应用策略
  depends_on = [aws_s3_bucket.logs]
}

3.3.2 何时必须使用 depends_on

  • 无属性引用的间接依赖:资源 A 需要资源 B 存在,但 A 的配置中不直接引用 B 的任何属性。
  • 模块间的依赖:模块 A 的输出被模块 B 使用,但模块 B 内部资源未直接引用模块 A 的输出。
  • Provisioner 执行顺序:本地执行(local-exec)或远程执行(remote-exec)需要前置资源已就绪。
  • 第三方 API 延迟:某些云服务创建后需要几秒才能被其他服务访问,depends_on 可配合 time_sleep 资源使用。
注意:过度使用 depends_on 会降低 Terraform 的并行执行效率。优先使用隐式依赖,仅在必要时添加显式依赖。

3.4 Provider、资源与数据源协作流程

下图展示了 Terraform 在执行 plan 和 apply 时,Provider、Resource、Data Source 与依赖关系的协作流程:

Provider、资源、数据源与依赖关系协作流程 1. Provider 初始化 2. 加载 Data Source 3. 构建依赖图 4. 按依赖顺序执行资源创建 资源 A(无依赖) 资源 B(依赖 A) 资源 C(依赖 B) depends_on 显式 5. 输出属性 & 更新状态文件 输出资源属性 写入 terraform.tfstate 缓存 Data Source 结果 说明: • 隐式依赖通过属性引用自动建立,depends_on 用于无属性引用的场景。 • Data Source 在 plan 阶段优先加载,不参与依赖图排序。

3.5 多 Provider 协作与别名

在同一个配置中使用多个同类型 Provider(如多个 AWS 区域)时,需要使用 alias 区分:

# 默认 Provider:us-east-1
provider "aws" {
  region = "us-east-1"
}

# 别名 Provider:eu-west-1
provider "aws" {
  alias  = "west_eu"
  region = "eu-west-1"
}

# 使用默认 Provider 创建资源
resource "aws_vpc" "primary" {
  cidr_block = "10.0.0.0/16"
}

# 使用别名 Provider 创建资源
resource "aws_vpc" "secondary" {
  provider   = aws.west_eu
  cidr_block = "10.1.0.0/16"
}

3.6 依赖关系最佳实践

  • 优先隐式依赖:通过属性引用让 Terraform 自动推断,保持代码简洁。
  • depends_on 用于模块边界:当模块 A 的输出被模块 B 使用时,在模块 B 的 depends_on 中声明。
  • 避免循环依赖:如果资源 A 依赖 B,B 又依赖 A,Terraform 会报错。此时需要重构资源设计。
  • 使用 terraform graph 可视化依赖:生成 DOT 格式的依赖图,便于调试复杂依赖关系。
调试技巧:执行 terraform graph | dot -Tpng > graph.png 可生成依赖关系图,直观查看资源创建顺序。

3.7 本章小结

概念 关键点 常用场景
Provider 声明来源、版本约束、认证配置 连接云平台、Kubernetes、DNS 等
Resource 管理基础设施对象的生命周期 创建 EC2、VPC、RDS、S3 等
Data Source 只读查询已有资源信息 获取 AMI ID、VPC ID、可用区列表
depends_on 显式声明无属性引用的依赖 模块间依赖、Provisioner 顺序、API 延迟