3. Kubernetes Provider配置

在 Terraform 中操作 Kubernetes 资源,第一步永远是让 Provider 知道“连到哪个集群、以什么身份连”。Terraform 的 kubernetes Provider 本身不负责创建集群,它只负责在已有集群上做声明式变更。因此,Provider 配置的质量直接决定了后续 plan 与 apply 是否稳定、可复现、可审计。本节围绕三种连接方式——kubeconfig、exec 插件、in-cluster——讲清它们的适用场景、配置写法、认证授权链路以及命名空间隔离的落地方式。

三种连接方式的定位与选择

kubeconfig 是最常见的方式:Provider 读取一个 kubeconfig 文件(或直接内联 host、client_certificate、client_key、cluster_ca_certificate),用其中的证书或 token 完成认证。它适合本地开发、CI 中挂载凭据文件、以及跨多个集群切换的场景。缺点是凭据是静态的,证书过期或 token 轮换后需要更新文件。

exec 插件方式在 kubeconfig 中配置 exec 段,让 kubectl 风格的凭据插件在每次请求时动态生成短期 token。它适合云托管集群(EKS、GKE、AKS)以及使用 OIDC、Vault 等短期凭据的环境。优点是凭据不落盘、自动刷新;缺点是依赖外部可执行文件,CI 镜像里必须包含对应插件。

in-cluster 方式让 Terraform 运行在集群内部的 Pod 中,Provider 自动读取 /var/run/secrets/kubernetes.io/serviceaccount/ 下的 token、CA 和 namespace。它适合“集群内自管理”的场景,例如用 Terraform 在集群里管理 CRD、命名空间或平台组件。优点是无需分发 kubeconfig;缺点是权限完全由该 ServiceAccount 的 RBAC 决定,且难以跨集群。

Terraform kubernetes Provider 三种连接方式与认证链路 Terraform provider "kubernetes" plan / apply ① kubeconfig config_path / 内联证书 client_certificate + key 静态凭据,适合本地/CI ② exec 插件 aws eks get-token gke-gcloud-auth-plugin 短期 token,自动刷新 ③ in-cluster ServiceAccount token /var/run/secrets/... Pod 内运行,RBAC 决定权限 kube-apiserver 认证 → 授权 → 准入 Authentication / RBAC 命名空间隔离 ns: dev ns: staging ns: prod metadata.namespace 选择原则:本地/CI 用 kubeconfig;云托管集群用 exec 插件;集群内自管理用 in-cluster;三者最终都经 apiserver 认证授权后落到目标 namespace

kubeconfig 方式的配置与注意点

最直接的写法是指定 config_path,并可用 config_context 选择上下文:

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

在 CI 中,通常把 kubeconfig 内容写入临时文件,再用环境变量 KUBE_CONFIG_PATH 指向它,避免把路径硬编码。也可以完全内联,不依赖文件:

provider "kubernetes" {
  host                   = "https://10.0.0.10:6443"
  cluster_ca_certificate = file("${path.module}/ca.crt")
  client_certificate     = file("${path.module}/client.crt")
  client_key             = file("${path.module}/client.key")
}

这里有一个容易踩的坑:client_certificate 与 client_key 必须成对出现,且证书中的 CN/O 决定 RBAC 身份。如果只给 token,则用 token 参数。无论哪种,host 必须与证书 SAN 匹配,否则会报 x509 校验失败。建议把证书文件放在模块目录外,通过变量或 file() 读取,避免误提交到仓库。

exec 插件:让凭据动态生成

exec 方式不在 Provider 里写凭据,而是让 kubeconfig 的 users[].exec 段调用外部命令。以 EKS 为例,kubeconfig 片段如下:

users:
- name: eks-prod
  user:
    exec:
      apiVersion: client.authentication.k8s.io/v1beta1
      command: aws
      args:
        - eks
        - get-token
        - --cluster-name
        - prod-cluster
        - --region
        - ap-southeast-1

Terraform 侧只需 config_path 指向该文件即可。每次请求时,Provider 会执行 aws eks get-token 拿到 15 分钟有效的 token。GKE 则使用 gke-gcloud-auth-plugin,AKS 使用 kubelogin。关键点:CI 镜像必须安装对应 CLI 并完成登录(如 aws configure 或 OIDC 角色扮演),否则 exec 会失败。此外,exec 命令的输出格式必须符合 ExecCredential 规范,否则 Provider 无法解析。

in-cluster:Pod 内的零配置连接

当 Terraform 以 Job 或 Pod 形式运行在集群内时,Provider 可以留空,自动使用 in-cluster 配置:

provider "kubernetes" {
  # 不写 host/token,自动读取 ServiceAccount
}

它读取的是挂载到 Pod 的 token、ca.crt 和 namespace。此时权限完全由该 ServiceAccount 绑定的 Role/ClusterRole 决定。例如只管理某个命名空间,就创建 Role 并绑定到该 SA,避免使用 cluster-admin。注意:in-cluster 的 token 默认会过期(取决于集群配置),但 kubelet 会自动轮换挂载文件,Provider 每次读取最新内容,因此通常无需手动干预。

认证授权与命名空间隔离的落地

三种方式最终都汇入 apiserver 的“认证 → 授权 → 准入”链路。认证解决“你是谁”,授权(RBAC)解决“你能做什么”,准入控制解决“是否符合策略”。在 Terraform 中,命名空间隔离主要靠两件事:一是 Provider 不设置全局 namespace,而是在每个资源里显式写 metadata.namespace;二是用 kubernetes_namespace 资源先创建命名空间,再在其中创建资源。

resource "kubernetes_namespace" "dev" {
  metadata { name = "dev" }
}

resource "kubernetes_config_map" "app" {
  metadata {
    name      = "app-config"
    namespace = kubernetes_namespace.dev.metadata[0].name
  }
  data = { LOG_LEVEL = "debug" }
}

这样命名空间与资源形成依赖,terraform destroy 时也会按序清理。若多个团队共用集群,建议每个团队一个 Terraform 工作区 + 一个独立 ServiceAccount,并用 RoleBinding 限定到各自命名空间。判断隔离是否生效,可以用 kubectl auth can-i 验证:

kubectl auth can-i create configmaps \
  --namespace dev \
  --as system:serviceaccount:dev:tf-runner

返回 yes 表示授权正确,返回 no 则说明 RoleBinding 缺失或命名空间不匹配。把这条检查纳入 CI 的 plan 前置步骤,可以在真正 apply 之前就发现权限问题,避免半途失败留下不一致状态。

选择连接方式时,优先考虑凭据的生命周期与运行位置:静态凭据用 kubeconfig,短期凭据用 exec,集群内运行用 in-cluster。三者不是互斥的,同一套代码可以通过变量切换,但生产环境应固定一种并纳入审计。