4. 用Terraform管理命名空间与基础资源

在Kubernetes中,命名空间(Namespace)是逻辑隔离的基本单元,而ResourceQuota、LimitRange和ServiceAccount则是这个隔离单元内部最基础的治理资源。它们共同决定了“谁能在哪个空间里用多少资源、以什么默认值运行、以什么身份访问API”。如果这些资源靠手工创建,多集群、多环境场景下几乎无法保证一致性;用Terraform声明式交付,才能让命名空间从“临时容器”变成可版本化、可审计、可复现的基础设施单元。

为什么这四类资源要一起交付

单独创建一个Namespace很容易,但一个“可用”的命名空间远不止一个名字。它至少需要回答四个问题:这个空间允许消耗多少CPU和内存(ResourceQuota);如果Pod没有写requests/limits,默认给多少、上限是多少(LimitRange);这个空间里的工作负载用哪个身份访问API Server(ServiceAccount);以及这些对象之间的依赖顺序如何保证。Terraform的依赖图天然适合表达这种“命名空间为父、其余资源为子”的层级关系。

下面这张图展示了本节四类资源在Terraform中的声明式依赖关系与作用位置。Namespace是根节点,ResourceQuota和LimitRange作用于整个命名空间,ServiceAccount则被Pod引用,形成从“空间边界”到“工作负载身份”的完整链路。

Terraform 声明式交付:命名空间基础资源依赖关系 Namespace kubernetes_namespace ResourceQuota requests.cpu / limits.memory pods / services / pvc 数量上限 作用范围:整个 Namespace LimitRange default / defaultRequest min / max 单容器约束 作用范围:Pod / Container ServiceAccount automount_service_account_token image_pull_secret 绑定 作用范围:Pod 身份 工作负载(Deployment / StatefulSet / Job) Pod 引用 ServiceAccount,受 LimitRange 默认值约束,受 ResourceQuota 总量限制 Terraform State 统一记录四类资源 kubectl 验证 describe / get quota

用Terraform定义命名空间与配额

下面给出一个可直接参考的示例。假设我们要为团队 team-alpha 在 dev 环境创建一个命名空间,并施加资源配额与默认限制。示例参数属于教学假设,实际数值需根据集群容量和团队SLA调整。

resource "kubernetes_namespace" "team_alpha" {
  metadata {
    name = "team-alpha-dev"
    labels = {
      environment = "dev"
      owner       = "team-alpha"
    }
  }
}

resource "kubernetes_resource_quota" "team_alpha" {
  metadata {
    name      = "team-alpha-quota"
    namespace = kubernetes_namespace.team_alpha.metadata[0].name
  }
  spec {
    hard = {
      "requests.cpu"    = "8"
      "requests.memory" = "16Gi"
      "limits.cpu"      = "16"
      "limits.memory"   = "32Gi"
      "pods"            = "40"
      "services"        = "10"
      "persistentvolumeclaims" = "8"
    }
  }
}

resource "kubernetes_limit_range" "team_alpha" {
  metadata {
    name      = "team-alpha-limitrange"
    namespace = kubernetes_namespace.team_alpha.metadata[0].name
  }
  spec {
    limit {
      type = "Container"
      default = {
        cpu    = "500m"
        memory = "512Mi"
      }
      default_request = {
        cpu    = "200m"
        memory = "256Mi"
      }
      min = {
        cpu    = "50m"
        memory = "64Mi"
      }
      max = {
        cpu    = "2"
        memory = "4Gi"
      }
    }
  }
}

resource "kubernetes_service_account" "app_runner" {
  metadata {
    name      = "app-runner"
    namespace = kubernetes_namespace.team_alpha.metadata[0].name
  }
  automount_service_account_token = false
}

这段配置中有几个关键点值得注意。第一,kubernetes_resource_quota 和 kubernetes_limit_range 的 namespace 字段都引用了 kubernetes_namespace.team_alpha.metadata[0].name,Terraform会自动推导出依赖关系,保证命名空间先创建。第二,ResourceQuota中的 requests.cpu 与 limits.cpu 是两套独立约束:前者限制所有Pod的requests之和,后者限制limits之和。如果只设了limits.cpu而没设requests.cpu,Kubernetes会拒绝创建未声明requests的Pod。第三,LimitRange的 default 和 default_request 分别对应未写limits和未写requests时的注入值,min/max 则是对单容器的硬边界。

ResourceQuota与LimitRange配合使用时,LimitRange的max值不能超过ResourceQuota的hard上限,否则会出现“单个Pod合法但总量必然超限”的矛盾配置。建议先确定命名空间总量,再反推单容器上限。

ServiceAccount的声明式交付要点

ServiceAccount看似简单,但在Terraform中有两个容易忽略的细节。一是 automount_service_account_token,默认值为true,意味着每个Pod都会自动挂载一个包含API访问令牌的卷。对于不需要访问API Server的工作负载,应显式设为false,减少令牌泄露面。二是如果私有镜像仓库需要拉取凭证,应通过 image_pull_secret 字段绑定,而不是在每个Deployment里重复声明。

resource "kubernetes_service_account" "app_runner" {
  metadata {
    name      = "app-runner"
    namespace = kubernetes_namespace.team_alpha.metadata[0].name
  }
  automount_service_account_token = false
  image_pull_secret {
    name = "harbor-cred"
  }
}

创建完成后,可以用以下命令验证交付结果。判断依据是:命名空间状态为Active,ResourceQuota的Used与Hard均正确显示,LimitRange的默认值已生效,ServiceAccount存在且未自动挂载令牌。

kubectl get ns team-alpha-dev
kubectl describe quota team-alpha-quota -n team-alpha-dev
kubectl describe limitrange team-alpha-limitrange -n team-alpha-dev
kubectl get sa app-runner -n team-alpha-dev -o yaml

常见排障与状态判断

如果 terraform apply 报错提示命名空间不存在,通常是因为ResourceQuota或LimitRange的 namespace 字段写成了硬编码字符串而非引用,导致Terraform无法建立依赖。如果Pod创建被拒绝并提示“must specify limits”,说明LimitRange的default未生效或Pod所在的命名空间与LimitRange不一致。如果ResourceQuota显示 Used 接近 Hard,则新Pod会进入Pending或被拒绝,此时应检查是否有工作负载未设置requests导致配额被异常占用。

删除命名空间会级联删除其下所有资源,包括ResourceQuota、LimitRange和ServiceAccount。在Terraform中执行 terraform destroy 前,务必确认State中该命名空间下没有需要保留的工作负载,或使用 prevent_destroy = true 对关键命名空间加保护。