资源抽象与统一管理:Terraform 核心概念、Provider 配置、资源编排基础

好,我们进入第二章。这一章,我打算聊聊多云环境里最基础、也最绕不开的一个话题——资源抽象。

你想想看,AWS 有 AWS 的写法,Azure 有 Azure 的语法,GCP 又自成一套。如果每个云平台都单独维护一套脚本,那运维团队迟早要崩溃。我个人习惯用 Terraform 来做这件事。它就像一个翻译官,把不同云厂商的 API 差异,统一成一套 HCL 语言。

2.1 Terraform 的核心概念

先说说 Terraform 的几个核心概念。搞懂了这些,后面的编排才能顺起来。

  • State(状态):Terraform 会维护一个状态文件,记录你当前管理的所有资源。说白了,它就是 Terraform 的「小本本」,记着哪些资源已经创建了、属性是什么。我在项目中遇到过有人不小心删了 state 文件,结果 Terraform 以为资源不存在,又重新创建了一遍,导致一堆重复资源。嗯,这是个坑。
  • Resource(资源):这是你要管理的具体对象,比如一台云服务器、一个数据库实例、一个 DNS 记录。每个资源都有一个类型和名称。
  • Module(模块):把一组相关的资源打包起来,方便复用。比如你写了一个「Web 服务器模块」,里面包含了 ECS、安全组、负载均衡器。下次再建同样的环境,直接调用模块就行。
  • Provider(提供者):这是 Terraform 和云平台之间的桥梁。每个云厂商都有自己的 Provider。

核心要点:Terraform 的核心思想是「声明式配置」。你告诉它「我要什么」,而不是「怎么去做」。它自己会算出执行计划,然后按顺序创建或更新资源。

2.2 Provider 配置

Provider 配置是 Terraform 的第一步。没有它,Terraform 连云平台的 API 都连不上。

一个典型的 Provider 配置长这样:

terraform {
  required_providers {
    aws = {
      source  = "hashicorp/aws"
      version = "~> 4.0"
    }
    azurerm = {
      source  = "hashicorp/azurerm"
      version = "~> 3.0"
    }
  }
}

provider "aws" {
  region = "us-east-1"
}

provider "azurerm" {
  features {}
}

这里有几个点要注意:

  • source:指定 Provider 的来源。官方的一般是 hashicorp/xxx
  • version:锁定版本。我建议不要用 latest,否则哪天 Provider 升级了,你的配置可能就炸了。
  • region / features:每个 Provider 有自己的配置参数。AWS 要指定区域,Azure 要开启 features 块。

注意:如果你同时管理多个云平台,记得给每个 Provider 起一个 alias 别名。否则 Terraform 会搞混,不知道用哪个 Provider 去创建资源。

我曾经在一个项目里同时管理 AWS 和阿里云,结果忘了加 alias,Terraform 一直报错说找不到 Provider。排查了半天才发现是这个问题。嗯,细节决定成败。

2.3 资源编排基础

资源编排,说白了就是写 HCL 代码来定义你的基础设施。我习惯把资源编排分成三步:定义资源、配置依赖、执行计划。

2.3.1 定义资源

每个资源块的基本结构是:

resource "资源类型" "资源名称" {
  参数1 = 值1
  参数2 = 值2
}

举个例子,创建一个 AWS EC2 实例:

resource "aws_instance" "web_server" {
  ami           = "ami-0c55b159cbfafe1f0"
  instance_type = "t2.micro"

  tags = {
    Name = "WebServer"
  }
}

这里 aws_instance 是资源类型,web_server 是你在 Terraform 里给这个资源起的名字。注意,这个名字只在 Terraform 内部使用,不会影响云平台上的实际名称。

2.3.2 配置依赖

资源之间经常有依赖关系。比如,你得先创建 VPC,才能在里面创建子网。Terraform 会自动分析依赖关系,但有时候它分析不出来,你就得手动声明。

手动声明依赖用 depends_on

resource "aws_subnet" "main" {
  vpc_id     = aws_vpc.main.id
  cidr_block = "10.0.1.0/24"

  depends_on = [aws_vpc.main]
}

小技巧:尽量让 Terraform 自动推断依赖关系。只有遇到循环依赖或者隐式依赖时,才用 depends_on。滥用它会让你的代码变得又臭又长。

2.3.3 执行计划

写完了配置,别急着 apply。先跑一遍 terraform plan,看看 Terraform 打算做什么。它会输出一个执行计划,告诉你哪些资源要创建、哪些要修改、哪些要删除。

我每次都会仔细看 plan 的输出。特别是 destroy 操作,一旦误删了生产环境的数据库,那可就麻烦了。我曾经见过有人没看 plan 直接 apply,结果把整个 VPC 给删了……嗯,那场面,相当刺激。

2.4 实战:编排一个简单的 Web 应用

光说不练假把式。我们来写一个完整的例子,编排一个简单的 Web 应用:一台 EC2 实例 + 一个安全组。

# 配置 AWS Provider
provider "aws" {
  region = "ap-southeast-1"
}

# 创建安全组
resource "aws_security_group" "web_sg" {
  name        = "web-sg"
  description = "Allow HTTP and SSH"

  ingress {
    from_port   = 80
    to_port     = 80
    protocol    = "tcp"
    cidr_blocks = ["0.0.0.0/0"]
  }

  ingress {
    from_port   = 22
    to_port     = 22
    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"]
  }
}

# 创建 EC2 实例
resource "aws_instance" "web" {
  ami                    = "ami-0c55b159cbfafe1f0"
  instance_type          = "t2.micro"
  vpc_security_group_ids = [aws_security_group.web_sg.id]

  tags = {
    Name = "WebServer"
  }
}

这个例子很简单,但包含了资源编排的核心要素:Provider 配置、资源定义、隐式依赖(EC2 引用了安全组的 ID)。

跑一下 terraform apply,Terraform 会先创建安全组,再创建 EC2 实例。顺序不会错,因为它已经分析出了依赖关系。

2.5 避坑指南

最后,分享几个我踩过的坑:

  • State 文件要妥善保管:最好用远程后端(比如 S3、Terraform Cloud)存储 state 文件。别放在本地,否则换台电脑就找不到了。
  • Provider 版本要锁定:用 version 参数固定版本,避免意外升级导致配置不兼容。
  • 不要手动修改云平台上的资源:如果你在 AWS 控制台手动修改了某个资源,Terraform 下次 apply 时会尝试把它改回来。这就是所谓的「配置漂移」。我曾经因为这个,半夜被报警电话吵醒……
  • 先 plan,再 apply:这个习惯一定要养成。plan 是你的安全网。

总结一下:Terraform 的核心就是「声明式配置 + 状态管理」。Provider 是连接云平台的桥梁,资源编排是定义基础设施的蓝图。掌握了这些,你就能在多云环境中游刃有余地管理资源了。

下一章,我们会深入 Terraform 的状态管理和远程后端。到时候聊聊怎么多人协作、怎么避免状态冲突。嗯,那才是真正的实战。