Skip to content

NetworkPolicy

Kubernetes 默认的网络模型是"Pod 之间全通"。在一个 namespace 里的任意 Pod 可以访问其他 namespace 的任意 Pod——不管业务上有没有这个需要。NetworkPolicy 的作用是把访问范围收窄:哪些 Pod 可以访问哪些 Pod 的哪些端口。

NetworkPolicy 是白名单模型——默认全通,创建策略后才开始拦截。这个方向很重要:没有策略时全通,有策略后只放行明确允许的流量,其余拒绝。

CNI 必须支持

NetworkPolicy 只是 K8s 资源对象。真正执行拦截的是 CNI 插件:

bash
kubectl -n kube-system get pods -o wide | grep -E 'calico|cilium|flannel'

Calico、Cilium 完全支持。Flannel 等简单 CNI 不支持或支持有限。CNI 不支持 NetworkPolicy 时,策略对象可以创建成功(API Server 不会拒绝),但流量不会被拦截——这种"策略已创建、实际没生效"很容易被误判。

验证 CNI 是否真的在执行策略:创建一个默认拒绝策略,然后从另一个 Pod 访问,看是否被拦截。

默认拒绝入站

不含任何 ingress allow 规则的 NetworkPolicy——效果是禁止所有入站流量:

yaml
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: default-deny-ingress
  namespace: demo
spec:
  podSelector: {}         # {} 表示当前 namespace 所有 Pod
  policyTypes:
    - Ingress

创建之后,demo namespace 里所有 Pod 的入站流量全部被拒。Pod 本身还能发起出站请求(出站没限制),但外部任何来源到这些 Pod 的流量都到不了。

podSelector: {} 选中 namespace 里全部 Pod。policyTypes 只写 Ingress 时出站不受影响。不写 policyTypes 时,K8s 根据 ingressegress 字段自动推断——规则里只有 ingress 时自动设 policyTypes: [Ingress]

白名单放行

允许带 role=frontend 的 Pod 访问 app=api 的 8080 端口:

yaml
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: allow-frontend-to-api
  namespace: demo
spec:
  podSelector:
    matchLabels:
      app: api            # 策略保护 app=api 的 Pod
  policyTypes:
    - Ingress
  ingress:
    - from:
        - podSelector:
            matchLabels:
              role: frontend   # 允许 role=frontend 的 Pod 入站
      ports:
        - protocol: TCP
          port: 8080           # 只允许 8080

from.podSelector 默认只匹配同 namespace 的 Pod。跨 namespace 访问要加 namespaceSelector

yaml
ingress:
  - from:
      - namespaceSelector:
          matchLabels:
            team: platform
    ports:
      - protocol: TCP
        port: 8080

给 namespace 打标签:

bash
kubectl label namespace platform team=platform

不写 ports 时放行所有端口。不写 from 时放行所有来源——效果上等于不限制,但显式声明后策略意图更清楚。

出站限制和 DNS

限制出站时第一个踩的坑是 DNS。Pod 访问 Service 的前提是能解析 DNS,DNS 又跑在 kube-system 的 CoreDNS Pod 里。出站策略没放行 DNS 时,域名解析失败,所有外部依赖看起来都断了——实际可能只是 DNS 被拦:

yaml
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: allow-dns-egress
  namespace: demo
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

DNS 的 UDP 53 是常规查询,TCP 53 用于大响应和区域传输。只放 UDP 53 在大部分场景够用,但某些长域名或 DNSSEC 场景需要 TCP 53。

排查

bash
kubectl -n demo get networkpolicy
kubectl -n demo describe networkpolicy allow-frontend-to-api

临时 Pod 内测试:

bash
kubectl -n demo run test-client --rm -it --image=curlimages/curl --restart=Never -- sh
curl -v http://api:8080/healthz
现象方向
策略已创建但流量没拦截CNI 不支持 NetworkPolicy
同 namespace 访问失败podSelector 标签、端口号、Pod labels
跨 namespace 访问失败namespace label、namespaceSelector
域名解析突然失败Egress 策略没放行 CoreDNS UDP 53
Service 访问超时策略拦截的是 Pod 流量——先直连 Pod IP 对比,Pod IP 通但 Service 不通是别的问题

逐步收紧

一次性全集群默认拒绝容易把监控采集、日志采集、CoreDNS 和基础设施一起拦掉。从非核心 namespace 开始,先默认拒绝入站、再加白名单、观察流量正常后再推进下一个 namespace——每步都先用 kubectl describe networkpolicy 确认策略已创建,再用临时 Pod 验证生效。