运维知识
悠悠
2026年10月20日

别把 Namespace 当安全边界:Kubernetes 多团队隔离的生产级落地方案

下午两点,生产集群告警突然密集起来:节点 CPU 超过 90%,核心服务延迟翻倍,部分 Pod 开始重启。排查后发现,问题不是核心服务发布了新版本,而是另一个项目组把批处理 Deployment 扩到了几十个副本。

两个项目明放在不同 Namespace,为什么还能互相影响?

答案很直接:Namespace 主要解决资源命名和管理范围问题,本身不会自动限制 CPU、内存和 Pod 数量,也不会阻断跨 Namespace 网络访问,更不会天然形成完整的权限边界。

我经常用一句话提醒团队:

Namespace 只是隔离的容器,ResourceQuota、LimitRange、NetworkPolicy 和 RBAC 才是装在容器上的护栏。

Namespace 到底隔离了什么

在同一个 Kubernetes 集群中,可以为不同团队、环境或业务创建不同 Namespace:

Kubernetes Cluster
├── team-a
│   ├── Deployment
│   ├── Service
│   ├── ConfigMap
│   └── Secret
├── team-b
│   ├── Deployment
│   ├── Service
│   └── Secret
└── platform-system
    ├── Ingress Controller
    └── Monitoring

不同 Namespace 可以存在同名的 Deployment、Service 和 ConfigMap。例如,team-a 与 team-b 都能创建名为 api-server 的 Service。

访问其他 Namespace 的 Service 时,可以使用完整 DNS 名称:

api-server.team-b.svc.cluster.local

但这种逻辑分组不等于强隔离。

Namespace 默认不会解决下面这些问题:

  • 一个团队创建大量 Pod,耗尽集群 CPU、内存或可调度资源。
  • Pod 直接访问其他 Namespace 中的 Service 或 Pod。
  • 用户通过过大的 RBAC 权限读取其他团队的 Secret。
  • 不受限制的容器占用 CPU,造成同节点业务抖动。
  • 容器逃逸、内核漏洞或节点故障带来的安全风险。
  • 对 PersistentVolume、Node、ClusterRole 等集群级资源进行隔离。

因此,Namespace 适合做管理边界和策略作用范围,但不能被当作虚拟机、独立集群或操作系统级安全边界。安全要求较高的租户,还需要结合独立节点池、污点与容忍、Pod Security、准入策略、运行时沙箱,必要时直接使用独立集群。

动手前,先把集群现状查清楚

不要拿一份网上复制的 YAML 直接往生产集群里执行。先确认 Kubernetes 版本、网络插件和当前策略。

下面都是只读检查:

kubectl version
kubectl api-resources --api-group=networking.k8s.io
kubectl get namespaces --show-labels
kubectl get nodes

重点确认三件事:

  • 客户端与服务端版本是否在团队支持范围内。
  • 集群是否提供 networking.k8s.io/v1 的 NetworkPolicy。
  • Namespace 是否带有预期标签,例如 kubernetes.io/metadata.name=team-a。

API 中存在 NetworkPolicy,并不代表网络插件一定会执行它。还要确认集群使用的 CNI 及其策略能力:

kubectl -n kube-system get daemonset
kubectl -n kube-system get pods --show-labels

常见的 Calico、Cilium 等网络方案可以执行 NetworkPolicy,但具体能力与部署配置有关。如果 CNI 不支持策略,NetworkPolicy 对象可能创建成功,却不会真正阻断流量。

接着检查目标 Namespace 已有的保护措施:

kubectl get resourcequota,limitrange -n team-a
kubectl get networkpolicy -n team-a
kubectl get role,rolebinding -n team-a
kubectl get pods -n team-a --show-labels
kubectl describe resourcequota -n team-a

判断时不要只看“对象是否存在”,还要看内容是否有效:

  • ResourceQuota 的 Used 是否长期接近 Hard。
  • LimitRange 是否设置了合理的默认 request 和 limit。
  • NetworkPolicy 的选择器是否真的匹配业务 Pod。
  • RoleBinding 绑定的是用户、组还是 ServiceAccount。
  • 工作负载标签是否稳定,能否被网络策略准确选中。

网络策略特别依赖标签。策略选择器写错时,可能完全不生效,也可能选中超出预期的 Pod。

把资源护栏写进声明式配置

下面是一份可作为起点的示例。数值只是示例,生产环境应根据容量规划、历史监控和业务峰值调整。

apiVersion: v1
kind: Namespace
metadata:
  name: team-a
  labels:
    owner: team-a
    environment: production
