Skip to content

NetworkPolicy:声明访问意图到节点执行 ​

专题索引 / 上一篇:Service 控制面与节点代理

Service 回答“流量应去哪个后端”;NetworkPolicy 回答“这条流量是否允许发生”。两者都依赖 label,但不是同一套状态:Service 产生可选 endpoint,Policy 对选中的 Pod 声明入站和出站边界。

text
NetworkPolicy API 对象
  -> 选择一组 Pod 与方向(Ingress / Egress)
  -> 声明允许的对端、端口、协议
  -> 支持该能力的 CNI agent watch 变化
  -> 将规则编译为节点数据面状态
  -> 报文在 Pod 进出路径上被允许或拒绝

Kubernetes API 定义意图,但 Kubernetes 核心并不为每个 Node 内置一个通用 NetworkPolicy 执行器。集群必须安装支持 NetworkPolicy 的网络插件;iptables、nftables、eBPF 或其他内部机制是插件的实现选择。

1. Policy 先选择,再隔离,再允许 ​

podSelector 确定 Policy 作用于哪些 Pod,policyTypes 确定入站、出站还是两者。一个方向上,只要 Pod 被至少一个该方向的 Policy 选中,它就在这个方向上变为隔离状态;之后允许集合由所有适用 Policy 的规则并集决定。

text
没有选中 ingress Policy 的 Pod
  -> ingress 默认不因 NetworkPolicy 被限制

被 ingress Policy 选中的 Pod
  -> 只允许所有适用 ingress 规则合并后的流量

这意味着 NetworkPolicy 不是按文件顺序执行的防火墙规则,也没有“后写的 deny 覆盖前面的 allow”这类通用语义。标准 API 的关键是选中后白名单化,多个 Policy 的允许规则相加。

一个空的、选中目标 Pod 且包含某个方向的 Policy,常用于构造该方向的默认拒绝:它让 Pod 进入隔离状态,但没有提供任何允许规则。

2. 一条规则表达什么 ​

规则通常由三个部分组成:目标方向、对端身份、L4 端口与协议。

yaml
spec:
  podSelector:
    matchLabels:
      app: api
  policyTypes: [Ingress]
  ingress:
    - from:
        - namespaceSelector:
            matchLabels:
              team: web
          podSelector:
            matchLabels:
              app: frontend
      ports:
        - protocol: TCP
          port: 8080

上例表达的是:被 app=api 选中的 Pod,允许来自同一条 from 中同时匹配 namespace 与 Pod selector 的 TCP/8080 流量。它不表达 HTTP 路径、JWT 身份、域名或业务角色;标准 NetworkPolicy 的边界是 L3/L4 网络访问控制。

podSelector、namespaceSelector 和 ipBlock 的语义也不能混写:前两者依赖 Kubernetes 身份和标签;ipBlock 针对 CIDR。Pod 流量在数据面上何时被 NAT、是否与 ipBlock 交叉,取决于 CNI 与 Service 实现,应在目标集群验证。

3. 从控制面到数据面 ​

Policy 被写入 API Server 后,网络插件的每 Node agent 通常 watch Policy、Pod、Namespace 与 endpoint 变化,计算本机接口上的有效规则,再把结果物化为 dataplane 条目。

text
Policy / Pod label / Namespace label 改变
  -> CNI agent 更新本地身份与选择结果
  -> 编译 ingress / egress 规则
  -> iptables/nftables chain、eBPF map 或等价状态变更
  -> 新报文按当前规则判定

这与 kube-proxy 的结构相似:中心 API 传播声明,Node 保存快路径状态;但它们的输入和输出不同。kube-proxy 物化“Service 到 endpoint 的映射”,Policy agent 物化“某端点可与哪些对端通信的约束”。不要在 Service 故障时只看 kube-proxy,也不要在 Policy 故障时只看 EndpointSlice。

4. Service、DNS 与 Policy 的常见误区 ​

假设 frontend 调用 api:

text
frontend DNS 查询 api.default.svc
  -> DNS 请求本身必须允许 egress
frontend 连接 Service ClusterIP:8080
  -> Service 选出一个 api endpoint
  -> Policy 在实际报文路径上决定该流量是否可达

因此“只允许访问 api”并不自动允许 DNS。一个严格 egress Policy 经常先表现为域名解析失败,而不是业务连接被拒绝。另一方面,Service 已有 ready endpoint 也不表示请求能通过 Policy;后端 Pod 的 ingress 和客户端 Pod 的 egress 都可能独立拒绝它。

Policy 在 Service DNAT 前还是后、规则看到的是 ClusterIP 还是 Pod IP,并没有一套跨 CNI 的具体实现承诺。设计时以 Kubernetes Policy 的选择与方向语义表达意图;排障时用目标 CNI 的 flow log、drop event、BPF map 或防火墙规则确认实际执行点。

5. 按状态拆解一次“能解析但连不上” ​

节点要验证的事实常见错误
选择范围Policy 是否真的选中客户端或服务端 Podlabel/namespace 不匹配,误以为 Policy 已生效
隔离方向ingress、egress 是否分别进入隔离只检查服务端 ingress,漏掉客户端 egress
允许并集所有适用 Policy 合起来是否允许该对端与端口期待一条 Policy 覆盖或否定另一条
Service 后端EndpointSlice 是否有 ready endpoint将无后端误判为 Policy 拦截
节点执行CNI agent 与本地数据面是否已同步YAML 已创建就假定报文规则已安装
实际流量源、目的、端口、协议和丢包点只看 kubectl describe,没有 flow 证据

6. 这一设计能迁移到哪里 ​

NetworkPolicy 展示了一个成熟系统如何把稳定的用户语义和可替换的底层实现分开:

  1. API 只定义跨实现必须一致的访问意图。
  2. 插件负责把意图编译到最接近报文的执行位置。
  3. 控制面状态与数据面状态分别可观察、分别会失败。
  4. 标准边界外的需求,例如 HTTP 授权、FQDN 策略、全局 deny 优先级,应明确交给更高层网关、服务网格或厂商扩展,而不是误认为基础 API 已承诺。

资料与边界 ​

本文只描述 Kubernetes 标准 NetworkPolicy 的意图模型。是否支持、能否跨节点执行、规则命中位置、可观测性和扩展能力,都依赖所选网络插件与节点配置。

学习规划与研究资料库