1. 为什么 Namespaces 是 K8s 里最被低估的隔离手段
很多人刚接触 Kubernetes 的时候,对 Namespaces 的理解就停留在“建个命名空间,把 Pod 丢进去”这个层面。我一开始也这样,觉得这东西不就是个文件夹嘛,把资源分分类而已。直到有一次在测试环境里,一个同事误删了某个 Namespace 下的 Service,结果整个测试链路全挂了,而生产环境因为 Namespace 隔离得当,毫发无损。那次之后我才真正意识到,Namespaces 远不止“分类”这么简单,它是 K8s 里做逻辑隔离最轻量、最灵活、也最容易被忽视的一把利器。
这篇文章我想跟你聊的,就是怎么在实际项目里把 Namespaces 用好、用对、用出价值。不管你是刚上手 K8s 的新手,还是已经跑过几个集群的老手,我相信下面这些从实战里摸出来的经验,多少能帮你少踩几个坑。我会从设计思路讲到具体操作,从资源配额讲到网络策略,从常见问题讲到排查技巧,尽量把每个环节的“为什么”和“怎么做”都说透。
先明确一下:Namespaces 解决的核心问题是在同一个物理集群内,为不同的团队、项目、环境提供逻辑上的资源分组和隔离。它不是网络隔离,也不是安全隔离的银弹,但它是一切上层隔离策略的基础。你后面要做的 ResourceQuota、LimitRange、NetworkPolicy、RBAC,几乎都要挂在 Namespace 这个维度上。所以,把 Namespaces 设计好,后面的路会顺很多。
2. 集群 Namespace 的整体设计与划分思路
2.1 按团队划分还是按环境划分
这是我在实际项目里被问得最多的一个问题。我的经验是:优先按环境划分,再按团队细分。为什么?因为环境之间的隔离需求通常比团队之间的隔离需求更刚性。开发环境的东西绝对不能跑到生产环境去,这是底线。而团队之间的隔离,很多时候可以通过 RBAC 和 ResourceQuota 在同一个 Namespace 内做更细粒度的控制。
具体来说,我一般会这样设计:
dev:开发环境,资源配额宽松,允许频繁变更staging:预发布环境,配置尽量贴近生产,但资源规模可以缩小prod:生产环境,资源配额严格,变更需要审批monitoring:监控组件专用,独立出来避免被业务资源挤占ingress-nginx:入口控制器专用,同样独立kube-system:系统组件,绝对不要动
如果团队规模比较大,可以在每个环境下面再按团队细分,比如dev-teamA、dev-teamB、prod-teamA这样。但我不建议一上来就分得太细,Namespace 太多管理成本会急剧上升。我见过一个集群里有一百多个 Namespace,最后连谁负责哪个都搞不清楚了。
2.2 哪些资源是 Namespace 级别的,哪些不是
这个问题非常关键,搞错了会导致很多莫名其妙的错误。我整理了一个表格,你可以直接对照着看:
| 资源类型 | 是否属于 Namespace | 说明 |
|---|---|---|
| Pod | 是 | 最常见的 Namespace 级资源 |
| Service | 是 | 服务发现的范围限定在 Namespace 内 |
| ConfigMap | 是 | 配置隔离的重要载体 |
| Secret | 是 | 敏感信息按 Namespace 隔离 |
| PVC | 是 | 存储卷声明是 Namespace 级的 |
| Deployment | 是 | 工作负载控制器 |
| StatefulSet | 是 | 有状态工作负载 |
| DaemonSet | 是 | 但 Pod 会调度到所有节点 |
| Node | 否 | 集群级资源 |
| PV | 否 | 存储卷是集群级的 |
| StorageClass | 否 | 集群级资源 |
| ClusterRole | 否 | 集群级 RBAC |
| Namespace 本身 | 否 | 它是集群级资源 |
注意:DaemonSet 虽然定义在某个 Namespace 里,但它创建的 Pod 会跑在所有节点上,所以它并不受 Namespace 的资源配额限制。这一点很多人会搞混。
2.3 命名规范与标签策略
命名规范这件事,看起来小,实际上影响很大。我建议采用环境-团队-用途的三段式命名,比如prod-payment-api、dev-search-cache。全小写,用连字符分隔,不要用下划线或大写字母,因为 K8s 的 DNS 命名规则对大小写敏感,混用容易出问题。
标签策略同样重要。我一般会给每个 Namespace 打上这几个标签:
apiVersion: v1 kind: Namespace metadata: name: prod-payment-api labels: environment: prod team: payment cost-center: "12345" managed-by: platform-team这些标签后面可以用来做成本分摊、策略匹配、监控分组,非常有用。特别是cost-center这个标签,财务那边做成本核算的时候会感谢你的。
3. 核心隔离机制与实操配置详解
3.1 ResourceQuota:给 Namespace 戴上紧箍咒
ResourceQuota 是我认为 Namespace 体系里最重要的一个东西。没有它,一个 Namespace 里的 Pod 可以把整个集群的资源吃光,其他 Namespace 全部遭殃。我亲身经历过一次,某个开发在 dev 环境里跑了一个死循环的 Job,创建了几百个 Pod,把节点资源全部占满,导致 prod 环境的 Pod 被驱逐。从那以后,我给每个 Namespace 都配了 ResourceQuota。
一个典型的 ResourceQuota 配置长这样:
apiVersion: v1 kind: ResourceQuota metadata: name: prod-quota namespace: prod-payment-api spec: hard: requests.cpu: "20" requests.memory: 40Gi limits.cpu: "40" limits.memory: 80Gi pods: "100" services: "20" persistentvolumeclaims: "10" requests.storage: 500Gi这里有几个参数需要解释一下。requests.cpu和requests.memory是所有 Pod 的 requests 总和的上限,limits.cpu和limits.memory是 limits 总和的上限。为什么两个都要设?因为 K8s 调度器看的是 requests,而实际运行时受 limits 约束。只设 requests 不设 limits,可能导致节点过载;只设 limits 不设 requests,调度器可能把 Pod 调度到资源不足的节点上。
pods这个参数限制的是 Namespace 内 Pod 的总数。这个值不要设得太大,否则一个 Namespace 可以创建大量小 Pod,把节点的 PID 或 IP 资源耗尽。我一般会根据节点规模和业务需求来估算,比如 3 个节点、每个节点最多跑 110 个 Pod,那总容量大概 330 个,给单个 Namespace 设 100 个已经算宽松了。
实操心得:ResourceQuota 设置之后,如果 Namespace 内已有的资源已经超过了配额,新的资源创建会被拒绝,但已有的资源不会被删除。所以最好在 Namespace 创建初期就把配额设好,避免后期调整时出现“存量超限”的尴尬局面。
3.2 LimitRange:给单个 Pod 设默认值和上下限
ResourceQuota 管的是总量,LimitRange 管的是单个 Pod 或 Container 的粒度。这两个配合使用效果最好。LimitRange 可以做三件事:设置默认的 requests 和 limits、设置最小和最大的 requests/limits、限制 PVC 的大小范围。
apiVersion: v1 kind: LimitRange metadata: name: prod-limits namespace: prod-payment-api spec: limits: - type: Container default: cpu: 500m memory: 512Mi defaultRequest: cpu: 200m memory: 256Mi max: cpu: "4" memory: 8Gi min: cpu: 50m memory: 64Mi - type: PersistentVolumeClaim max: storage: 100Gi min: storage: 1Gidefault和defaultRequest的作用是:如果用户在创建 Pod 时没有指定 requests 和 limits,就自动使用这些默认值。这个功能非常实用,因为很多开发人员写 YAML 的时候会忘记写 resources 字段,有了 LimitRange,至少不会出现“裸奔”的 Pod。
max和min则是硬性约束。比如max.cpu: "4"意味着任何单个 Container 的 CPU limit 不能超过 4 核。这个限制可以防止某个 Pod 申请过大的资源,导致调度失败或者影响其他 Pod。
注意:LimitRange 的
max和 ResourceQuota 的hard是两套独立的约束。LimitRange 管单个 Pod,ResourceQuota 管总量。两者都要配,才能形成完整的资源管控体系。
3.3 NetworkPolicy:Namespace 间的网络隔离
默认情况下,K8s 集群内所有 Pod 之间是可以互相通信的,不管它们在哪个 Namespace。这在多租户场景下是个安全隐患。NetworkPolicy 就是用来解决这个问题的。
不过要注意,NetworkPolicy 需要 CNI 插件支持。Calico、Cilium、Weave Net 这些主流 CNI 都支持,但 Flannel 默认不支持。如果你用的是 Flannel,需要额外配置才能启用 NetworkPolicy。
一个典型的场景是:只允许prod-payment-api这个 Namespace 的 Pod 接收来自ingress-nginxNamespace 的流量,拒绝其他所有入站流量。
apiVersion: networking.k8s.io/v1 kind: NetworkPolicy metadata: name: allow-ingress-only namespace: prod-payment-api spec: podSelector: {} policyTypes: - Ingress ingress: - from: - namespaceSelector: matchLabels: name: ingress-nginx ports: - protocol: TCP port: 8080这个配置的意思是:选中prod-payment-api下所有 Pod,只允许来自带有name: ingress-nginx标签的 Namespace 的流量进入,且只允许访问 8080 端口。其他所有入站流量一律拒绝。
实操心得:NetworkPolicy 是白名单机制,一旦某个 Pod 被任何 NetworkPolicy 选中,它就会拒绝所有未被明确允许的流量。所以配置的时候要特别小心,别把自己需要用的流量也给拒了。我建议先在测试环境验证,确认没问题再上生产。
3.4 RBAC:把权限锁在 Namespace 里
RBAC 和 Namespace 的结合是权限隔离的关键。通过 Role 和 RoleBinding,你可以让某个用户或 ServiceAccount 只在特定的 Namespace 里有权限,出了这个 Namespace 就什么也干不了。
apiVersion: rbac.authorization.k8s.io/v1 kind: Role metadata: namespace: dev-teamA name: developer rules: - apiGroups: ["", "apps", "batch"] resources: ["pods", "deployments", "services", "jobs"] verbs: ["get", "list", "watch", "create", "update", "patch", "delete"] --- apiVersion: rbac.authorization.k8s.io/v1 kind: RoleBinding metadata: name: developer-binding namespace: dev-teamA subjects: - kind: User name: dev-user-a apiGroup: rbac.authorization.k8s.io roleRef: kind: Role name: developer apiGroup: rbac.authorization.k8s.io这个配置让dev-user-a这个用户在dev-teamA这个 Namespace 里拥有对 Pod、Deployment、Service、Job 的完整操作权限,但在其他 Namespace 里没有任何权限。
注意:Role 和 RoleBinding 是 Namespace 级的,ClusterRole 和 ClusterRoleBinding 是集群级的。如果你想让某个用户在所有 Namespace 里都有只读权限,可以用 ClusterRole 加 RoleBinding 的组合,这样权限只在绑定的 Namespace 生效。
4. 完整实操流程:从零搭建一个多租户 Namespace 体系
4.1 创建 Namespace 并打标签
第一步当然是创建 Namespace。我习惯用 YAML 文件而不是kubectl create namespace命令,因为 YAML 可以版本化管理,方便追溯和回滚。
apiVersion: v1 kind: Namespace metadata: name: prod-payment-api labels: environment: prod team: payment cost-center: "12345"创建命令:
kubectl apply -f namespace.yaml创建完之后验证一下:
kubectl get namespace prod-payment-api --show-labels4.2 配置 ResourceQuota 和 LimitRange
接着把前面提到的 ResourceQuota 和 LimitRange 都应用上去:
kubectl apply -f resourcequota.yaml kubectl apply -f limitrange.yaml验证配置是否生效:
kubectl describe resourcequota prod-quota -n prod-payment-api kubectl describe limitrange prod-limits -n prod-payment-api这里有个小技巧:kubectl describe resourcequota会显示当前已使用的资源和剩余配额,非常直观。我一般会把这个命令加到日常巡检脚本里,定期检查有没有 Namespace 快超限了。
4.3 配置网络策略
网络策略的配置需要根据实际业务流量来定。我一般会先梳理清楚哪些 Namespace 需要互相通信,然后按最小权限原则配置。
kubectl apply -f networkpolicy.yaml验证网络策略是否生效,可以用一个临时 Pod 来测试:
kubectl run test-pod --image=busybox -n default -- sleep 3600 kubectl exec -it test-pod -n default -- wget -qO- --timeout=3 http://payment-service.prod-payment-api:8080如果网络策略配置正确,这个请求应该超时或被拒绝。如果返回了内容,说明策略没生效,需要检查 CNI 插件是否支持 NetworkPolicy。
4.4 配置 RBAC 权限
RBAC 的配置要根据团队的组织结构来。我一般会为每个团队创建一个 Role,然后绑定到对应的用户或 ServiceAccount。
kubectl apply -f role.yaml kubectl apply -f rolebinding.yaml验证权限:
kubectl auth can-i create pods -n prod-payment-api --as=dev-user-a kubectl auth can-i create pods -n dev-teamA --as=dev-user-a第一条命令应该返回yes,第二条应该返回no。如果结果不符合预期,检查 RoleBinding 的 subjects 和 roleRef 是否正确。
4.5 设置默认 Namespace 和上下文切换
日常操作中频繁指定-n参数很麻烦,可以用kubectl config set-context来设置默认 Namespace:
kubectl config set-context --current --namespace=prod-payment-api这样后续的kubectl命令就不需要每次都加-n了。不过要小心,切换上下文之后容易忘记当前在哪个 Namespace,建议在 shell 提示符里显示当前 Namespace,或者用kubens这类工具来管理。
实操心得:我强烈建议安装
kubectx和kubens这两个工具。kubectx用来快速切换集群,kubens用来快速切换 Namespace。它们能极大提升日常操作效率,减少因为搞错 Namespace 而导致的误操作。
5. 常见问题与排查技巧实录
5.1 Pod 创建失败提示“exceeded quota”
这是最常见的 ResourceQuota 相关问题。排查步骤:
- 查看 ResourceQuota 的当前使用情况:
kubectl describe resourcequota -n <namespace> - 检查 Pod 的 requests 和 limits 是否超过了剩余配额
- 如果确实超了,要么调整配额,要么优化 Pod 的资源申请
避坑技巧:ResourceQuota 的
hard值调整之后,不会自动重新计算已有资源。如果之前已经超限,调整配额后需要等一段时间或者手动触发重新计算。我一般会先删除一些不必要的资源,再调整配额。
5.2 Pod 创建失败提示“no limit range”
这个错误通常是因为 LimitRange 配置了max但 Pod 没有指定 limits,或者 Pod 的 limits 超过了max。解决方法:
- 检查 LimitRange 配置:
kubectl describe limitrange -n <namespace> - 确保 Pod 的 resources 字段符合 LimitRange 的要求
- 如果 LimitRange 配置了
default,Pod 不指定 resources 也可以,但建议还是显式指定
5.3 跨 Namespace 服务访问不通
Service 的 DNS 名称是<service>.<namespace>.svc.cluster.local,跨 Namespace 访问必须带上 Namespace 名称。比如从dev-teamA访问prod-payment-api的payment-service,需要用payment-service.prod-payment-api.svc.cluster.local。
如果 DNS 解析没问题但连接不通,检查 NetworkPolicy 是否阻止了流量。可以用kubectl describe networkpolicy -n <namespace>查看策略详情。
5.4 删除 Namespace 卡在 Terminating 状态
这是 K8s 里非常经典的问题。Namespace 删除时会清理里面的所有资源,如果有资源无法删除(比如 Finalizer 没处理完),Namespace 就会一直卡在 Terminating。
排查方法:
kubectl get namespace <name> -o json | jq '.status.conditions'查看是哪些资源卡住了:
kubectl api-resources --verbs=list --namespaced -o name | xargs -n 1 kubectl get --show-kind --ignore-not-found -n <name>如果确认资源已经不需要了,可以手动移除 Finalizer:
kubectl get namespace <name> -o json | jq '.spec.finalizers = []' | kubectl replace --raw /api/v1/namespaces/<name>/finalize -f -注意:手动移除 Finalizer 是有风险的操作,可能导致资源泄漏。只有在确认资源已经清理干净或者不再需要的情况下才这么做。
5.5 常见问题速查表
| 问题现象 | 可能原因 | 排查命令 | 解决方法 |
|---|---|---|---|
| Pod 创建失败,提示 exceeded quota | ResourceQuota 超限 | kubectl describe resourcequota -n <ns> | 调整配额或清理资源 |
| Pod 创建失败,提示 no limit range | LimitRange 约束不满足 | kubectl describe limitrange -n <ns> | 调整 Pod resources 或 LimitRange |
| 跨 Namespace 访问不通 | DNS 名称不对或 NetworkPolicy 阻止 | kubectl exec测试连通性 | 使用完整 DNS 名称或调整 NetworkPolicy |
| Namespace 卡在 Terminating | 资源 Finalizer 未处理 | kubectl get namespace -o json | 清理资源或手动移除 Finalizer |
| Service 无法解析 | CoreDNS 问题或 Service 不存在 | kubectl get svc -n <ns> | 检查 Service 和 CoreDNS |
| RBAC 权限不足 | RoleBinding 配置错误 | kubectl auth can-i | 检查 Role 和 RoleBinding |
6. 一些容易被忽视的细节和进阶用法
6.1 Namespace 的自动清理
K8s 从 1.20 版本开始支持自动清理已删除 Namespace 中的资源。这个功能默认是关闭的,需要在 kube-controller-manager 中启用NamespaceDefaultDeletionPolicy特性门控。启用后,Namespace 删除时会自动清理里面的资源,不需要手动处理 Finalizer。
不过这个功能在生产环境要慎用,因为自动清理可能会误删一些还在使用的资源。我一般只在测试环境启用,生产环境还是手动确认后再删除。
6.2 用 Namespace 做成本分摊
结合前面提到的cost-center标签,可以用 Prometheus 和 Grafana 做成本分摊看板。具体做法是:
- 用 kube-state-metrics 采集 Namespace 级别的资源使用数据
- 在 Prometheus 里按 Namespace 聚合 CPU 和内存使用量
- 在 Grafana 里按
cost-center标签分组展示
这样每个团队用了多少资源、该分摊多少成本,一目了然。我在上一家公司就是这么做的,财务那边非常满意。
6.3 Namespace 与 GitOps 的结合
如果你在用 ArgoCD 或 Flux 这类 GitOps 工具,可以把 Namespace 的定义也纳入 Git 管理。每个 Namespace 对应一个目录,里面放 Namespace 本身、ResourceQuota、LimitRange、NetworkPolicy、RBAC 等配置。这样 Namespace 的创建和变更都有版本记录,方便审计和回滚。
namespaces/ ├── prod-payment-api/ │ ├── namespace.yaml │ ├── resourcequota.yaml │ ├── limitrange.yaml │ ├── networkpolicy.yaml │ └── rbac.yaml ├── dev-teamA/ │ ├── namespace.yaml │ ├── resourcequota.yaml │ └── limitrange.yaml └── monitoring/ ├── namespace.yaml └── resourcequota.yaml这种结构清晰、可维护,新人接手也能快速理解。
6.4 监控 Namespace 级别的资源使用
除了 ResourceQuota 的硬限制,我还建议配置监控告警。当某个 Namespace 的资源使用率达到配额的 80% 时,提前告警,避免真的超限了才发现。
Prometheus 告警规则示例:
groups: - name: namespace-quota rules: - alert: NamespaceQuotaNearLimit expr: | kube_resourcequota{type="used"} / kube_resourcequota{type="hard"} > 0.8 for: 5m labels: severity: warning annotations: summary: "Namespace {{ $labels.namespace }} 的 {{ $labels.resource }} 使用率超过 80%"这个告警规则会监控所有 Namespace 的所有资源配额,使用率超过 80% 持续 5 分钟就触发告警。非常实用。
6.5 关于默认 Namespace 的那些坑
default这个 Namespace 是 K8s 自带的,很多人图省事就把东西往里面丢。我强烈建议不要这么做。原因有三:
第一,defaultNamespace 没有 ResourceQuota 和 LimitRange,资源不受控。第二,defaultNamespace 的 RBAC 权限通常比较宽松,容易出安全问题。第三,所有东西混在一起,排查问题的时候非常痛苦。
我的做法是:把defaultNamespace 当成一个“禁止使用”的 Namespace,通过 ResourceQuota 把它限制到最小,比如只允许创建 1 个 Pod、100m CPU、128Mi 内存。这样即使有人不小心往里面创建资源,也造不成什么影响。
实操心得:我见过一个集群,
defaultNamespace 里跑了上百个 Pod,包括一些关键的业务服务。后来做安全审计的时候发现,这些 Pod 的 ServiceAccount 权限过大,存在严重的安全隐患。清理的时候花了整整两天时间。所以,从一开始就养成好习惯,别用default。
7. 我个人在实际操作中的几点体会
Namespace 这个东西,说简单也简单,说复杂也复杂。简单在于它的概念很直观,就是逻辑分组;复杂在于它牵扯到资源管控、网络隔离、权限管理、成本分摊等方方面面。我做了这么多集群,最大的体会就是:Namespace 的设计一定要在集群搭建初期就做好,后期再改成本极高。
我踩过最大的一个坑,就是早期没有给 Namespace 配 ResourceQuota,结果一个测试环境的 Namespace 把整个集群的资源吃光了,导致生产环境受影响。从那以后,我给每个 Namespace 都配了配额,而且把“创建 Namespace 必须同时创建 ResourceQuota 和 LimitRange”写进了团队的操作规范里。
另一个体会是,Namespace 的命名和标签一定要规范。我见过太多集群,Namespace 名字五花八门,有的用下划线,有的用大写,有的用中文,最后做自动化的时候各种兼容问题。统一用环境-团队-用途的小写连字符格式,能省掉很多麻烦。
最后再分享一个小技巧:如果你经常需要在多个 Namespace 之间切换,可以在 shell 里定义一个函数,把当前 Namespace 显示在提示符里。这样你随时都知道自己在哪个 Namespace,避免误操作。我用的是 zsh,在.zshrc里加了这么一段:
kube_ns() { local ns=$(kubectl config view --minify -o jsonpath='{..namespace}' 2>/dev/null) echo "${ns:-default}" } PROMPT='%F{blue}[$(kube_ns)]%f '$PROMPT这样每次打开终端,提示符里都会显示当前 Namespace,非常直观。如果你用的是 bash,原理类似,改一下PS1就行。
这个内容后续还可以这样扩展:结合 Open Policy Agent 做 Namespace 级别的策略校验,确保所有创建的资源都符合团队的规范;或者结合 Kyverno 做自动化的 Namespace 配置注入,新建 Namespace 时自动带上 ResourceQuota、LimitRange 和 NetworkPolicy。这些进阶玩法,等有机会再单独聊。