---
apiVersion: v1
kind: ResourceQuota
metadata:
  name: team-a-quota
  namespace: team-a
spec:
  hard:
    requests.cpu: "4"
    requests.memory: 8Gi
    limits.cpu: "8"
    limits.memory: 16Gi
    pods: "50"
    services: "20"
    count/jobs.batch: "20"
---
apiVersion: v1
kind: LimitRange
metadata:
  name: team-a-container-limits
  namespace: team-a
spec:
  limits:
    - type: Container
      defaultRequest:
        cpu: 100m
        memory: 128Mi
      default:
        cpu: 500m
        memory: 512Mi
      min:
        cpu: 20m
        memory: 32Mi
      max:
        cpu: "2"
        memory: 2Gi

ResourceQuota 控制整个 Namespace 可以申请的资源总量和对象数量。LimitRange 则为没有显式填写 request、limit 的新容器补充默认值,并限制单个容器可以申请的范围。

这两个对象要配合使用。如果只配置 ResourceQuota,却要求每个容器填写 request 和 limit,历史工作负载或遗漏字段的新工作负载可能在准入阶段被拒绝。LimitRange 可以降低这种风险,但默认值仍需符合真实负载,不能随意拍脑袋。

还要注意:

  • ResourceQuota 统计的是资源声明和对象数量,不是实时 CPU 使用率。
  • CPU 超过 limit 时通常会受到节流。
  • 内存超过 limit 时,容器可能被 OOM Kill。
  • LimitRange 只在对象创建或更新进入准入流程时发挥作用,不会自动修改已经运行的 Pod。

资源约束解决的是容量争抢,网络隔离需要 NetworkPolicy。

下面先分别建立入站和出站默认拒绝策略,便于出现问题时独立回滚:

apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: default-deny-ingress
  namespace: team-a
spec:
  podSelector: {}
  policyTypes:
    - Ingress
---
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: default-deny-egress
  namespace: team-a
spec:
  podSelector: {}
  policyTypes:
    - Egress

应用后,Namespace 中所有 Pod 会分别进入入站和出站隔离状态。接下来必须按实际依赖放行流量。

例如,允许同一 Namespace 内的 Pod 互相访问:

apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: allow-same-namespace
  namespace: team-a
spec:
  podSelector: {}
  policyTypes:
    - Ingress
  ingress:
    - from:
        - podSelector: {}

允许 Pod 查询 CoreDNS 的示例如下:

apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: allow-cluster-dns
  namespace: team-a
spec:
  podSelector: {}
  policyTypes:
    - Egress
  egress:
    - to:
        - namespaceSelector:
            matchLabels:
              kubernetes.io/metadata.name: kube-system
          podSelector:
            matchLabels:
              k8s-app: kube-dns
      ports:
        - protocol: UDP
          port: 53
        - protocol: TCP
          port: 53

这里的 k8s-app: kube-dns 只是常见标签,执行前必须用下面的命令核对:

kubectl -n kube-system get pods --show-labels
kubectl -n kube-system get service

如果集群启用了 NodeLocal DNSCache,DNS 流量目的地址和 CNI 的处理方式可能不同,应按当前网络插件文档和实际抓取的连接信息调整,不能机械照抄。

NetworkPolicy 是“允许规则叠加”模型。多条策略同时选中一个 Pod 时,允许范围会取并集,不存在简单的“后一条覆盖前一条”。另外,策略通常按源和目的 Pod 的标签判断,经过网关、代理或地址转换后,实际匹配结果可能与直觉不同。

权限隔离则通过 Namespace 级 Role 和 RoleBinding 完成。下面为部署流水线创建一个最小权限示例:

apiVersion: v1
kind: ServiceAccount
metadata:
  name: deployer
  namespace: team-a
---
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
  name: deployment-maintainer
  namespace: team-a
rules:
  - apiGroups: ["apps"]
    resources: ["deployments"]
    verbs: ["get", "list", "watch", "create", "update", "patch"]
  - apiGroups: [""]
    resources: ["pods", "pods/log", "events"]
    verbs: ["get", "list", "watch"]
---
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
  name: deployer-binding
  namespace: team-a
subjects:
  - kind: ServiceAccount
    name: deployer
    namespace: team-a
roleRef:
  apiGroup: rbac.authorization.k8s.io
  kind: Role
  name: deployment-maintainer

这里没有授予删除 Deployment、读取 Secret、修改 RoleBinding 等权限。流水线需要什么就增加什么,不要为了省事直接绑定 cluster-admin。

管理员可以在具备模拟身份权限的前提下验证授权:

