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 决定,且难以跨集群。
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 之前就发现权限问题,避免半途失败留下不一致状态。