Appearance
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 # 只允许 8080from.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: 53DNS 的 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 验证生效。