kubectl auth can-i patch deployments.apps \
  -n team-a \
  --as=system:serviceaccount:team-a:deployer

kubectl auth can-i get secrets \
  -n team-a \
  --as=system:serviceaccount:team-a:deployer

预期结果分别为 yes 和 no。如果当前操作者没有 impersonate 权限,--as 请求会被拒绝,应由集群管理员执行验证,不要临时扩大自己的权限。

上线不能一把梭:先验证,再灰度

网络默认拒绝属于高风险变更。遗漏 DNS、数据库、消息队列、监控、日志、镜像拉取或第三方接口中的任何一项,都可能让业务立即异常。

建议先把所有清单纳入 Git 评审,并做服务端试运行:

kubectl apply --dry-run=server -f team-a-guardrails.yaml
kubectl diff -f team-a-guardrails.yaml

服务端 dry-run 不会持久化对象,但可以发现字段错误、部分准入规则冲突和权限问题。kubectl diff 出现差异时可能返回退出码 1,这代表“存在差异”,不等同于命令执行异常。

正式实施可以按下面的顺序推进:

  1. 备份现有清单和关键监控基线。
  2. 先在测试 Namespace 验证 ResourceQuota、LimitRange 和 RBAC。
  3. 梳理真实网络依赖,创建明确的允许规则。
  4. 只选择一小组带稳定标签的 Pod 验证 NetworkPolicy。
  5. 观察请求成功率、P95/P99 延迟、DNS 错误、连接超时和重启次数。
  6. 验证通过后,再扩大策略选择范围并启用默认拒绝。

只读采证可以这样保存:

kubectl get resourcequota,limitrange -n team-a -o yaml
kubectl get networkpolicy -n team-a -o yaml
kubectl get role,rolebinding -n team-a -o yaml
kubectl get events -n team-a --sort-by=.lastTimestamp

真正应用配置属于有状态变更:

kubectl apply -f team-a-guardrails.yaml
kubectl get resourcequota,limitrange,networkpolicy -n team-a
kubectl describe resourcequota -n team-a

变更后还要从受策略保护的 Pod 验证 DNS、上游服务和下游依赖,而不能只看 YAML 已经创建成功。对关键调用执行健康检查,同时观察应用日志与网络插件的拒绝日志。

如果出站默认拒绝导致生产依赖中断,应优先回滚新增策略:

kubectl delete networkpolicy default-deny-egress -n team-a

这条命令会立即放宽出站访问,存在安全暴露风险,只适合经过授权的紧急止损。恢复业务后应根据证据补齐精确放行规则,再重新启用默认拒绝。

RBAC、Quota 和 LimitRange 则应通过版本库中的上一版清单回滚:

kubectl apply -f previous/team-a-guardrails.yaml

删除 LimitRange 不会还原已经创建的 Pod;删除 ResourceQuota 也不会自动重建此前被拒绝的对象。回滚后必须重新验证工作负载和权限状态。

一个脱敏场景:有 Namespace,为什么还会拖垮邻居

下面用一个脱敏后的典型场景说明,时间、名称和数值均为示例数据。

14:02,监控发现三个节点 CPU 使用率从约 55% 上升到 92%,订单接口 P99 延迟由 180ms 增加到 1.4s。订单服务没有发布记录,但部分 Pod 出现 CPU throttling 和探针超时。

值班人员先执行只读检查:

kubectl top nodes
kubectl top pods -A --sort-by=cpu
kubectl get events -A --sort-by=.lastTimestamp
kubectl get deployments -A

结果显示,reporting Namespace 中的 report-worker Deployment 在几分钟前从 4 个副本扩到了 80 个副本,每个 Pod 都没有声明 CPU request 和 limit。

继续检查:

kubectl get deployment report-worker -n reporting -o yaml
kubectl get resourcequota,limitrange -n reporting
kubectl get pods -n reporting -o wide

关键证据形成了一条完整链路:

  • 异常时间与 report-worker 扩容时间一致。
  • 新增 Pod 集中分布在发生抖动的节点。
  • Namespace 中不存在 ResourceQuota 和 LimitRange。
  • Pod 未声明资源 request,调度器无法按真实需求预留容量。
  • Pod 未设置 CPU limit,运行时可以持续争抢节点 CPU。
  • 订单服务自身没有变更,数据库和入口流量也没有同量级异常。

团队记录原副本数和变更来源后,经业务确认把报表任务临时降到 4 个副本:

kubectl scale deployment report-worker \
  -n reporting \
  --replicas=4

