1. 课程导入与Terraform核心概念
在云上部署数据库与中间件,最容易踩的坑不是“不会点控制台”,而是环境一旦变多,配置就开始漂移:测试环境的 RDS 参数组和线上不一致,Redis 白名单漏加了一台应用机,MQ 的实例规格被临时改大后没人记录。手工操作能解决一次问题,却很难保证第一百次仍然一致。本课程要解决的核心问题,就是如何用 Terraform 把云数据库与中间件资源变成可版本化、可复用、可审计的代码。
先明确课程目标。学完本课程,你应当能够:独立编写 Terraform 配置来创建和管理云数据库(如 MySQL、PostgreSQL、Redis)与中间件(如消息队列、注册中心)资源;理解 Provider、Resource、State、变量与输出之间的关系;能够用模块封装一套“数据库+中间件”的标准组合;能够通过 plan 与 apply 的差异判断变更是否符合预期;并具备排查常见错误(如状态锁冲突、资源已存在、依赖顺序错误)的能力。这些目标贯穿后续 29 章,本节先把地基打牢。
Terraform 在云数据库与中间件编排中的定位
云厂商的控制台、CLI、SDK 都能创建数据库,为什么还要引入 Terraform?关键在于“声明式”和“状态管理”。控制台是命令式的:你点一下,它执行一次,执行完就结束了,没人记得当时点了什么。Terraform 是声明式的:你描述“我要一个 8 核 32G、MySQL 8.0、高可用版的实例”,Terraform 负责计算从当前状态到目标状态需要做哪些动作。它把“期望状态”写进代码,把“实际状态”记进 State 文件,两者对比后生成执行计划。
对于数据库与中间件这类有状态资源,这一点尤其重要。数据库实例不是无状态的 Web 服务器,删掉重建意味着数据丢失。Terraform 的 plan 机制让你在真正执行前看到“这次是原地修改参数组,还是要销毁重建实例”,从而避免误操作。同时,代码可以进 Git,每一次规格调整、白名单变更都有提交记录,满足审计与回滚需求。
IaC 基本工作流
基础设施即代码(Infrastructure as Code,IaC)的工作流可以概括为四个动作:编写、初始化、计划、应用。下面用一个最小示例说明。假设我们要在云上创建一个 MySQL 实例,配置目录结构如下:
terraform-demo/
├── main.tf
├── variables.tf
└── terraform.tfvars
在 main.tf 中声明 Provider 和 Resource:
terraform {
required_providers {
alicloud = {
source = "aliyun/alicloud"
version = "~> 1.200"
}
}
}
provider "alicloud" {
region = var.region
}
resource "alicloud_db_instance" "mysql" {
engine = "MySQL"
engine_version = "8.0"
instance_type = "rds.mysql.c1.large"
instance_storage = 100
category = "HighAvailability"
name = "demo-mysql"
}
执行流程是:先运行 terraform init,它会下载 alicloud Provider 插件并初始化后端;再运行 terraform plan,Terraform 读取配置、对比 State,输出“将新增 1 个资源”的计划;确认无误后运行 terraform apply,真正调用云 API 创建实例。创建完成后,实例的 ID、连接地址等信息会被写入 State 文件。下次修改配置再 plan,Terraform 就能算出是改参数还是重建。
这里有一个关键判断依据:plan 输出中的符号。+ 表示新增,~ 表示原地修改,-/+ 表示先销毁再重建。对于数据库,看到 -/+ 就要格外警惕,因为重建通常意味着数据丢失。如果只是改一个白名单,正常应该是 ~。如果 plan 显示要重建,说明你改到了不可变字段(如引擎版本、存储类型),需要改用其他方式(如先建新实例再迁移)。
Provider 与 Resource 模型
Terraform 本身不直接认识阿里云、AWS 或腾讯云,它通过 Provider 插件与云平台对话。Provider 负责三件事:认证(读取 AK/SK 或环境变量)、定义资源类型(如 alicloud_db_instance)、把 Terraform 的声明翻译成云 API 调用。可以理解为:Terraform 是大脑,Provider 是翻译官,云平台是执行者。
Resource 是 Terraform 管理的最小单元,每个 Resource 对应云上的一个真实对象。Resource 块由三部分组成:类型(type)、本地名称(name)和参数体(arguments)。类型决定“创建什么”,本地名称用于在配置内部引用,参数体描述“创建成什么样”。例如 alicloud_db_instance.mysql 中,alicloud_db_instance 是类型,mysql 是本地名称,花括号内是参数。
Resource 之间可以互相引用,形成依赖关系。比如创建一个 Redis 实例后,把它的连接地址写入应用配置,就可以用 alicloud_kvstore_instance.redis.connection_domain 来引用。Terraform 会自动分析这种引用,决定创建顺序:先建 Redis,再建依赖它的资源。这种隐式依赖比手工写顺序更可靠。
name 字段决定。两者可以不同,但建议保持一致,便于排查。下面这张图把本节的核心关系串起来:左侧是 Terraform 配置层,中间是 Provider 翻译层,右侧是云上真实资源,底部是 State 记录的实际状态。理解这张图,就理解了 Terraform 编排数据库与中间件的基本模型。
回到实践。假设你要为测试环境创建一套“MySQL + Redis”组合,第一步不是急着写代码,而是先确认三件事:用哪个 Provider、Region 是什么、认证信息从哪里来。认证信息建议通过环境变量 ALICLOUD_ACCESS_KEY 和 ALICLOUD_SECRET_KEY 注入,不要写进 .tf 文件,避免密钥泄露。第二步,把 Region、实例规格、存储大小抽成变量,放在 variables.tf 中,这样同一套代码可以通过不同的 .tfvars 文件部署到测试和生产。第三步,先只写一个 Resource,跑通 init、plan、apply 全流程,确认 State 正常生成,再逐步增加 Redis、白名单、参数组等资源。
判断是否成功,看三个信号:plan 输出没有报错且变更数量符合预期;apply 完成后云控制台能看到实例,且实例 ID 与 State 中记录一致;再次执行 plan 时显示 “No changes”,说明配置与真实状态已经收敛。如果第二次 plan 仍然显示有变更,通常说明某个参数被云平台自动改写(如默认参数组),需要显式声明该参数或使用 lifecycle 忽略它。