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定义命名空间与配额
下面给出一个可直接参考的示例。假设我们要为团队 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 则是对单容器的硬边界。
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导致配额被异常占用。
terraform destroy 前,务必确认State中该命名空间下没有需要保留的工作负载,或使用 prevent_destroy = true 对关键命名空间加保护。