开一台云服务器,通常不需要写代码。在控制台选地域、镜像、规格和网络,确认之后等它启动就行。

控制台已经能完成的事情,为什么还要换一种语言重新描述?学习 IaC 时,我觉得这个问题值得先想清楚。

如果只创建一台临时机器,手动操作确实方便。麻烦出现在第二套环境上。测试环境跑通之后,生产环境也要准备同样的网络、安全组和服务器。照着控制台再点一遍,哪些参数必须一致,哪些参数应该变化?过一段时间,同事又调整了安全组,原来的操作文档还能不能作为依据?

创建动作结束得很快,后面的维护却会持续很久。基础设施即代码,也就是 Infrastructure as Code,主要处理的就是这段持续变化的过程。

从控制台到脚本,已经解决了哪些问题?

云资源通常可以通过控制台、CLI 或 API 创建。控制台适合人工操作,CLI 方便写脚本,API 则可以接入应用和流水线。它们最后都会调用云平台提供的能力。

把操作写成脚本,已经能省掉不少重复劳动。例如创建安全组、开通服务器,再把公网 IP 输出给后续部署任务。只要接口和参数明确,这条链路就可以自动执行。

但脚本运行到一半失败时,问题会多起来:安全组已经创建,服务器还没有创建,再跑一次会怎样?如果机器已经存在,只想修改实例名称,脚本会更新原来的机器,还是再开一台?

这些逻辑都能自己写,只是脚本需要逐渐掌握资源查询、身份识别、差异比较和失败恢复。随着管理对象增多,维护成本也会增加。

Terraform 把这部分资源管理能力做成了通用工具。我们描述希望得到的资源,工具结合已经管理的对象和云平台返回的信息,计算接下来应该执行的操作。

声明式配置到底声明了什么?

可以把一个基础设施需求写成几句话:

  • 在指定地域准备一台 Ubuntu 云服务器。
  • 服务器接入指定子网,并绑定一个安全组。
  • 安全组允许管理端访问 SSH。
  • 创建完成后,提供服务器的公网 IP。

这里描述了目标和资源关系。服务器创建前需要哪些信息、哪些 API 要先调用,可以交给工具根据引用关系安排。

下面是 Terraform 中声明安全组的一个片段:

1
2
3
4
resource "tencentcloud_security_group" "web" {
name = "iac-demo-web"
description = "Security group for the IaC demo"
}

这段配置表达的是:当前配置要管理一个腾讯云安全组,它在 Terraform 内部的名称是 web,云上的名称是 iac-demo-web。第一次应用时需要创建;资源已经存在且配置没有变化时,就不应该再创建一个副本。

Terraform 的这种行为依赖状态记录和 Provider 的实现。不能据此推断“任何脚本执行多少次都没有副作用”。例如在资源创建后追加一段远程 Shell,它是否能重复执行,还要看脚本本身。

IaC 是一类工程实践,也不要求所有工具都使用同一种语言、同一种状态文件。本文讨论的声明式配置和 state,是理解 Terraform 这条实现路线的重点。

代码、state 和云上资源为什么要分开?

配置文件和 state 都描述资源,初看似乎保存一份就够了。

实际创建安全组之后,云平台会返回一个 ID。Terraform 需要记录:配置里的 tencentcloud_security_group.web,对应云上的哪个安全组。下一次修改配置时,它才能找到同一个对象。

三者承担的工作不同:

对象 保存什么 用来回答什么
.tf 配置 期望的资源属性与依赖关系 希望环境变成什么样?
state 资源地址与远端对象的映射,以及已记录的属性 这份配置正在管理哪些对象?
云平台 实际存在的资源及其当前状态 资源现在到底是什么样?

state 是上一次记录下来的信息,云上资源可能已经被其他操作改变。正常执行 terraform plan 时,Terraform 会通过 Provider 读取相关资源,刷新它对远端状态的认识,再生成变更计划。

如果有人从控制台修改了被管理的属性,下一次计划可能就会显示差异。这叫配置漂移。此时需要判断这次修改是否应该保留:合理的调整应写回配置,不合理的调整可以通过后续应用纠正。