这是有状态变更,风险是报表处理积压。执行后需要同时观察节点 CPU、订单延迟、报表队列长度和失败任务数。若指标没有恢复,就说明初判不完整,应停止继续修改并回到事件、节点和依赖链路重新取证。

本次示例中,节点 CPU 在约 8 分钟后回落到 60% 左右,订单 P99 恢复到 200ms 以内,说明扩容与资源争抢之间存在充分的现场证据。

最终修复不是“禁止 reporting 团队扩容”,而是补齐以下措施:

  • 为 Namespace 设置总量配额和 Pod 数量上限。
  • 通过 LimitRange 给新容器提供保守的默认 request、limit。
  • 为核心 Deployment 显式填写经过压测验证的资源声明。
  • 为自动扩缩容设置合理的最小、最大副本数。
  • 对 Namespace 配额使用率、Pod 数量和节点资源水位告警。
  • 对高消耗批处理使用独立节点池或调度约束,降低同节点干扰。

这次故障的根因并不是 Namespace 失效,而是团队把 Namespace 当成了完整资源隔离,却没有配置真正执行资源约束的策略。

这些常见误区,比没配策略更危险

“分了 Namespace,网络自然不通。”
默认情况下通常仍然可以跨 Namespace 通信。必须由支持策略的 CNI 执行 NetworkPolicy,并通过实际连接测试验证。

“创建一条 default-deny 就安全了。”
如果允许规则没有覆盖 DNS、网关和必要依赖,结果可能是业务中断;如果 CNI 不支持,结果又可能是什么都没拦住。

“配置 ResourceQuota 就不会抢资源。”
Quota 控制声明总量,不能代替容器级 limit、节点容量规划和调度隔离。实时 CPU 争抢仍需要合理的 request、limit 和节点治理。

“RoleBinding 只能绑定 Role,所以很安全。”
RoleBinding 也可以引用 ClusterRole,只是权限作用于该 RoleBinding 所在的 Namespace。若引用的 ClusterRole 包含读取 Secret 等能力,仍可能造成敏感信息泄露。

“LimitRange 上线后,旧 Pod 会自动补齐限制。”
不会。既有 Pod 需要通过受控滚动发布重新创建,变更前应检查资源余量和 PodDisruptionBudget,避免同时重建造成容量不足。

“多个 NetworkPolicy 会互相覆盖。”
不会简单覆盖。对同一方向的允许规则通常会叠加,排查时必须查看所有选中该 Pod 的策略。

上生产前,收藏这份检查清单

  • 确认 Namespace 边界:列出哪些资源是 Namespace 级、哪些是集群级,避免把 Node、PV、ClusterRole 当成已隔离资源。
  • 核对 CNI 能力:NetworkPolicy 创建成功不代表流量已经受控,必须做实际连通性测试。
  • 设置 ResourceQuota:限制资源声明和对象数量,避免单个团队无限扩容。
  • 设置 LimitRange:为遗漏资源字段的新容器补充默认值,否则配额可能导致发布被拒绝。
  • 显式填写关键服务资源:默认值只适合兜底,核心服务应使用压测和历史数据确定 request、limit。
  • 先放行依赖再默认拒绝:直接应用全量 default-deny,最容易造成 DNS和下游连接中断。
  • 检查所有匹配策略:NetworkPolicy 允许规则会叠加,只看其中一条可能得出错误结论。
  • 坚持 RBAC 最小权限:不要给流水线和普通开发账号绑定 cluster-admin,尤其要控制 Secret 与权限对象。
  • 准备分项回滚:入站、出站、配额和权限分别管理,故障时才能只回滚受影响部分。
  • 监控拒绝与配额水位:策略没有监控就难以判断是业务故障、容量不足还是访问被拒绝。
  • 高安全租户使用更强隔离:面对不可信工作负载时,应考虑独立节点、运行时沙箱或独立集群。

今天就可以做一个动作:选择一个非核心 Namespace,依次检查 Quota、LimitRange、NetworkPolicy 和 RoleBinding。如果四项都是空的,这个 Namespace 现在承担的更多是“文件夹”作用,而不是生产隔离。

Namespace 很重要,但它只是起点。真正可靠的多团队治理,来自资源、网络、权限、调度和准入策略共同形成的防线。

如果这篇文章对你有帮助,欢迎关注、点赞、在看,并转发给正在建设 Kubernetes 多租户规范的同事。

公众号:耕云躬行录

个人博客:躬行笔记

文章目录

博主介绍

热爱技术的云计算运维工程师,Python全栈工程师,分享开发经验与生活感悟。
欢迎关注我的微信公众号@运维躬行录,领取海量学习资料

微信二维码