很多刚接触Kubernetes的开发者,或者经常和容器集群打交道的运维,都有过这样的经历:为了把自己写的Web应用暴露成外网可访问的地址,要手动敲好几条kubectl命令,从弄Ingress规则,到给Ingress控制器配权限(RBAC),每一步都得小心写错。更麻烦的是,要是团队有好几个人操作,每个人的配置可能不一样,过段时间自己都记不清当时的配置逻辑,改个规则还要重新翻之前的命令历史。这时候,用Terraform来做自动化编排,就能把这些麻烦都解决掉。

一、手动操作的痛点

手动配置K8s的Ingress和RBAC,核心问题在于“不可重复、容易出错、没有版本记录”。

1.1 具体踩过的坑

我之前帮公司搭测试集群,手动部署Ingress控制器的时候,因为忘了给ServiceAccount绑定对应的权限,导致Ingress一直没法获取Service的后端信息,折腾了快一小时才发现是RBAC的绑定写错了。还有一次,团队里两个开发同时改Ingress的YAML,一个改了域名,另一个改了路径,最后apply的时候覆盖了之前的配置,导致测试环境的所有应用都没法访问,排查了半天才找到冲突点。这些问题本质上都是手动操作没有“追踪记录”,也没法自动处理依赖,全靠人记,自然容易出问题。

二、Terraform的解决方案

Terraform是现在很火的“基础设施即代码”工具,核心就是把需要部署的资源用代码写出来,像写应用代码一样可以存到Git里,版本管理,多人协作也不会乱。对于K8s来说,Terraform有专门的Kubernetes Provider,能直接声明式地创建、修改、删除K8s的各种资源,包括Ingress这种路由规则,还有RBAC的权限资源。

2.1 核心原理

简单说就是,Terraform用Kubernetes Provider连接你的K8s集群,然后你写好的代码就是想要的资源状态,Terraform会自动对比集群当前的状态,算出要改的部分,然后执行对应的操作。比如你写了一个Ingress资源,Terraform会检查集群里有没有这个Ingress,没有就创建,有就对比配置,不一样就修改,不会瞎改其他资源。

三、具体实现示例(技术栈:Terraform + Kubernetes Provider)

接下来我给一个完整的示例,用Terraform配置Ingress和对应的RBAC权限,所有步骤都加了注释,你可以直接复制用,也可以根据自己的需求改。

# 配置Kubernetes Provider,这里用本地开发环境的kube config,适合新手
provider "kubernetes" {
  # 指向你本地的kube config路径,一般是~/.kube/config,不用改的话直接用这个就行
  config_path = "~/.kube/config"
  # 如果是生产环境,建议用变量传kube config内容,或者用环境变量,不要写死路径
}

# -------------------------- RBAC相关资源 --------------------------
# 1. 创建ServiceAccount:这个账号是给Ingress控制器用的,用来调用K8s的API
resource "kubernetes_service_account" "ingress_controller_sa" {
  metadata {
    name      = "nginx-ingress-controller-sa" # 自定义名字,要唯一
    namespace = "kube-system" # Ingress控制器通常部署在kube-system命名空间,这里对应
  }
}

# 2. 创建ClusterRole:定义Ingress控制器需要的权限,这里用最小权限原则,只给需要的
resource "kubernetes_cluster_role" "nginx_ingress_role" {
  metadata {
    name = "nginx-ingress-minimal-role" # ClusterRole是集群级的,名字不能和其他重复
  }

  # 规则1:允许获取、列表、监听services、endpoints、secrets(Ingress需要这些信息)
  rule {
    api_groups = [""] # 空表示核心API组
    resources  = ["services", "endpoints", "secrets"]
    verbs      = ["get", "list", "watch"] # 这些是Ingress必须的操作
  }

  # 规则2:允许操作Ingress资源(networking.k8s.io组的v1版本)
  rule {
    api_groups = ["networking.k8s.io"]
    resources  = ["ingresses"]
    verbs      = ["get", "list", "watch"]
  }
}

# 3. 创建ClusterRoleBinding:把刚才的ClusterRole权限绑定到ServiceAccount上
resource "kubernetes_cluster_role_binding" "nginx_ingress_role_binding" {
  metadata {
    name = "nginx-ingress-role-binding" # 集群级,唯一名字
  }

  # 绑定的角色,指定API组、类型、名字
  role_ref {
    api_group = "rbac.authorization.k8s.io"
    kind      = "ClusterRole"
    name      = kubernetes_cluster_role.nginx_ingress_role.metadata[0].name # 引用上面的ClusterRole名字,避免写错
  }

  # 绑定的主体(ServiceAccount),指定类型、名字、命名空间
  subject {
    kind      = "ServiceAccount"
    name      = kubernetes_service_account.ingress_controller_sa.metadata[0].name # 引用ServiceAccount名字
    namespace = kubernetes_service_account.ingress_controller_sa.metadata[0].namespace # 对应命名空间
  }
}

