1. Kubernetes Provider 与认证
一句话总结: Kubernetes Provider 通过 kubeconfig 或 Token 完成认证,Provider 版本选型要在 kubernetes/helm/kubectl 三个之间按需组合。
Terraform 管理 K8s 资源有三类 Provider:kubernetes(原生 API 资源)、helm(Helm Chart 发布)、kubectl(通用 YAML 应用)。它们各司其职,组合使用覆盖「声明原生资源 + 发布 Chart + 应用清单」。
terraform {
required_providers {
kubernetes = {
source = "hashicorp/kubernetes"
version = "~> 2.0"
}
helm = {
source = "hashicorp/helm"
version = "~> 2.0"
}
}
}
1.1 认证方式
Provider 读取 kubeconfig 与上下文,或显式指定 host/token/cluster_ca_certificate。云上集群通常用「云 Provider 获取凭据 + 传给 k8s Provider」。
provider "kubernetes" {
host = data.aws_eks_cluster.cluster.endpoint
cluster_ca_certificate = base64decode(data.aws_eks_cluster.cluster.certificate_authority[0].data)
token = data.aws_eks_cluster_auth.cluster.token
}
provider "helm" {
kubernetes {
host = data.aws_eks_cluster.cluster.endpoint
cluster_ca_certificate = base64decode(data.aws_eks_cluster.cluster.certificate_authority[0].data)
token = data.aws_eks_cluster_auth.cluster.token
}
}
一句话:认证链通常是「云 Provider 建集群 → data 读取凭据 → 两个 K8s Provider 复用同一组 endpoint/token」。
1.2 三个 Provider 的边界
| Provider | 管什么 | 用在哪 |
|---|---|---|
| kubernetes | deployment/service/configmap/secret 等原生资源 | 内联声明小资源 |
| helm | release 的安装/升级/回滚 | 发布第三方 Chart |
| kubectl | 任意 YAML 清单、自定义资源的直接应用 | 现有 YAML 复用 |
2. kubectl / helm 资源编排
一句话总结: helm Provider 发布 Chart 并管理 release 生命周期,kubectl Provider 把任意 YAML 当资源管理,两者把「手敲 kubectl apply」变成声明式。
2.1 helm release
resource "helm_release" "nginx_ingress" {
name = "nginx-ingress"
repository = "https://kubernetes.github.io/ingress-nginx"
chart = "ingress-nginx"
namespace = "ingress-nginx"
create_namespace = true
version = "4.10.0"
set {
name = "controller.replicaCount"
value = "2"
}
set {
name = "controller.service.type"
value = "LoadBalancer"
}
}
set 覆盖 values,复杂覆盖建议用 values 字段读取独立 values 文件,便于评审与复用。
resource "helm_release" "prometheus" {
name = "prometheus"
repository = "https://prometheus-community.github.io/helm-charts"
chart = "prometheus"
values = [file("${path.module}/values/prometheus-values.yaml")]
}
2.2 kubectl 应用清单
kubectl Provider 通过 apply 语义把 YAML 作为资源管理,删除时自动清理。
resource "kubectl_manifest" "example" {
yaml = <<-YAML
apiVersion: v1
kind: Namespace
metadata:
name: app-team
YAML
}
resource "kubectl_manifest" "deploy" {
yaml = file("${path.module}/manifests/deployment.yaml")
depends_on = [helm_release.nginx_ingress]
}
2.3 优先级建议
能用 kubernetes Provider 原生声明的,不绕道 kubectl;能用 helm 发布 Chart 的,不手工 apply。原生 Provider 有 schema 校验,kubectl 是「直接透传 YAML」,越直接越容易出隐藏错误。
3. Ingress 与证书
一句话总结: Ingress 用 annotation 绑定 ingress-nginx 控制器,TLS 证书用 cert-manager 自动签发,Terraform 只需声明 Ingress 与 Certificate 资源。
3.1 Ingress 资源
resource "kubernetes_ingress_v1" "web" {
metadata {
name = "web-ingress"
namespace = "app-team"
annotations = {
"nginx.ingress.kubernetes.io/rewrite-target" = "/"
}
}
spec {
ingress_class_name = "nginx"
rule {
host = "app.example.com"
http {
path {
path = "/"
path_type = "Prefix"
backend {
service {
name = "web"
port {
number = 80
}
}
}
}
}
}
}
}
3.2 证书:cert-manager 声明式签发
证书不在 Terraform 里生成,而是声明 Certificate 自定义资源,由 cert-manager 控制器自动签发并写回 Secret。
resource "kubectl_manifest" "cert" {
yaml = <<-YAML
apiVersion: cert-manager.io/v1
kind: Certificate
metadata:
name: app-tls
namespace: app-team
spec:
secretName: app-tls-secret
issuerRef:
name: letsencrypt-prod
kind: ClusterIssuer
dnsNames:
- app.example.com
YAML
}
随后 Ingress 引用该 Secret 提供 TLS:
spec {
tls {
hosts = ["app.example.com"]
secret_name = "app-tls-secret"
}
}
一句话:Terraform 只声明「我要一个证书」,签发的执行者是集群里的 cert-manager,这正是「声明式 + 控制器收敛」的典型。
4. Operator 与 CRD
一句话总结: Operator 把复杂应用封装成 CRD,Terraform 通过 kubectl/kubernetes Provider 声明 CRD 实例,由 Operator 控制器收敛到期望状态。
4.1 为什么需要 CRD
数据库、消息队列等有状态组件,原生 Kubernetes 资源表达不了「备份、主从、扩容」语义。Operator 引入自定义资源(CRD),把运维逻辑下沉到控制器。
resource "kubectl_manifest" "postgres_cluster" {
yaml = <<-YAML
apiVersion: postgres-operator.crunchydata.com/v1beta1
kind: PostgresCluster
metadata:
name: hippo
namespace: app-team
spec:
postgresVersion: 15
instances:
- name: instance1
replicas: 3
backups:
pgbackrest:
repos:
- name: repo1
volume:
volumeClaimSpec:
accessModes: ["ReadWriteOnce"]
resources:
requests:
storage: 1Gi
YAML
}
4.2 谁先装 CRD
CRD 由 Operator 安装(通常 helm release 同时带上 CRD),Terraform 声明实例时要用 depends_on 保证 Operator 已就绪。
resource "helm_release" "pg_operator" {
name = "postgres-operator"
repository = "https://crunchydata.github.io/charts"
chart = "postgres-operator"
}
resource "kubectl_manifest" "postgres_cluster" {
yaml = file("${path.module}/manifests/postgres.yaml")
depends_on = [helm_release.pg_operator]
}
一句话:CRD 实例由 Terraform 声明,收敛由 Operator 负责,Terraform 只关心「清单应用成功」,不关心控制器内部怎么达成。
5. 与 k8s 控制器配合
一句话总结: Terraform 负责「应用清单」,K8s 控制器负责「收敛现状」,两者之间用 depends_on、annotations 与现状读取协作,避免 Terraform 重复造轮子。
5.1 现状读取与等待
用 kubernetes_* 的 data source 读取控制器已生成的现状(如 Service 的 LoadBalancer IP),把集群内产生的外部值带回 Terraform 供其他资源使用。
data "kubernetes_service" "nginx" {
metadata {
name = "ingress-nginx-controller"
namespace = "ingress-nginx"
}
}
output "lb_address" {
value = data.kubernetes_service.nginx.status[0].load_balancer[0].ingress[0].hostname
}
5.2 时间与依赖的边界
| 场景 | 谁负责 |
|---|---|
| 清单应用成功 | Terraform 的 apply 完成 |
| Pod 真正 Running | Deployment 控制器收敛 |
| 证书签发完成 | cert-manager 控制器收敛 |
| LoadBalancer 分配 | 云负载均衡异步分配 |
Terraform 的 apply 返回不等于集群「真正就绪」。需要等待时,可用 kubernetes_manifest 的 wait 字段或外部的就绪探测脚本。
5.3 避免与控制器抢资源
Helm 注释管理(app.kubernetes.io/managed-by: Helm)标注的资源被 Terraform 直接改会冲突。要么全交给 Helm,要么全交给 Terraform,不要两边同时改同一资源。跨工具交接用 import 或移除注释后再接管。
6. 权限与安全
一句话总结: Terraform 用的 K8s 凭据应是最小权限的 ServiceAccount,敏感值进 Secret 资源时走敏感字段标记与加密,避免明文入库。
6.1 最小权限的 ServiceAccount
Terraform 与 CI 使用的 K8s 凭据遵循最小权限:只能创建它需要的命名空间与资源类型。
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
name: terraform-ops
namespace: app-team
rules:
- apiGroups: ["apps"]
resources: ["deployments"]
verbs: ["get", "list", "create", "update", "patch", "delete"]
- apiGroups: [""]
resources: ["services", "configmaps", "secrets"]
verbs: ["get", "list", "create", "update", "patch", "delete"]
6.2 敏感值处理
Secret 内容用 sensitive = true 变量传入,不要以字面量出现在 tfvars 中。
resource "kubernetes_secret_v1" "app" {
metadata {
name = "app-secret"
namespace = "app-team"
}
data = {
"password" = var.app_password
}
}
variable "app_password" {
type = string
sensitive = true
}
一句话:K8s 侧的权限与敏感值治理,与 AWS 侧的最小权限 IAM 是同一套安全哲学,只是落在 Role/RoleBinding 与 Secret 上。
7. 常见坑
一句话总结: K8s Provider 高发坑集中在 namespace 不存在、Helm 与原生资源冲突、证书签发时序、CRD 顺序,前三个几乎每天都会遇到。
| 坑 | 现象 | 正确做法 |
|---|---|---|
| namespace 缺失 | 资源创建报「namespace not found」 | 先声明 namespace,或 create_namespace |
| 双工具改一资源 | 每次 plan 都出 diff | 统一管理方,跨工具用 import 交接 |
| 证书未就绪 | Ingress 引用不存在的 Secret | 用 depends_on 或 wait 就绪探测 |
| CRD 未安装 | manifest 应用失败 | CRD 随 Operator 先装,depends_on |
| Provider 版本漂移 | 行为不一致 | required_providers 锁版本 |
7.1 一个稳妥的编排顺序
1. kubernetes_namespace 声明 namespace
2. helm_release 安装基础组件(ingress、cert-manager、operator)
3. kubectl_manifest 应用 CRD 实例(Certificate、PostgresCluster 等)
4. kubernetes_* 声明应用原生资源(deployment/service/ingress)
5. data 读取控制器回写的现状,输出给其他系统
8. 总结
Kubernetes Provider 把「kubectl 手工操作」变成可审查的声明式配置:
| 环节 | 要点 |
|---|---|
| Provider | kubernetes/helm/kubectl 三件套按需组合 |
| 认证 | 云 Provider 建集群,data 取凭据复用 |
| 编排 | helm 管 Chart,kubectl 管 YAML,原生管资源 |
| Ingress | annotation 绑定控制器,证书交给 cert-manager |
| CRD | Terraform 声明实例,Operator 控制器收敛 |
| 协作 | apply 完成 ≠ 集群就绪,必要时 wait |
| 安全 | ServiceAccount 最小权限,敏感值不进仓库 |
| 顺序 | namespace → helm → CRD → 原生资源 |
一句话收尾:Terraform 在 K8s 世界的角色是「声明期望清单」,把收敛的执行权交给集群内的控制器,二者各司其职,才能把声明式 IaC 的优势延伸到云原生。下一篇「数据源与远程数据」将回到 Terraform 本身,讲 data source 与远程 state 的读取机制。
延伸阅读
继续阅读
探索更多技术文章
浏览归档,发现更多关于系统设计、工具链和工程实践的内容。