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 根据 ingress 和 egress 字段自动推断——规则里只有 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 验证生效。