# -------------------------- Ingress资源 --------------------------
# 创建Ingress资源,用来暴露你的Web应用(这里假设你的应用叫web,在default命名空间的80端口)
resource "kubernetes_ingress_v1" "web_app_ingress" {
  metadata {
    name      = "web-app-ingress" # Ingress名字,同命名空间唯一
    namespace = "default" # 你的应用所在的命名空间,这里是default
    # 注解:指定用哪个Ingress控制器,这里是nginx,要是用traefik就改成traefik
    annotations = {
      "kubernetes.io/ingress.class" = "nginx"
    }
  }

  # Ingress的具体规则,域名和路径匹配
  spec {
    rule {
      host = "your-app.example.com" # 你的域名,要先解析到集群的Ingress控制器节点IP
      http {
        path {
          path = "/" # 匹配根路径,要是匹配子路径可以写成"/api"
          path_type = "Prefix" # 路径类型,Prefix是前缀匹配,Exact是精确匹配
          backend {
            service {
              name = "web-service" # 你的Web应用的Service名字,对应K8s里的Service资源
              port {
                number = 80 # 你的Service的端口号,应用跑在80端口
              }
            }
          }
        }
      }
    }
  }
}

你可以把这段代码存成main.tf,然后在同目录下运行terraform init初始化,再用terraform plan看计划(不会真的改集群),确认没问题后运行terraform apply,就会自动创建所有资源,比手动敲命令省心多了。

四、应用场景

这种用法适合很多场景:

  1. 个人开发环境:搭完集群,一条terraform apply就搞定Ingress和权限,不用再记kubectl命令;
  2. 团队协作的测试环境:把TF代码存在Git里,所有人改代码提交,合并后apply,不会有配置冲突;
  3. 生产环境标准化部署:所有环境用同一套TF代码,改配置只要改代码,审计方便,不会随便乱改;
  4. 多环境部署:用Terraform的Workspace功能,切换dev、test、prod环境,对应不同的域名、命名空间,一套代码搞定。

五、技术优缺点

先讲优点:第一,版本可控,代码存Git里,每一次修改都有记录,回滚也方便,比如之前改坏了,git checkout到之前的版本再apply就行;第二,减少手动错误,Terraform会自动检查配置的依赖,比如RBAC的ClusterRole和Binding的关系,不会出现绑定错的情况;第三,可复用性高,你可以把这个代码封装成模块,以后搭新集群直接调用模块,改几个参数就行;第四,声明式配置,你只要说想要什么样的状态,不用管怎么一步步做,Terraform自动处理。 再讲缺点:第一,要学一点Terraform的HCL语法,虽然不难,但对于完全没接触过的人可能要花一点时间;第二,对于非常简单的场景(比如一个应用的临时暴露),用Terraform可能有点“重”,不如直接敲命令快;第三,依赖Kubernetes Provider的稳定性,要是Provider和你的K8s版本不兼容,可能会出问题,需要注意更新Provider版本。

六、注意事项

这里给几个实际用的时候要注意的点:

  1. RBAC权限要最小化:刚才的ClusterRole只给了Ingress必须的权限,不要随便加多余的,比如不要给删除Pod、修改配置的权限,安全第一;
  2. Ingress控制器要和注解匹配:要是你部署的是Traefik控制器,注解里的ingress.class要改成traefik,不然Ingress不会被正确处理;
  3. kube config的安全性:生产环境不要把kube config提交到Git,建议用Terraform的变量传入kube config的内容,或者用环境变量,防止密钥泄露;
  4. 资源命名要唯一:K8s里的同类型资源(比如同一个命名空间下的Ingress)名字不能重复,不然会报错;
  5. 先plan再apply:每次改代码后,先运行terraform plan,看一下会改哪些资源,确认没问题再apply,避免误操作。

七、总结

从手动敲一堆kubectl命令,到用Terraform写代码编排Ingress和RBAC,其实是把“人的记忆”换成了“代码的记录”,适合现在团队协作和标准化运维的需求。不管你是刚接触K8s的新手,还是有经验的运维,用Terraform来管理这些资源,都能减少很多不必要的麻烦,让集群的配置更稳定、更容易维护。