Terraform CLI 通常在执行命令时进行这些工作。它不会因为电脑里保存着一份 .tf,就一直在后台检查云资源。

写进 Git 后,变更怎样被审查?

假设原来只允许管理端访问 SSH,现在要开放一个业务端口。通过控制台直接修改,评审者只能依赖操作说明和平台日志。通过 IaC 修改,则可以先看到配置差异,再检查 Terraform 计算出的执行计划。

一条常见的流程是:

1
2
3
4
5
6
7
8
9
修改配置并提交 Git
↓
检查格式和配置有效性
↓
生成 plan,检查受影响资源
↓
评审后执行 apply
↓
检查资源状态与实际访问结果

Git diff 和 plan 要一起看。修改一行代码,有时会触发资源替换;一个看起来很小的参数调整,可能意味着先删除旧资源再创建新资源。单看代码行数,容易低估影响。

IaC 也给自动化检查提供了入口。例如在流水线里检查网络开放范围、资源标签和允许使用的实例规格。不过这些检查需要额外配置,Terraform 不会自动知道团队的生产标准。

版本控制能回退到什么程度?

配置存进 Git,可以追溯谁在什么时候修改了什么,也可以恢复旧版本的配置。接下来仍然需要重新生成计划,判断怎样把当前环境调整到旧配置描述的样子。

这里有一个不能省略的限制:已经删除的数据库,即使按旧配置重新创建,里面的数据也不会自动回来。数据恢复需要备份和恢复机制,云盘、IP 等对象能否原样恢复,也取决于资源本身的生命周期。

因此,我会把“回退配置”和“恢复业务”分开检查。前者由版本管理帮助完成,后者还涉及数据、访问入口以及服务是否恢复可用。

类似地,相同配置能够提高环境的一致性,但不能承诺每次得到完全相同的结果。若配置查询“当前可用的某个镜像”并直接取第一条,下一次查询结果变了,新建环境也可能变化。需要稳定复现时,镜像、依赖版本和关键输入都要明确下来。

Terraform 负责到哪里?

Terraform 由 HashiCorp 开发,通过 Provider 接入不同平台。腾讯云、AWS、阿里云等云平台,以及 Kubernetes、Cloudflare 等服务,都可以通过相应的 Provider 管理。

多平台支持带来的便利,是可以在一个工作流里管理不同系统。至于具体资源怎么写,仍然要看各个平台的资源模型。腾讯云 CVM 的配置不能只改一个 Provider 名称,就变成 AWS EC2。

在交付链路中,我会先按管理对象划分职责:

管理对象 常见的处理方式
网络、云服务器、云数据库等资源 Terraform 调用平台 API 管理
机器内部的软件与系统配置 镜像预置、cloud-init、Ansible 等
集群里的应用运行状态 Kubernetes 及相应的应用交付工具

这些边界可以重叠。例如 Terraform 有 Helm Provider,也能调用脚本安装软件。是否放在同一份配置里,要考虑它们的变更频率、依赖关系和失败后的恢复方式。

学习时把虚拟机、K3s 和 Argo CD 串起来,有助于看懂完整的依赖链。实际维护时,如果应用频繁更新,而基础设施变化很少,就值得把两者的执行流程分开。

笔记还提到了 Pulumi 和 Crossplane。它们都与 IaC 有关,但使用方式不同:Pulumi 支持使用通用编程语言描述资源;Crossplane 基于 Kubernetes 的资源与控制器机制管理外部基础设施。这里先把 Terraform 的工作流学清楚,工具对比可以留到出现具体选型需求时再做。

从一个小环境开始验证

对学习项目,我更愿意先管理一台服务器和它的安全组。创建完成后,修改一个容易检查的属性,重新生成计划,再观察实际结果。最后确认这套环境能按预期清理。

这几步能暴露很多只看概念时察觉不到的问题:凭证从哪里读取,资源 ID 保存在哪里,计划是否出现了意外删除,重复执行为什么没有继续创建新机器。

下一篇《Terraform概述》会沿着这套环境,展开 HCL、Provider、资源依赖和 state 的关系。

参考资料