2. Terraform核心机制回顾

在进入 Kubernetes 平台交付的具体实践之前,有必要把 Terraform 的四个核心机制重新梳理清楚:Provider 负责与目标平台对话,Resource 描述期望的基础设施对象,State 记录 Terraform 眼中的真实世界,Plan/Apply 循环则把“期望”与“现状”之间的差异转化为可执行的动作。理解这四者如何咬合,是后续用 Terraform 管理 Namespace、Deployment、Service、Ingress 乃至整个集群的前提。

Provider:Terraform 与外部平台的适配层

Provider 是 Terraform 与外部 API 之间的插件。它把 Terraform 的通用配置语言翻译成某个平台能理解的 HTTP 请求、SDK 调用或命令行操作。对于 Kubernetes 平台交付,最常用的 Provider 有两类:kubernetes Provider 直接操作 Kubernetes API Server,适合管理 Namespace、Deployment、Service、ConfigMap 等集群内对象;helm Provider 则封装 Helm Chart 的安装与升级,适合交付 Ingress Controller、Prometheus、Cert-Manager 等复杂组件。

Provider 需要配置连接信息。以 kubernetes Provider 为例,典型配置如下:

provider "kubernetes" {
  config_path    = "~/.kube/config"
  config_context = "prod-cluster"
}

这里的 config_path 指向 kubeconfig 文件,config_context 指定使用哪个上下文。Provider 配置本身不创建任何资源,它只是告诉 Terraform“接下来要跟哪个集群、以什么身份对话”。如果 Provider 配置错误,Plan 阶段就会在刷新状态时失败,而不是等到 Apply 才报错。

Resource:声明式描述期望状态

Resource 是 Terraform 配置中的最小管理单元。每个 Resource 块声明一个基础设施对象,并给出它的期望属性。Terraform 不关心“如何创建”,只关心“最终应该是什么样”。例如,声明一个 Kubernetes Namespace:

resource "kubernetes_namespace" "app" {
  metadata {
    name = "demo-app"
    labels = {
      "app.kubernetes.io/part-of" = "demo-platform"
    }
  }
}

这段配置表达的是:集群中应存在一个名为 demo-app 的 Namespace,并带有指定标签。如果该 Namespace 已存在但标签不同,Terraform 会尝试修正标签;如果不存在,则创建。Resource 的地址由类型和名称组成,如 kubernetes_namespace.app,这个地址在 State 中唯一标识该对象。

Resource 之间可以建立依赖关系。例如,Deployment 需要放在某个 Namespace 中,可以通过 depends_on 显式声明,也可以让 Terraform 根据属性引用自动推断。依赖关系决定了创建和销毁的顺序,这是声明式管理能够正确落地的重要保障。

State:Terraform 的“记忆”

State 是 Terraform 记录“上次操作后真实世界是什么样”的数据库。它保存了每个 Resource 的地址、属性值、依赖关系以及 Provider 返回的元数据。没有 State,Terraform 每次 Plan 都只能看到配置文件,无法知道哪些资源已经存在、哪些需要更新、哪些需要删除。

State 的默认存储是本地文件 terraform.tfstate,但在团队协作和 Kubernetes 平台交付场景中,必须使用远程后端,例如 S3、GCS、Azure Blob 或 Terraform Cloud。远程后端不仅提供共享存储,还支持状态锁定,防止两个人同时 Apply 导致状态冲突。一个典型的 S3 后端配置如下:

terraform {
  backend "s3" {
    bucket         = "tf-state-demo"
    key            = "k8s-platform/terraform.tfstate"
    region         = "ap-southeast-1"
    dynamodb_table = "tf-lock"
    encrypt        = true
  }
}

State 中可能包含敏感信息,例如 Secret 的明文值或 kubeconfig 凭据,因此后端存储必须加密,访问权限必须严格控制。此外,State 不应手工编辑,任何手动修改都可能导致 Terraform 对真实世界的认知与实际情况脱节。

Plan/Apply 循环:从差异到动作

Terraform 的核心工作循环可以概括为:Refresh → Plan → Apply。执行 terraform plan 时,Terraform 先刷新 State,从 Provider 读取每个已管理对象的当前属性,然后与配置文件中的期望状态逐项比较,生成一份执行计划。计划中用符号标记动作:+ 表示创建,~ 表示原地更新,-/+ 表示销毁后重建,- 表示销毁。

执行 terraform apply 时,Terraform 按依赖顺序执行计划中的动作,并在每一步完成后更新 State。如果某一步失败,State 会记录已完成的部分,后续再次 Apply 时会从失败点继续,而不是从头开始。这种“增量收敛”的特性,使得 Terraform 适合管理长期演进的平台环境。

下面这张图展示了四个核心机制之间的协作关系:Provider 连接目标平台,Resource 声明期望状态,State 记录已知状态,Plan/Apply 循环负责计算差异并执行收敛。

配置文件 (.tf) resource "kubernetes_namespace" resource "kubernetes_deployment" 期望状态声明 Provider kubernetes / helm 连接 API Server 翻译为 API 调用 Kubernetes 集群 Namespace / Deployment Service / Ingress 真实运行状态 解析 API 调用 State 资源地址 + 属性 依赖关系 + 元数据 远程后端 + 状态锁 Refresh 读取现状 Plan / Apply 比较期望 vs 现状 生成执行计划 按依赖顺序执行 已知状态 期望状态 执行动作 Apply 后写回 State 核心循环:Refresh 读取真实状态 → Plan 计算差异 → Apply 执行收敛 → State 记录新状态 声明式管理的关键:只描述“应该是什么”,由 Terraform 负责“怎么做到”

声明式管理的关键概念

声明式与命令式的根本区别在于:命令式关注“执行哪些步骤”,声明式关注“最终达到什么状态”。在 Terraform 中,这意味着你不需要写“如果 Namespace 不存在则创建,否则检查标签”这样的逻辑,只需要声明 Namespace 应该存在且带有指定标签。Terraform 会自动完成差异计算和动作编排。

但声明式管理有几个必须注意的实践要点。第一,State 必须与真实世界保持一致。如果有人绕过 Terraform 手工修改了集群中的资源,State 就会过时,Plan 可能产生意外结果。第二,Plan 必须被认真审阅。尤其是出现 -/+ 重建标记时,要确认是否真的需要销毁重建,避免误删有状态服务。第三,敏感信息不应出现在配置文件中。Kubernetes Secret 的明文值应通过变量、环境变量或外部数据源注入,而不是硬编码在 .tf 文件里。

在 Kubernetes 平台交付中,建议把 State 按环境或按集群拆分,例如 dev/、staging/、prod/ 各自独立后端。这样单个环境的 Apply 失败不会影响其他环境,也便于权限隔离和变更审计。