3. 多租户网络拓扑设计:租户命名空间规划、默认拒绝策略、允许特定流量模型

多租户网络拓扑设计的核心目标是实现租户间的强隔离与租户内的最小权限通信。本章将围绕三个关键步骤展开:命名空间规划、默认拒绝策略部署、以及精细化允许规则的编写。错误的规划会导致后续故障排查成本急剧上升,因此设计阶段必须严谨。

3.1 租户命名空间规划原则

命名空间(Namespace)是 Kubernetes 中实现多租户隔离的基本边界。每个租户应独占一个或多个命名空间,并通过 NetworkPolicy 限制跨命名空间流量。

规划维度 推荐做法 常见错误
命名空间命名 使用 tenant-{name}-{env} 格式,如 tenant-alpha-prod 使用无意义缩写或数字 ID,难以识别归属
标签体系 强制添加 tenant: alpha、environment: prod 标签 标签缺失或混乱,导致 NetworkPolicy 选择器失效
资源配额 为每个租户命名空间设置 ResourceQuota 和 LimitRange 未设置配额,单个租户耗尽集群资源
共享服务 将 DNS、监控、日志等共享服务部署在独立 kube-system 或 infra 命名空间 共享服务与租户服务混部,增加策略复杂度
最佳实践:在集群初始化阶段,使用 kubectl label namespace 为所有租户命名空间打上统一的租户标签。这为后续基于标签选择器的 NetworkPolicy 提供了可靠的匹配基础。

3.2 默认拒绝策略:零信任的起点

在创建任何允许规则之前,必须为每个租户命名空间部署一条默认拒绝所有入站流量的 NetworkPolicy。这是实现零信任网络模型的基础,确保所有未显式允许的流量都被阻断。

apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: default-deny-ingress
  namespace: tenant-alpha-prod
spec:
  podSelector: {}
  policyTypes:
  - Ingress
  # 没有 ingress 规则,拒绝所有入站流量

如果需要同时拒绝出站流量(更严格的隔离),可以额外部署一条默认拒绝出站策略:

apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: default-deny-egress
  namespace: tenant-alpha-prod
spec:
  podSelector: {}
  policyTypes:
  - Egress
  # 没有 egress 规则,拒绝所有出站流量
注意:默认拒绝出站策略会阻止 Pod 访问集群 DNS(通常位于 kube-system 命名空间的 coredns 服务)。如果部署了该策略,必须显式添加一条允许访问 DNS 的 egress 规则,否则 Pod 将无法解析服务名。

3.3 允许特定流量模型

在默认拒绝的基础上,通过精细化的 NetworkPolicy 规则实现以下三种典型流量模型:

  • 租户内部通信:允许同一租户内不同应用组件的 Pod 互相访问。
  • 跨租户服务调用:仅允许特定租户的特定服务访问另一个租户的特定端口。
  • 基础设施访问:允许租户 Pod 访问集群 DNS、监控 Agent 等共享服务。

3.3.1 租户内部通信规则

假设租户 Alpha 在 tenant-alpha-prod 命名空间中运行前端(frontend)和后端(backend)服务。前端需要访问后端的 8080 端口。

apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: allow-frontend-to-backend
  namespace: tenant-alpha-prod
spec:
  podSelector:
    matchLabels:
      app: backend
  policyTypes:
  - Ingress
  ingress:
  - from:
    - podSelector:
        matchLabels:
          app: frontend
    ports:
    - protocol: TCP
      port: 8080

3.3.2 跨租户服务调用规则

当租户 Beta 的支付服务需要调用租户 Alpha 的订单服务时,需要在 Alpha 的命名空间中创建一条允许来自 Beta 命名空间特定 Pod 的入站规则。

apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: allow-beta-payment-to-alpha-order
  namespace: tenant-alpha-prod
spec:
  podSelector:
    matchLabels:
      app: order-service
  policyTypes:
  - Ingress
  ingress:
  - from:
    - namespaceSelector:
        matchLabels:
          tenant: beta
      podSelector:
        matchLabels:
          app: payment-service
    ports:
    - protocol: TCP
      port: 50051
关键点:跨命名空间规则必须同时使用 namespaceSelector 和 podSelector。仅使用 namespaceSelector 会允许该命名空间内所有 Pod 访问,违背最小权限原则。

3.4 多租户网络拓扑结构图

下图展示了一个典型的两租户网络拓扑,包含默认拒绝策略和精细化允许规则。

多租户网络拓扑与 NetworkPolicy 规则 租户 Alpha 命名空间 (tenant-alpha-prod) 默认拒绝所有入站流量 (default-deny-ingress) 前端 Pod app: frontend 后端 Pod app: backend TCP 8080 租户 Beta 命名空间 (tenant-beta-prod) 默认拒绝所有入站流量 (default-deny-ingress) 支付 Pod app: payment-service 订单 Pod app: order-service TCP 50051 基础设施命名空间 (kube-system / infra) CoreDNS Prometheus DNS 查询 (UDP 53) 图例: 默认拒绝策略 租户内部 Pod 跨租户 Pod 基础设施服务 允许的流量路径

3.5 策略验证与常见故障

部署 NetworkPolicy 后,必须进行验证。以下表格列出了常见故障模式及其根因:

故障现象 可能原因 排查命令
Pod 无法访问同命名空间服务 默认拒绝策略已部署,但未添加内部允许规则 kubectl describe networkpolicy -n <ns>
Pod 无法解析 DNS 默认拒绝出站策略阻止了 UDP 53 流量 kubectl logs <pod> -n kube-system coredns
跨命名空间访问被拒绝 namespaceSelector 标签不匹配或缺失 kubectl get namespace --show-labels
策略未生效 CNI 插件不支持 NetworkPolicy(如 Flannel) kubectl get pods -n kube-system | grep calico
验证工具:使用 kubectl run tmp-shell --rm -it --image=nicolaka/netshoot -- /bin/bash 启动一个临时调试 Pod,然后使用 curl、nc、nslookup 等工具测试连通性。这是排查 NetworkPolicy 故障最直接的方法。

3.6 设计检查清单

在完成多租户网络拓扑设计后,请对照以下清单进行复核:

  1. 命名空间标签:每个租户命名空间是否已打上 tenant 和 environment 标签?
  2. 默认拒绝策略:每个租户命名空间是否已部署 default-deny-ingress?是否需要 default-deny-egress?
  3. DNS 规则:如果部署了出站拒绝策略,是否已添加允许访问 CoreDNS 的规则?
  4. 最小权限:每条允许规则是否同时指定了 namespaceSelector 和 podSelector?是否限制了端口?
  5. CNI 兼容性:集群使用的 CNI 插件(Calico、Cilium、Weave)是否支持 NetworkPolicy?
  6. 资源配额:是否为每个租户命名空间设置了 ResourceQuota 和 LimitRange?

遵循上述设计原则和检查清单,可以构建一个安全、可维护的多租户网络拓扑,大幅降低后续故障排查的复杂度。